fp_growth(Frequent Pattern tree)關聯算法

注意:此方法筆者已經試過,計算關聯詞,在跑大批量數據模型的時候,很消耗內存。打個比方,如果你的數據集足夠大,內存全部能吃完。具體一點,每個子列表有10個詞,共500個子列表,16個G不夠用的,可以想象上萬條以上是什麼情況。
改進的方法:
1、upgraded FP-growsth, UFP 算法
http://www.bjutxuebao.com/bjgydx/article/2016/0254-0037-42-5-697.html#outline_anchor_19
2、頻繁模式挖掘中Apriori、FP-Growth和Eclat算法的實現和對比(Python實現)
https://www.cnblogs.com/infaraway/p/6774521.html

FP-growth算法理解

FP-growth(Frequent Pattern Tree, 頻繁模式樹),是韓家煒老師提出的挖掘頻繁項集的方法,是將數據集存儲在一個特定的稱作FP樹的結構之後發現頻繁項集或頻繁項對,即常在一塊出現的元素項的集合FP樹。
FP-growth算法比Apriori算法效率更高,在整個算法執行過程中,只需遍歷數據集2次,就能夠完成頻繁模式發現,其發現頻繁項集的基本過程如下:
(1)構建FP樹
(2)從FP樹中挖掘頻繁項集

FP-growth的一般流程如下:

1:先掃描一遍數據集,得到頻繁項爲1的項目集,定義最小支持度(項目出現最少次數),刪除那些小於最小支持度的項目,然後將原始數據集中的條目按項目集中降序進行排列。
2:第二次掃描,創建項頭表(從上往下降序),以及FP樹。
3:對於每個項目(可以按照從下往上的順序)找到其條件模式基(CPB,conditional patten base),遞歸調用樹結構,刪除小於最小支持度的項。如果最終呈現單一路徑的樹結構,則直接列舉所有組合;非單一路徑的則繼續調用樹結構,直到形成單一路徑即可。
示例說明
如下表所示數據清單(第一列爲購買id,第二列爲物品項目):

Tid Items
1 1, I2, I5
2 I2, I4
3 I2, I3
4 I1, I2, I4
5 I1, I3
6 I2, I3
7 I1, I3
8 I1, I2, I3, I5
9 I1, I2, I3

第一步:構建FP樹

1、 掃描數據集,對每個物品進行計數:

1 2 3 4 5
6 7 6 2 2

2、 設定最小支持度(即物品最少出現的次數)爲2
3、按降序重新排列物品集(如果出現計數小於2的物品則需刪除)

2 1 3 4 5
7 6 6 2 2

4、 根據項目(物品)出現的次數重新調整物品清單

Tid Items
1 I2, I1, I5
2 I2, I4
3 I2, I3
4 I2, I1, I4
5 I1, I3
6 I2, I3
7 I1, I3
8 I2, I1, I3, I5
9 I2, I1, I3

5、構建FP樹
加入第一條清單(I2, I1, I5):
在這裏插入圖片描述
加入第二條清單(I2, I4):
出現相同的節點進行累加(I2)
在這裏插入圖片描述
下面依次加入第3-9條清單,得到FP樹:
在這裏插入圖片描述
第二步:挖掘頻繁項集
對於每一個元素項,獲取其對應的條件模式基(conditional pattern base)。條件模式基是以所查找元素項爲結尾的路徑集合。每一條路徑其實都是一條前綴路徑。
按照從下往上的順序,考慮兩個例子。
(1)考慮I5,得到條件模式基{(I2 I1:1), (I2 I1 I3)}, 構造條件FP樹如下,然後遞歸調用FP-growth,模式後綴爲I5。這個條件FP樹是單路徑的,在FP-growth中直接列舉{I2:2,I1:2,I3:1}的所有組合,之後和模式後綴I5取並集得到支持度大於2的所有模式:{ I2 I5:2, I1 I5:2, I2 I1 I5:2}。
在這裏插入圖片描述
(2)I5的情況是比較簡單的,因爲I5對應的條件FP-樹是單路徑的。下面考慮I3,I3的條件模式基是{(I2 I1:2), (I2:2), (I1:2)},生成的條件FP-樹如左下圖,然後遞歸調用FP-growth,模式前綴爲I3。I3的條件FP-樹仍然是一個多路徑樹,首先把模式後綴I3和條件FP樹中的項頭表中的每一項取並集,得到一組模式{I2 I3:4, I1 I3:4},但是這一組模式不是後綴爲I3的所有模式。還需要遞歸調用FP-growth,模式後綴爲{I1,I3},{I1,I3}的條件模式基爲{I2:2},其生成的條件FP-樹如右下圖所示。這是一個單路徑的條件FP-樹,在FP-growth中把I2和模式後綴{I1,I3}取並得到模式{I1 I2 I3:2}。理論上還應該計算一下模式後綴爲{I2,I3}的模式集,但是{I2,I3}的條件模式基爲空,遞歸調用結束。最終模式後綴I3的支持度大於2的所有模式爲:{ I2 I3:4, I1 I3:4, I1 I2 I3:2}
在這裏插入圖片描述
在這裏插入圖片描述
根據FP-growth算法,最終得到的支持度大於2頻繁模式如下:

item 條件模式基 條件FP樹 產生的頻繁模式
I5 {(I2 I1:1),(I2 I1 I3:1)} (I2:2, I1:2) I2 I5:2, I1 I5:2, I2 I1 I5:2
I4 {(I2 I1:1), (I2:1)} (I2:2) I2 I4:2
I3 {(I2 I1:2), (I2:2), (I1:2)} (I2:4, I1:2), (I1:2) I2 I3:4, I1 I3:4, I2 I1 I3:2
I1 {(I2:4)} (I2:4) I2 I1:4

