FP_growth算法

本文介绍了FP_growth算法,对比了它与Apriori算法的区别,阐述了FP_growth的优势和构建FP树的过程。详细讲解了如何从FP树中挖掘频繁项集的步骤,并给出了条件模式基和创建条件FP树的概念。同时,文中还提供了Python代码示例以及使用kosarak.dat数据集进行实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

FP_growth算法

本文基于机器学习实战这本书记录一下学习路程,就当是笔记了,我是机器实战的搬运工,各位大爷不喜勿喷~~~

1、Apriori与FP_growth的区别

Aprori算法利用频繁集的两个特性,过滤了很多无关的集合,效率提高不少,但是我们发现Apriori算法是一个候选消除算法,每一次消除都需要扫描一次所有数据记录,造成整个算法在面临大数据集时显得无能为力。今天我们介绍一个新的算法挖掘频繁项集,效率比Aprori算法高很多。

FpGrowth算法通过构造一个树结构来压缩数据记录,使得挖掘频繁项集只需要扫描两次数据记录,而且该算法不需要生成候选集合,所以效率会比较高

2、FP_growth优缺点

优点:一般快于Apriori算法 
缺点:实现比较困难,在某些数据集上性能会下降 
适用数据类型:标称型数据

3、构建FP树

3.1构建过程1
第一遍对所有元素项的出现次数进行计数,去掉不满足最小支持度的元素项
第二遍扫描中只需要考虑那些频繁元素,读入每个项集将其添加到一条已经存在的路径中,若该路径不存在则创建一条新路径。
事务ID事务中的元素
001r,z,h,j,p
002z,y,x,w,v,u,t,s
003z
004r,x,n,o,s
005y,r,x,z,q,t,p
006y,z,x,e,q,s,t,m

根据上面的实例构建fp树
这里写图片描述
这里没有出现p,q,的原因是因为我们实战的有最小支持度的阀值,当小于这个阀值的时候我们认为其是不频繁的。

3.2构建过程2

除了给出FP树之外还需要一个头指针表来指向给定的类型的第一个实例。利用头指针表,快速访问FP中一个给定类型的所有元素。
这里写图片描述
这里给出前两个事务的构建过程
这里写图片描述

fp树伪代码

传入:数据集dataSet, 最小尺度minSup
第一次遍历数据集统计各元素出现的次数,在这同时创建头指针表
过滤掉不满足的元素
创建根节点
第二次遍历数据集
   根据全局频率进行排序
   更新树节点(在这写一个updateTree函数)
返回  创建的树,头指针表 

4、从一颗FP树中挖掘频繁项集

4.1从FP树中抽取频繁项集分为三个步骤
从FP树中获得条件模式基
利用条件模式基,构建一颗条件FP树
迭代重复步骤(1),(2),直到树包含一个元素项为止
4.2 条件模式基

1、条件模式基:条件模式基是所查找元素项为结尾的路径集合。
2、前缀路径:每一条到所查找元素的路径,也就是条件模式基里面的一条路径,一条前缀路径是介于所查找元素项与树根节点的内容(利用头指针表可以求前缀路径,前缀路径将被用来构建条件FP树)

4.3创建条件FP树

对于每一个频繁项,都要创建一个条件FP树。
递归查找频繁项集伪代码

传入:头指针表(headtable),最小阀值(minSup),用来存储前缀路径的集合(preFix),存储频繁集的列表(freItemList)

1.将头指针表中的元素按照从小到大的顺序排列好存储到bigL列表里面
2.遍历bigL每个元素basePat:
    复制前缀路径到新的频繁集newFreSet里面
    将basePat添加到newFreSet里面
    并将newFreSet添加到freItemList列表里面
    寻找到basePat的条件基
    利用basePat条件基构建basePat的条件树
    如果构建basePat的条件树有头指针表,那么就继续递归调用本身的发掘频繁集函数

给一波代码先瞅瞅……

