機(jī)器學(xué)習(xí)實(shí)戰(zhàn)-使用FP-growth算法來高效發(fā)現(xiàn)頻繁項(xiàng)集

上一章介紹了發(fā)現(xiàn)頻繁項(xiàng)集與關(guān)鍵規(guī)則的算法绢记,本章將繼續(xù)關(guān)注發(fā)現(xiàn)頻繁項(xiàng)集這一任務(wù)柳洋。我們會深入探索該任務(wù)的解決方法叔汁,并應(yīng)用FP-growth算法進(jìn)行處理傻挂。這種算法雖然能更為高效地發(fā)現(xiàn)頻繁項(xiàng)集乘碑,但不能用于發(fā)現(xiàn)關(guān)聯(lián)規(guī)則。

FP-growth算法
優(yōu)點(diǎn):一般要快于Apriori
缺點(diǎn):實(shí)現(xiàn)比較困難金拒,在某些數(shù)據(jù)集上性能會下降
適用數(shù)據(jù)類型:標(biāo)稱型數(shù)據(jù)

FP-growth算法講數(shù)據(jù)存儲在一種稱為FP樹的緊湊數(shù)據(jù)結(jié)構(gòu)中兽肤。FP代表頻繁模式,F(xiàn)requent pattern绪抛。同搜索樹不同的是资铡,一個(gè)元素項(xiàng)可以在一棵FP樹中出現(xiàn)多次。FP樹會存儲項(xiàng)集的出現(xiàn)頻率幢码,而每個(gè)項(xiàng)集會以路徑的方式存儲在樹中笤休。
相似點(diǎn)之間的鏈接即節(jié)點(diǎn)鏈接(node link),用于快速發(fā)現(xiàn)相似的位置症副。
本章的FP樹比書中的其他樹更加復(fù)雜店雅,因此要?jiǎng)?chuàng)建一個(gè)類來保存樹的每一個(gè)節(jié)點(diǎn):

#FP樹
class treeNode:
    def __init__(self, nameValue, numOccur, parentNode):
        self.name = nameValue
        self.count = numOccur#計(jì)數(shù)器
        self.nodeLink = None#用于鏈接相似的元素項(xiàng)
        self.parent = parentNode#指向父節(jié)點(diǎn),需要被更新
        self.children = {} #存放子節(jié)點(diǎn)
    
    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)

先創(chuàng)建樹中的一個(gè)單節(jié)點(diǎn):

In [45]: import fpGrowth
    ...: rootNode = fpGrowth.treeNode('pyramid',9,None)

接下來增加一個(gè)子節(jié)點(diǎn):

In [46]: rootNode.children['eye'] = fpGrowth.treeNode('eye',13,None)

顯示子節(jié)點(diǎn):

In [47]: rootNode.disp()
   pyramid   9
     eye   13

再增加一個(gè)節(jié)點(diǎn)看看子節(jié)點(diǎn)的展示效果:

In [48]: rootNode.children['phoenix']=fpGrowth.treeNode('phoenix',3,None)
    ...: rootNode.disp()
    ...: 
   pyramid   9
     eye   13
     phoenix   3

除了剛剛給出的FP樹以外瓦糕,還需要一個(gè)頭指針表來指向給定類型的第一個(gè)實(shí)例底洗。這里使用一個(gè)字典作為數(shù)據(jù)結(jié)構(gòu),來保存頭指針表咕娄。除了存放指針外亥揖,頭指針表還可以用來存放FP樹中每類元素的總數(shù)。
接下來,我們通過代碼來實(shí)現(xiàn)上述過程:

def createTree(dataSet, minSup=1): #數(shù)據(jù)集费变,最小支持度
    headerTable = {}
    #遍歷數(shù)據(jù)集兩次
    for trans in dataSet:#統(tǒng)計(jì)
        for item in trans:
            headerTable[item] = headerTable.get(item, 0) + dataSet[trans]
    for k in headerTable.keys():  #移除不滿足最小支持度的元素項(xiàng)
        if headerTable[k] < minSup: 
            del(headerTable[k])
    freqItemSet = set(headerTable.keys())
    #print 'freqItemSet: ',freqItemSet
    if len(freqItemSet) == 0: return None, None  #都不滿足摧扇,則退出
    for k in headerTable:
        headerTable[k] = [headerTable[k], None] #修改為下一步準(zhǔn)備
    #print 'headerTable: ',headerTable
    retTree = treeNode('Null Set', 1, None) #根節(jié)點(diǎn)
    for tranSet, count in dataSet.items():  #第二次遍歷
        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)#用排序后的頻率項(xiàng)進(jìn)行填充
    return retTree, headerTable 

