FP_growth算法

原文出處:http://blog.sina.com.cn/s/blog_6e85bf420100ogp8.html

FP_growth算法是韓家煒老師在2000年提出的關聯分析算法,該算法和Apriori算法最大的不同有兩點:第一,不產生候選集,第二,只需要兩次遍歷數據庫,大大提高了效率,用31646條測試記錄,最小支持度是2%,用Apriori算法要半個小時但是用FP_growth算法只要6分鐘就可以了,效率非常明顯。它的核心是FP_tree,一種樹型數據結構,特點是儘量把相同元素用一個節點表示,這樣就大大減少了空間,和birch算法有類似的思想。還是以如下數據爲例。FP_growth算法
每一行表示一條交易,共有9行,既9筆交易,左邊表示交易ID,右邊表示商品名稱。最小支持度是22%,那麼每件商品至少要出現9*22%=2次纔算頻繁。第一次掃描數據庫,統計每件商品出現的次數,按次數對各個商品遞減排序,有:FP_growth算法。然後第二次掃描數據庫,在每條交易中按此種順序給商品排序,如果有某個商品出現的次數小於閾值2,則刪除該商品,有:

FP_growth算法
剩下的就是構造FP_tree了,這是核心,樹的每個節點的結構體如下:

//FP-tree的存儲結構
typedef struct CSNode{
 //商品編號
 int item;
 //次數
 int count;
 //父節點,孩子節點,兄弟節點
 CSNode *parent,*firstchild,*nextsibling;
 //相同商品的前驅,後繼節點,方便將相同商品的節點連接起來,根節點的直接孩子節點的這兩個指針都是空
 CSNode *pre,*next;
}*CSTree;
其中item,*firstchild,*nextsibling是樹這個結構體常用的屬性。count記錄商品item出現的次數,*parent是爲了方便從葉子節點逆向訪問根節點而設置的。*pre,*next的註釋已經很清楚了。構造樹的原則是:將每條記錄看做一個從根節點到葉子節點的路徑,如果某個商品在節點中已經存在了,則對應count計數器加1,相當於所有的前綴都要加1,如果不存在則在該條記錄的後面商品開闢一條新的路徑。下面一條一條記錄演示怎麼構造FP_tree。

   第三次訪問數據庫,構造FP_tree。第一條記錄:I2,I1,I5,有:

FP_growth算法
父節點沒有表示出來,根節點是空節點。2:1表示商品2出現了1次,其他表示類推。左邊的數組按照商品順序遞減排列,保存了各個商品的當前指針,目的是爲了在後面找到相同的後綴,將相同的商品用單項箭頭虛線連起來,實際是雙向鏈表鏈接的,並且將此時的節點商品1和節點商品5保存爲商品1和商品5的當前指針,而對於商品2,商品3,商品4的當前指針還在左邊的數組中保存。注意根節點的直接孩子不用連起來,後面會講理由。第二條記錄:I2,I4,有:

FP_growth算法
該記錄和第一條記錄共用前綴I2,所以商品2的次數要加1,而商品4則作爲商品2的一個新孩子節點,這裏沒有把兄弟節點畫出來。並且左邊商品4要指向該節點,此時商品4的當前指針指向節點商品4。第三條記錄:I2,I3,類似,結果是:

FP_growth算法
第四條記錄:I2,I1,I4有:

FP_growth算法
當添加完商品4後,商品4的當前指針要指向新的節點商品4,此時兩條紅色的虛線就把以商品4爲後綴的節點連起來了。第5條記錄:I1,I3,有:

FP_growth算法
商品1由於和根節點的所有直接子孩子(這裏只有商品2這個子孩子)不同,因此要另外開闢一條路徑。商品3的當前指針要指向新的節點商品3,如圖中的黃色虛線所指,到這裏體現了構造FP_tree的一般性了。再把剩下的記錄都加進來,最終的FP_tree是:

FP_growth算法
這顆FP_tree最大程度的把相同的商品放在用同一個節點保存,最大限度的節省了空間。剩下的工作就是挖掘這顆FP_tree了。

   挖掘的目的是找出FP_tree的各個路徑中相同的集合,有兩中方式,方式一,從根節點朝葉子節點順着遍歷樹,方式二,從葉子節點朝根節點逆着遍歷樹。想想方式一挺麻煩的,幸虧我們設置了*parent指針,通過它就可以很方便的用方式二。我們從商品出現次數由少到多的順序開始遍歷樹,先從商品5開始,由於有*pre,*next指針分方便將所有以商品5做爲葉子節點的路徑全找出來,然後再根據*parent指針找到父節點,根節點是空不用找。以I5做元素的條件模式基是:{(I2I1:1),(I2 I1I3:1)}。後面的1表示出現商品I2,I1,I5同時出現的次數。現在解釋爲什麼:根節點的直接孩子不用*pre,*next指針連起來,因爲假如連起來的話,那麼以它爲後綴時,將沒有前綴,也就是說它的頻繁項集是1,這在大多數情況下沒意義。由它構造出條件FP_tree,注意由於開始按照商品名稱排序了,那麼條件模式基中的每一項也會按照這種方式排序。如果條件模式基中某項A是另外一項B的子集那麼在算B時,要將A出現的次數加上,實現這個功能最簡單明瞭的方法就是一一匹配,假如條件模式基共有N項,則時間複雜度是N的平方,若先按照條件模式基的長度遞增排序得到:{(I2I1:1),(I2 I1I3:1)},排序的時間複雜度是N*log(N),那麼只有可能是長度短的項是長度長的項的子集,此時總匹配次數是:N-1 + N-2 +,,, + 1 = N*(N-1)/2,和前面的排序時間加起來是:N*log(N) +N*(N-1)/2當N大於時4時,該值小於N的平方。在實際中N一般會大於4。最終我們得到以I5作爲後綴的頻繁項集是:{I2I5:2},{I1 I5:2},{I2 I1I5:2}他們出現的次數都大於等於最小支持度。類似可以得到其它後綴的頻繁項集。

   FP_growth算法不產生候選序列,並且只需要3次遍歷數據庫,對比Apriori算法而言有了很大的改進。其實想想這也符合歷史發展的規律,Apriori在1993年才提出來的,那是數據挖掘纔剛起步,而到2000年時,已經有了一定的發展,FP_growth是站在Apriori的肩膀上發明的,這種現象具有普遍性。


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