#inTree为生成的Fp树,头指针表headerTable, preFix空集合Set()
#freItemList保存生成的频繁集
def mineTree(inTree, headerTable, minSup, preFix, freItemList):
# 对头指针出现的元素按照出现的频率从小到大进行排序
#遍历头指针表,挖掘频繁集
    bigL = [v[0] for v in sorted(headerTable.items(), key = lambda p:p[1])]  
    for basePat in bigL:
        #保存当前前缀路径basePat
        newSet = preFix.copy()
        newSet.add(basePat)  
         #将每个频繁项添加到频繁项集列表freqItemList
        freItemList.append(newSet)
         #递归调用findPrefixPath函数找到到元素项basePat的前缀路径
        condPattBases= findPrefixPath(basePat, headerTable[basePat][1]) 
         #根据当前元素项生成的前缀路径和最小支持度生成条件树
        myCondTree, myHead = createTree(condPattBases, 3)
        #若条件fp树有元素项,可以再次递归生成条件树
        if myHead != None:
#             print newSet
          #递归挖掘该条件树 
            mineTree(myCondTree, myHead, 2, newSet, freItemList)

例如我们为t创建一个条件FP树
这里写图片描述
我们已经挖掘到了为t创建了条件树,那么接下来就开始挖为{t,z},{t,x},{t,y}在创建……重复这个过程
直到条件树没有元素可以挖掘,如果看不懂那么就看下面这张图,这张图很好诠释发掘t所有频繁集的过程。
这里写图片描述

5、python代码



#encoding:utf-8
import numpy as py
from numpy import *
from audioop import reverse
#fp树的定义
class treeNode:
    def __init__(self, nameValue, numOccur, parentNode):
        self.name = nameValue#树节点的名字
        self.count = numOccur#树节点的计数
        self.nodeLink = None#链接下一棵树的线索,好像指针
        self.parent = parentNode#该树节点的的父亲
        self.children={}#树节点的孩子
    def inc(self, numOccur):#计算该树节点出现的支持度数,就是出现了几次
        self.count += numOccur
    def disp(self, ind = 1):#遍历这颗树
        print " "*ind,self.name," ", self.count 
        for child in self.children.values():
            child.disp(ind+1)
#构建fp树
def createTree(dataSet, minSup = 1):
    headerTable = {}#头结点的字典
    #1.统计元素出现的次数
    for trans in dataSet:#字典中的每条记录    网上俗称的事务
        for item in trans:#遍历的是事务中的每个元素
            #dict.get(key, default = None ) 有过有该键值就返回对应的价值,如果不存在键值就返回用户指定的值
            headerTable[item] = headerTable.get(item, 0) + dataSet[trans]
    #2 删除小于该支持度的元素项
    for k in headerTable.keys():
        if headerTable[k] < minSup:
            del(headerTable[k])
    #3 对元素项去重得到频繁集
    freqItemSet =set(headerTable.keys())
    #如果为空 则返回无需进行下一项
    if len(freqItemSet) == 0:
        return None, None
    #在头指针中  保留计数的数值以及指向每种类型第一个指针
    for k in headerTable:
        headerTable[k]=[headerTable[k], None]
    #根节点为空  并且出现的次数为一
    reTree = treeNode('NUll Set', 1, None)
    '''第二次遍历数据集 建立fp树'''
    for transet, count in dataSet.items():#transSet代表事务(一条物品的组合)  也就是一个集合啦 dataSet的键值,count代表出现的次数
        localID={}#key每项物品,count 商品出现的次数  
        for trans in transet:
            if trans in freqItemSet:
                localID[trans] = headerTable[trans][0]#记录商品出现的次数
#             print "loacal",localID
        if len(localID) > 0:
            #对这个事务(一条物品的组合)按照出现的度数  从大到小进行排序  为插入进行准备
            orderedItems = [v[0] for v in sorted(localID.items(), key=lambda p:p[1], reverse = True)]
            #构建树
            updateTree(orderedItems, reTree,headerTable, count)
    return reTree, headerTable#返回FP树结构,头指针
'''已经排好序的物品items,构建fp树'''
def updateTree(items, inTree,headerTable, count):
    if items[0] in inTree.children:#若该项已经在树中出现则计数加一,  好像字典树有木有………………
        inTree.children[items[0]].inc(count)
    else:#如果没有这个元素项,那么创建一个子节点
        inTree.children[items[0]] = treeNode(items[0], count, inTree)
        if headerTable[items[0]][1] == None:#如果头指针没有指向任何元素,那么指向该节点
            headerTable[items[0]][1] = inTree.children[items[0]]
        else:#如果已经指向,那么就继续加入这个链表  updateHeader这个函数的作用就是让已经加入的该链表的最后一项,指向这个新的节点 
            updateHeader(headerTable[items[0]][1], inTree.children[items[0]])
    #对剩下的元素项迭代调用,updateTree
    #不断调用自身,每次调用就会去掉列表的第一个元素
    #通过items[1::]实现     
    if len(items) > 1:
        updateTree(items[1::], inTree.children[items[0]], headerTable, count) 
