Apriori原理:如果某個項集是頻繁的,那麼它的所有子集都是頻繁的。
Apriori演算法:
1 輸入支援度閾值t和資料集
2 生成含有K個元素的項集的候選集(K初始為1)
3 對候選集每個項集,判斷是否為資料集中某條記錄的子集
4 如果是:增加候選集的計數
5 保留頻繁集(計數>t)
6 根據頻繁集生成含有K+1個元素的項集候選集
7 迴圈2-5,直至候選集為空
Apriori演算法是有缺點的
缺點是:1.需要多次掃描資料庫 2.產生大量的候選頻繁集 3.時間和空間複雜度高。
從演算法第3步可以看出,候選頻繁項集不一定是原始資料集中某一項的子集,即:x為頻繁項,y為頻繁項,{x,y}可以組成一個候選頻繁項集,但是原始資料集中不一定存在{x,y}的組合。這使得Apriori演算法有大量時間浪費在無效的集合上。
FP樹增長演算法是一種挖掘頻繁項集的演算法。Apriori演算法雖然簡單易實現,效果也不錯,但是需要頻繁地掃描資料集,IO費用很大。FP樹增長演算法有效地解決了這一問題,其通過兩次掃描資料集構建FP樹,然後通過FP樹挖掘頻繁項集。
背景知識
1.什麼是項和項集?
比如我們在購物的時候,購物車內的每一件商品成為一項,若干個項的集合成為項集。例如{啤酒,尿布}成為一個二元項集。
2.什麼是支援度?
支援度是在所有的項集中{X,Y}出現的可能性。
例如:購買商品的資料是(表示4條購物資訊):
①{啤酒,尿布,娃哈哈}
②{啤酒,方便麵}
③{尿布,奶粉}
④{啤酒,尿布,洗髮水}
在這組資料中,{啤酒,尿布}出現的可能性就是這裡面資料的概率。{啤酒,尿布}的支援度是2/4=50%.
{尿布,奶粉}的支援度是1/4=25%
3.什麼是頻繁項集?
我們首先設定一個最小閾值A,支援度大於A的項集就是頻繁項集,小於A的項集被剔除。
比如 我們設定閾值為30%,在上面的例子中{啤酒,尿布}就是頻繁項集,{尿布,奶粉}就要被剔除。
問題:如何求出頻繁項集?
首先構造FP樹
然後通過FP樹可以求出頻繁項集
FP演算法
FP增長演算法需要根據資料集生成FP樹,步驟如下:
步驟一:統計每個元素出現次數,保留頻繁元素(假設次數>3),按照元素出 現次數降序排序。
其中h,j,p,w,v,u,n,o,q,p,e,m的次數是小於等於3的.因此把它們去掉,然後把其他的字母按照次數從大到小排列。
步驟二:構建FP樹
通過上面的序列按照每一行進入樹根初始化樹葉。如果沒有相同的字母就重新建立葉子節點,每個葉子節點有字母其次數。
如圖所示
先插入(z,r),再插入(z,x,y,s,t),再插入(z),如圖
最後插完的結果是:
步驟三:FP樹中找到元素的字首路徑
以r為例,r的字首路徑為:以根結點為起點,結點r為終點的所有路徑。
先找到FP樹中所有“r”結點,然後從每一個“r”結點向根結點方向查詢,找到的所有路徑就是“r”的字首路徑
然而,找到所有“r”結點,需要遍歷整棵FP樹,這使得演算法的時間複雜度會很高。
為了方便查詢,可以用連結串列來加快尋找的字首路徑。
將FP樹中所有相同的結點用連結串列連線,可以將查詢結點的時間複雜度從O(n)降到常數級。
通過上面的操作就得到了如下所示的資訊。
步驟四:根據字首路徑構造條件FP樹
t的條件FP樹如下:
t字首路徑中的頻繁元素包括{z:3,x:3,y:3},這個數字表示對應的元素在原始資料集中和t一起出現的次數,如{z,t}出現3次,{x,t}出現3次,{y,t}出現3次。顯然,這些項集都是頻繁項集。
很容易發現,t的字首路徑也是一個資料集,生成t條件FP樹的過程,跟前面生成FP樹的過程相同,我們也可以在t的條件FP樹基礎上構造x的條件FP樹,對應的就是{t,x}的條件FP樹。
顯然,這是個遞迴的過程。
步驟5:遞迴構造下一層條件FP樹,直至條件FP樹為空.
總結:
1、頻繁集不是從FP樹產生,而是通過每一層遞迴獲得的頻繁元素組合產生。FP樹的作用是尋找字首路徑。
相比於Apriori演算法,FP樹的作用相當於是把遍歷資料集的結果用樹的結構儲存下來,而避免了重複掃描資料集。
2、FP樹只需要掃描兩次資料集,第一次統計各個元素出現次數,第二次根據過濾排序後的資料集生成FP樹。後續的過程都在FP樹上進行。
3、{x,t}的條件FP樹和{t,x}的條件FP樹是否相同? 演算法是否重複計算了這兩個條件FP樹?
這個問題涉及到FP增長演算法的一個根本問題,為什麼要使用字首路徑?
找到字首路徑後,需要對字首路徑集合進行過濾和排序,獲得下一層遞迴的資料集。
那麼不使用字首路徑,而使用完整路徑顯然也可以達到目的。
字首路徑有個非常重要的優點。
構造FP樹之前由於對元素進行了排序,如果x出現在t的字首路徑中,那麼t不可能再出現在x的字首路徑中,所以使用字首路徑避免了相同集合的重複計算!
另外,字首路徑的查詢非常方便,一個結點向上查詢,獲得的字首路徑是固定的,如果使用完整路徑,結點向下查詢可能會出現分叉,處理起來會變得非常複雜。