机器学习实战(Machine Learning in Action)学习笔记————08.使用FPgrowth算法来高效发现频繁项集

机器学习实战(Machine Learning in Action)学习笔记————08.使用FPgrowth算法来高效发现频繁项集

关键字:FPgrowth、频繁项集、条件FP树、非监督学习
作者:米仓山下
时间:2018-11-3
机器学习实战(Machine Learning in Action,@author: Peter Harrington)
源码下载地址:https://www.manning.com/books/machine-learning-in-action
git@github.com:pbharrin/machinelearninginaction.git

*************************************************************
一、使用FPgrowth算法来高效发现频繁项集

FPgrowth算法原理:
基于Apriori构建,但在完成相同任务时,采用了一些不同的的技术。这里的任务是将数据集存储在一个特定的称为FP树的结构之后发现频繁项集或则频繁项对,即在一块出现的的元素项的集合FP树。这种做法的执行速度要快于Apriori,通常性能要好两个数量级以上。
FP——Frequent pattern(频繁模式)

*************************************************************
二、FPgrowth算法——构建FP树

FP树构建函数
----------------------------------------------------------------------------
输入:dataSet——待挖掘数据集;minSup——最小支持度,默认为1
输出:retTree——构建的FP树; headerTable——头指针表

def createTree(dataSet, minSup=1): #create FP-tree from dataset but don't mine
    headerTable = {}
    
    #扫描两次数据集dataSet
    for trans in dataSet:#第一次扫描,统计所有元素出现的频次
        for item in trans:
            headerTable[item] = headerTable.get(item, 0) + dataSet[trans]
    for k in headerTable.keys():  #移除不符合minSup的items
        if headerTable[k] < minSup:
            del(headerTable[k])
    freqItemSet = set(headerTable.keys())
    #print 'freqItemSet: ',freqItemSet
    if len(freqItemSet) == 0: return None, None  #没有items符合minSup,返回None退出
    for k in headerTable:
        headerTable[k] = [headerTable[k], None]  #结构化headerTable
    #print 'headerTable: ',headerTable
    retTree = treeNode('Null Set', 1, None)      #创建FP树根节点
    for tranSet, count in dataSet.items():       #第二次扫描,构建FP树retTree
        localD = {}
        for item in tranSet:                     #获取条数据中每个元素的全局频次,以便排序
            if item in freqItemSet:
                localD[item] = headerTable[item][0]
        if len(localD) > 0:
            orderedItems = [v[0] for v in sorted(localD.items(), key=lambda p: p[1], reverse=True)]      #排序
            updateTree(orderedItems, retTree, headerTable, count)  #更新FP树retTree
    return retTree, headerTable                  #返回FP树retTree,头指针表headerTable

注:createTree足够灵活,下面构建条件FP树时还要用到

#更新FP树retTree
def updateTree(items, inTree, headerTable, count):
    if items[0] in inTree.children:          #如果第一个元素orderedItems[0]在子节点中
        inTree.children[items[0]].inc(count) #增加计数
    else:                                    #不存在,增加子节点
        inTree.children[items[0]] = treeNode(items[0], count, inTree)
        if headerTable[items[0]][1] == None: #头指针表中items没有指向节点
            headerTable[items[0]][1] = inTree.children[items[0]]
        else:           #头指针表中items以指向某个相似节点,追加到后面
            updateHeader(headerTable[items[0]][1], inTree.children[items[0]])
    if len(items) > 1:  #items不止一个元素,去掉第一个元素,递归调用updateTree构建树
        updateTree(items[1::], inTree.children[items[0]], headerTable, count)

----------------------------------------------------------------------------
测试:

>>> import fpGrowth
>>> simpdata=fpGrowth.loadSimpDat()
>>> initset=fpGrowth.createInitSet(simpdata)
>>> simpdata
[['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']]
>>> initset
{frozenset(['e', 'm', 'q', 's', 't', 'y', 'x', 'z']): 1, frozenset(['x', 's', 'r', 'o', 'n']): 1, frozenset(['s', 'u', 't', 'w', 'v', 'y', 'x', 'z']): 1, frozenset(['q', 'p', 'r', 't', 'y', 'x', 'z']): 1, frozenset(['h', 'r', 'z', 'p', 'j']): 1, frozenset(['z']): 1}
>>> minSup = 3
>>> myFPtree, myHeaderTab = fpGrowth.createTree(initset, minSup)
>>> myFPtree.disp()
   Null Set   1
     x   1
       s   1
         r   1
     z   5
       x   3
         y   3
           s   2
             t   2
           r   1
             t   1
       r   1
>>> myHeaderTab
{'s': [3, <fpGrowth.treeNode instance at 0x00000000039FE608>], 'r': [3, <fpGrowth.treeNode instance at 0x00000000039FE788>], 't': [3, <fpGrowth.treeNode instance at 0x00000000039FE688>], 'y': [3, <fpGrowth.treeNode instance at 0x00000000039FE5C8>], 'x': [4, <fpGrowth.treeNode instance at 0x00000000039FE588>], 'z': [5, <fpGrowth.treeNode instance at 0x00000000039FE548>]}
>>>