'''更新相似元素的链表,相当于延长这个链表啦'''     
def updateHeader(nodeToTest, targetNode):
    while(nodeToTest.nodeLink != None):
        nodeToTest = nodeToTest.nodeLink
    nodeToTest.nodeLink = targetNode

def loadSimpDat():
    simpDat = [['r', 'z', 'h', 'j', 'p'],
               ['z', 'y', 'x', 'w', 'v', 'u', 't', 's'],
               ['z'],
               ['r', 'x', 'n', 'o', 's'],
               ['y', 'r', 'x', 'z', 'q', 't', 'p'],
               ['y', 'z', 'x', 'e', 'q', 's', 't', 'm']]
    return simpDat
#对数据进行格式化处理转化成字典类型,<交易记录,count = 1>
def createInitSet(dataSet):
    retDict={}
    for transSet in dataSet:
        retDict[frozenset(transSet)] = 1
    return retDict
'''挖掘频繁集'''
#向上搜索,寻找到leafNode当前节点的路径
def ascentTree(leafNode, prefixPath):
    if leafNode.parent != None:#如果父节点不为空就继续向上寻找并记录
        prefixPath.append(leafNode.name)
        ascentTree(leafNode.parent, prefixPath)#递归向上查找
    return 
'''为给定的元素项找到前缀路径(条件模式基)'''
def findPrefixPath(basePat, treeNode):#basePat 要发掘的元素  treeNode发掘的节点
    condPats = {}#存放条件模式基,即含元素项basePat的前缀路径以及计数
    #<key,value>  key:前缀路径 value:路径计数
    while treeNode != None:
        prefixPath = []#存放不同路线的  前缀路径    包含basePat自身   在下面会去掉自身
        ascentTree(treeNode, prefixPath)
        if len(prefixPath) > 1:
            condPats[frozenset(prefixPath[1:])] = treeNode.count
        treeNode = treeNode.nodeLink
    return condPats
#inTree为生成的Fp树,头指针表headerTable, preFix空集合Set()
#freItemList保存生成的频繁集
def mineTree(inTree, headerTable, minSup, preFix, freItemList):
# 对头指针出现的元素按照出现的频率从小到大进行排序
#遍历头指针表,挖掘频繁集
    bigL = [v[0] for v in sorted(headerTable.items(), key = lambda p:p[1])]  
    for basePat in bigL:
        #保存当前前缀路径basePat
        newSet = preFix.copy()
        newSet.add(basePat)  
         #将每个频繁项添加到频繁项集列表freqItemList
        freItemList.append(newSet)
         #递归调用findPrefixPath函数找到到元素项basePat的前缀路径
        condPattBases= findPrefixPath(basePat, headerTable[basePat][1]) 
         #根据当前元素项生成的前缀路径和最小支持度生成条件树
        myCondTree, myHead = createTree(condPattBases, 3)
        #若条件fp树有元素项,可以再次递归生成条件树
        if myHead != None:
#             print newSet
          #递归挖掘该条件树 
            mineTree(myCondTree, myHead, 2, newSet, freItemList)
if __name__ == '__main__':

#     dataSet = loadSimpDat()
#     iniSet = createInitSet(dataSet)
#     myFpTree,myHeaderTab = createTree(iniSet, 3)
#     freItemList=[]
#     mineTree(myFpTree, myHeaderTab, 3, set([]), freItemList)
#     print mat(freItemList)
#------------从新闻网站站点点击流中挖掘---------------
    paresedDat = [line.split() for line in open('kosarak.dat').readlines()]  
    initSet = createInitSet(paresedDat)
    myFpTree, myHeaderTab = createTree(initSet, 100000)

    freItemList=[]
    mineTree(myFpTree, myHeaderTab, 100000      , set([]), freItemList)
    print mat(freItemList)


6、kosarak.dat数据集

上传在了我的资源里面

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值