def updateTree(items, inTree, headerTable, count):
    if items[0] in inTree.children:#檢查第一個(gè)元素是否作為子節(jié)點(diǎn)存在
        inTree.children[items[0]].inc(count) #更新計(jì)數(shù)
    else:   #新建一個(gè)子節(jié)點(diǎn)添加到樹中
        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:#對剩下的元素項(xiàng)迭代調(diào)用,每一次奧調(diào)用去掉第一個(gè)元素
        updateTree(items[1::], inTree.children[items[0]], headerTable, count)
        
def updateHeader(nodeToTest, targetNode):   #確保節(jié)點(diǎn)鏈接指向樹中該元素項(xiàng)的每一個(gè)實(shí)例
    while (nodeToTest.nodeLink != None):    #直達(dá)鏈尾
        nodeToTest = nodeToTest.nodeLink
    nodeToTest.nodeLink = targetNode

下面將數(shù)據(jù)集和數(shù)據(jù)包裝器加入代碼中:

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

def createInitSet(dataSet):
    retDict = {}
    for trans in dataSet:
        retDict[frozenset(trans)] = 1
    return retDict

首先挚歧,導(dǎo)入數(shù)據(jù)庫實(shí)例:

In [2]: import fpGrowth
   ...: simpDat = fpGrowth.loadSimpDat()
   ...: 

In [3]: simpDat
Out[3]: 
[['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']]

接下來為了函數(shù)createTree()扛稽,需要對上面的數(shù)據(jù)進(jìn)行格式化處理:

In [4]: initSet = fpGrowth.createInitSet(simpDat)
   ...: initSet
   ...: 
Out[4]: 
{frozenset({'e', 'm', 'q', 's', 't', 'x', 'y', 'z'}): 1,
 frozenset({'n', 'o', 'r', 's', 'x'}): 1,
 frozenset({'z'}): 1,
 frozenset({'s', 't', 'u', 'v', 'w', 'x', 'y', 'z'}): 1,
 frozenset({'p', 'q', 'r', 't', 'x', 'y', 'z'}): 1,
 frozenset({'h', 'j', 'p', 'r', 'z'}): 1}

于是可以通過如下 命令創(chuàng)建FP樹:

In [10]: myFPtree, myHeaderTab = fpGrowth.createTree(initSet, 3)
    ...: 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

上面給出的是元素項(xiàng)及其對應(yīng)的頻率計(jì)數(shù)值,其中每個(gè)縮進(jìn)表示所處的樹的深度滑负。
現(xiàn)在我們已經(jīng)構(gòu)建了FP樹在张,接下來就使用它進(jìn)行數(shù)據(jù)挖掘。
從FP樹中抽取頻繁項(xiàng)集的三個(gè)基本步驟如下:
(1)從FP樹中獲取條件模式基矮慕;
(2)利用條件模式基帮匾,構(gòu)建一個(gè)條件FP樹;
(3)迭代重復(fù)步驟(1)和步驟(2)痴鳄,直到樹包含一個(gè)元素項(xiàng)為止瘟斜。

首先從上一節(jié)發(fā)現(xiàn)的已經(jīng)保存在頭指針表中的單個(gè)頻繁元素項(xiàng)開始。對于每一個(gè)元素項(xiàng)痪寻,獲得其對于的條件模式基(conditional pattern base)螺句。條件模式基是以所查找元素項(xiàng)為結(jié)尾的路徑集合。每一條路徑其實(shí)都是一條前綴路徑(prefixpath)橡类。簡而言之蛇尚,一條前綴路徑是介于所查找元素項(xiàng)與樹根節(jié)點(diǎn)之間的所有內(nèi)容。
下面的程序清單給出了前綴路徑發(fā)現(xiàn)的代碼:

def ascendTree(leafNode, prefixPath): #迭代回溯整棵樹
    if leafNode.parent != None:
        prefixPath.append(leafNode.name)
        ascendTree(leafNode.parent, prefixPath)
   