*************************************************************
三、从一棵FP树种挖掘频繁项集

#递归查找频繁项:mineTree函数
----------------------------------------------------------------------------

#输入:inTree——输入FP树,递归调用时为此时的元素preFix条件FP树;headerTable——头指针表;minSup——最小支持数;preFix——初始化为set([]),递归调用时为条件FP树inTree对应的元素;freqItemList——初始化为[],用来存储频繁项集。
#输出:freqItemList——用来存储频繁项集
def mineTree(inTree, headerTable, minSup, preFix, freqItemList):
    bigL = [v[0] for v in sorted(headerTable.items(), key=lambda p: p[1])]  #头指针表排序
    for basePat in bigL:                #从头指针表bigL(headerTable)底端开始遍历(从小到大)
        newFreqSet = preFix.copy()
        newFreqSet.add(basePat)         #递归前,newFreqSet为单元素频繁项;递归时preFix不为空,开始组合
        freqItemList.append(newFreqSet) #将每个频繁项加入到列表freqItemList中
        condPattBases = findPrefixPath(basePat,\        #抽取条件模式基condPattBases,去掉了元素本身
                        headerTable[basePat][1])
        myCondTree, myHead = createTree(condPattBases,\ #根据条件模式基condPattBases构建条件频繁树myCondTree
                             minSup)
        if myHead != None:                              #挖掘FP条件树       
            #print 'conditional tree for: ',newFreqSet
            #myCondTree.disp(1)
            mineTree(myCondTree, myHead, minSup, \      #newFreqSet不为空set([]),递归调用mineTree函数
                              newFreqSet, freqItemList)

----------------------------------------------------------------------------
将源码中下面两行取消注释:
#print 'conditional tree for: ',newFreqSet
#myCondTree.disp(1)      #打印条件树

测试:

>>> myFreqList = []
>>> reload(fpGrowth)
<module 'fpGrowth' from 'fpGrowth.py'>

#遍历头指针表myHeaderTab,将其单元素频繁项加入myFreqList后,再找出每个元素的条件FP树。递归调用组合频繁项
>>> fpGrowth.mineTree(myFPtree, myHeaderTab, minSup, set([]), myFreqList)
conditional tree for:  set(['y'])
   Null Set   1
     x   3
       z   3
conditional tree for:  set(['y', 'z'])
   Null Set   1
     x   3
conditional tree for:  set(['s'])
   Null Set   1
     x   3
conditional tree for:  set(['t'])
   Null Set   1
     y   3
       x   3
         z   3
conditional tree for:  set(['x', 't'])
   Null Set   1
     y   3
conditional tree for:  set(['z', 't'])
   Null Set   1
     y   3
       x   3
conditional tree for:  set(['x', 'z', 't'])
   Null Set   1
     y   3
conditional tree for:  set(['x'])
   Null Set   1
     z   3
>>> myFreqList
[set(['y']), set(['y', 'z']), set(['y', 'x', 'z']), set(['y', 'x']), set(['s']), set(['x', 's']), set(['t']), set(['z', 't']), set(['x', 'z', 't']), set(['y', 'x', 'z', 't']), set(['y', 'z', 't']), set(['x', 't']), set(['y', 'x', 't']), set(['y', 't']), set(['r']), set(['x']), set(['x', 'z']), set(['z'])]
>>> len(myFreqList)
18
>>>


*************************************************************
四、示例:从新闻网站点击流中挖掘
kosarak.dat中有将近100万条记录,每一行包含了某个用户浏览过得新闻报道。有些用户只看过一篇,有的用户看过2498篇报道。用户和报道编码成整数,利用FPgrowth算法

#读取数据,数据集格式化

>>> parsedDat=[line.split() for line in open('kosarak.dat').readlines()]
>>> len(parsedDat)
990002
>>> initset=fpGrowth.createInitSet(parsedDat)

#构建FP树,寻找阅读量10+的新闻报道
>>> myFPtree, myHeaderTab = fpGrowth.createTree(initset, 100000)

#创建条件FP树
>>> myFreqList = []
>>> fpGrowth.mineTree(myFPtree, myHeaderTab, 100000, set([]), myFreqList)
>>> len(myFreqList)
9
>>> myFreqList
[set(['1']), set(['1', '6']), set(['3']), set(['11', '3']), set(['11', '3', '6']), set(['3', '6']), set(['11']), set(['11', '6']), set(['6'])]
>>>

----------------------------------------------------------------------------
总结:

优点:FPgrowth算法相比Apriori只需要对数据库进行两次扫描,能够显著加快频繁项集发现速度
缺点:该算法能够更高效的发现频繁项集,但不能用于发现关联规则
应用:搜索引擎推荐词(经常在一块出现的词对)等

posted on 2018-11-03 15:13  米仓山下  阅读(633)  评论(0编辑  收藏  举报

导航