FP-growth算法實現:

1. FP樹的類定義

class treeNode:
    '''
    FP樹的類定義
    '''
    def __init__(self, nameValue, numOccur, parentNode):
        self.name = nameValue #節點名字
        self.count = numOccur #節點計數值
        self.nodeLink = None #用於鏈接相似的元素項
        self.parent = parentNode      #needs to be updated
        self.children = {} #子節點

    def inc(self, numOccur):
        '''
        對count變量增加給定值
        '''
        self.count += numOccur

    def disp(self, ind=1):
        '''
        將樹以文本形式展示
        '''
#         print ('  '*ind, self.name, ' ', self.count)
        for child in self.children.values():
            child.disp(ind+1)

2.FP樹構建函數


def createTree(dataSet, minSup=1):
    '''
    創建FP樹
    '''
    headerTable = {}
    #第一次掃描數據集
    for trans in dataSet:#計算item出現頻數
        for item in trans:
            headerTable[item] = headerTable.get(item, 0) + dataSet[trans]
    headerTable = {k:v for k,v in headerTable.items() if v >= minSup}
    freqItemSet = set(headerTable.keys())
    #print ('freqItemSet: ',freqItemSet)
    if len(freqItemSet) == 0: return None, None  #如果沒有元素項滿足要求,則退出
    for k in headerTable:
        headerTable[k] = [headerTable[k], None] #初始化headerTable
    #print ('headerTable: ',headerTable)
    #第二次掃描數據集
    retTree = treeNode('Null Set', 1, None) #創建樹
    for tranSet, count in dataSet.items():  
        localD = {}
        for item in tranSet:  #put transaction items in order
            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)#將排序後的item集合填充的樹中
    return retTree, headerTable #返回樹型結構和頭指針表

def updateTree(items, inTree, headerTable, count):
    if items[0] in inTree.children:#檢查第一個元素項是否作爲子節點存在
        inTree.children[items[0]].inc(count) #存在,更新計數
    else:   #不存在,創建一個新的treeNode,將其作爲一個新的子節點加入其中
        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(headerTable[items[0]][1], inTree.children[items[0]])
    if len(items) > 1:#不斷迭代調用自身,每次調用都會刪掉列表中的第一個元素
        updateTree(items[1::], inTree.children[items[0]], headerTable, count)

def updateHeader(nodeToTest, targetNode):
    '''
    this version does not use recursion
    Do not use recursion to traverse a linked list!
    更新頭指針表,確保節點鏈接指向樹中該元素項的每一個實例
    '''
    while (nodeToTest.nodeLink != None):    
        nodeToTest = nodeToTest.nodeLink
    nodeToTest.nodeLink = targetNode

3. 抽取條件模式基

def ascendTree(leafNode, prefixPath): #迭代上溯整棵樹
    if leafNode.parent != None:
        prefixPath.append(leafNode.name)
        ascendTree(leafNode.parent, prefixPath)

def findPrefixPath(basePat, treeNode): #treeNode comes from header table
    condPats = {}
    while treeNode != None:
        prefixPath = []
        ascendTree(treeNode, prefixPath)
        if len(prefixPath) > 1: 
            condPats[frozenset(prefixPath[1:])] = treeNode.count
        treeNode = treeNode.nodeLink
    return condPats

4. 遞歸查找頻繁項集

def mineTree(inTree, headerTable, minSup, preFix, freqItemList):
    bigL = [v[0] for v in sorted(headerTable.items(), key=lambda p: p[1][0])]# 1.排序頭指針表
    for basePat in bigL:  #從頭指針表的底端開始
        newFreqSet = preFix.copy()
        newFreqSet.add(basePat)
#         print ('finalFrequent Item: ',newFreqSet)    #添加的頻繁項列表
        freqItemList.append(newFreqSet)
        condPattBases = findPrefixPath(basePat, headerTable[basePat][1])
#         print ('condPattBases :',basePat, condPattBases)
        # 2.從條件模式基創建條件FP樹
        myCondTree, myHead = createTree(condPattBases, minSup)
#         print ('head from conditional tree: ', myHead)
        if myHead != None: # 3.挖掘條件FP樹
#             print ('conditional tree for: ',newFreqSet)
            myCondTree.disp(1)            
            mineTree(myCondTree, myHead, minSup, newFreqSet, freqItemList)

5. 測試結果

def loadSimpDat():
    simpDat = [
                ['中國','朝鮮','巴基斯坦'],
                ['朝鮮','韓國'],
                ['中國','日本'],
                ['美國','日本','韓國'],
                ['中國','巴基斯坦'],
                ['日本','美國'],
                ['中國','日本','韓國']
              ]
    return simpDat

def createInitSet(dataSet):  
    retDict = {}  
    for trans in dataSet:  
        retDict[frozenset(trans)] = retDict.get(frozenset(trans), 0) + 1 #若沒有相同事項,則爲1;若有相同事項,則加1  
    return retDict
minSup = 2
simpDat = loadSimpDat()
initSet = createInitSet(simpDat)
myFPtree, myHeaderTab = createTree(initSet, minSup)
myFPtree.disp()
myFreqList = []
mineTree(myFPtree, myHeaderTab, minSup, set([]), myFreqList)
print(myFreqList)

打印結果如下
[[{‘美國’}, 2], [{‘日本’, ‘美國’}, 2], [{‘朝鮮’}, 2], [{‘巴基斯坦’}, 2], [{‘中國’, ‘巴基斯坦’}, 2], [{‘韓國’}, 3], [{‘韓國’, ‘日本’}, 2], [{‘日本’}, 4], [{‘中國’, ‘日本’}, 2], [{‘中國’}, 4]]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章