#遍歷鏈表直到到達(dá)結(jié)尾顾画,每遇到一個(gè)元素項(xiàng)都會調(diào)用ascendTree()函數(shù)來上溯FP樹佣蓉,ing收集所有遇到的元素項(xiàng)的名稱    
def findPrefixPath(basePat, treeNode): 
    condPats = {}
    while treeNode != None:
        prefixPath = []
        ascendTree(treeNode, prefixPath)
        if len(prefixPath) > 1: 
            condPats[frozenset(prefixPath[1:])] = treeNode.count
        treeNode = treeNode.nodeLink
    return condPats

使用之前構(gòu)建的樹來看一下實(shí)際的運(yùn)行效果:

In [4]: import fpGrowth
   ...: fpGrowth.findPrefixPath('x',myHeaderTab['x'][1])
   ...: 
Out[4]: {frozenset({'z'}): 3}

In [5]: fpGrowth.findPrefixPath('z',myHeaderTab['z'][1])
Out[5]: {}

In [6]: fpGrowth.findPrefixPath('r',myHeaderTab['r'][1])
Out[6]: {frozenset({'s', 'x'}): 1, frozenset({'z'}): 1, frozenset({'x', 'y', 'z'}): 1}

下面繼續(xù)補(bǔ)充完整程序:

def mineTree(inTree, headerTable, minSup, preFix, freqItemList):
    bigL = [v[0] for v in sorted(headerTable.items(), key=lambda p: p[1])]#對頭指針表的元素項(xiàng)按照其出現(xiàn)的頻率進(jìn)行排序
    for basePat in bigL:  #從頭指針的底端開始
        newFreqSet = preFix.copy()
        newFreqSet.add(basePat)
        freqItemList.append(newFreqSet)
        condPattBases = findPrefixPath(basePat, headerTable[basePat][1])
        myCondTree, myHead = createTree(condPattBases, minSup)
        if myHead != None: #遞歸調(diào)用
            print 'conditional tree for: ',newFreqSet
            myCondTree.disp(1)      
            mineTree(myCondTree, myHead, minSup, newFreqSet, freqItemList)

接下來運(yùn)行mineTree(),顯示出所有的條件樹:

In [14]: import fpGrowth
    ...: freqItems = []
    ...: fpGrowth.mineTree(myFPtree, myHeaderTab, 3, set([]),freqItems)
    ...: 
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(['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', 't'])
   Null Set   1
     y   3
conditional tree for:  set(['x'])
   Null Set   1
     z   3

下面檢查一下返回的項(xiàng)集是否與條件樹匹配:

In [15]: freqItems
Out[15]: 
[{'y'},
 {'y', 'z'},
 {'x', 'y', 'z'},
 {'x', 'y'},
 {'s'},
 {'s', 'x'},
 {'t'},
 {'t', 'z'},
 {'t', 'y', 'z'},
 {'t', 'x', 'z'},
 {'t', 'x', 'y', 'z'},
 {'t', 'x'},
 {'t', 'x', 'y'},
 {'t', 'y'},
 {'r'},
 {'x'},
 {'x', 'z'},
 {'z'}]
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末亲雪,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子疚膊,更是在濱河造成了極大的恐慌义辕,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寓盗,死亡現(xiàn)場離奇詭異灌砖,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)傀蚌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進(jìn)店門基显,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人善炫,你說我怎么就攤上這事撩幽。” “怎么了?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵窜醉,是天一觀的道長宪萄。 經(jīng)常有香客問我,道長榨惰,這世上最難降的妖魔是什么拜英? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮琅催,結(jié)果婚禮上居凶,老公的妹妹穿的比我還像新娘。我一直安慰自己藤抡,他們只是感情好侠碧,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著杰捂,像睡著了一般舆床。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上嫁佳,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天挨队,我揣著相機(jī)與錄音,去河邊找鬼蒿往。 笑死盛垦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的瓤漏。 我是一名探鬼主播腾夯,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蔬充!你這毒婦竟也來了蝶俱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤饥漫,失蹤者是張志新(化名)和其女友劉穎榨呆,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體庸队,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡积蜻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了彻消。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片竿拆。...
    茶點(diǎn)故事閱讀 40,675評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖宾尚,靈堂內(nèi)的尸體忽然破棺而出丙笋,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布不见,位于F島的核電站澳化,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏稳吮。R本人自食惡果不足惜缎谷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望灶似。 院中可真熱鬧列林,春花似錦、人聲如沸酪惭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽春感。三九已至砌创,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鲫懒,已是汗流浹背嫩实。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留窥岩,地道東北人甲献。 一個(gè)月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像颂翼,于是被迫代替她去往敵國和親晃洒。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容