1. 程式人生 > >Apriori演算法簡介及實現(python)

Apriori演算法簡介及實現(python)

Apriori這個詞的意思是“先驗的”,從priori這個詞根可以猜出來~;) 。該演算法用於從資料中挖掘頻繁項資料集以及關聯規則。其核心原理是基於這樣一類“先驗知識”: 

如果一個數據項在資料庫中是頻繁出現的,那麼該資料項的子集在資料庫中也應該是頻繁出現的(命題1)

$$     \forall X,Y\in J:(X\subseteq Y)\rightarrow f(X)\leq f(Y)  $$

反之亦然,其逆否命題為:

如果一個數據項在資料庫中很少出現,那麼包含該資料項的父集(superset)在資料庫中也應該很少出現。(命題2)

  $$ f(X)\geq f(Y)\rightarrow \forall X,Y\in J:(X\supseteq Y) $$

背景知識:

①假設我們要從資料庫中找到如下一種關聯規則:

$$ x\rightarrow y $$

也就是說,當某一資料項包含包含集合X時,該資料項肯定包含集合Y。

②既然說有X的地方必定有Y,那麼我們需要大量的資料來說明這一點。用X和Y同時出現的次數除以資料庫中資料項的總數得到“支援度”的概念:

  $$    Support,s(X\rightarrow Y) = \frac {\delta (X \cup Y)}{N};  $$

③在集合X出現的資料項中,是否一定會出現集合Y呢?我們用X和Y同時出現的次數除以X出現的全部次數,得到“置信度”的概念:

   $$   Confidence,c(X\rightarrow Y) = \frac {\delta (X \cup Y)}{\delta (X)}; $$

深入理解apriori演算法:

分析“支援度”和“置信度”的概念可知,在給定“支援度”和“置信度”的條件下為了找到關聯規則,首先需要找到符合“支援度”條件的X和Y的並集{X,Y}。由命題1可知,如果集合{X,Y}滿足“支援度”條件(即頻繁出現),那麼集合中的每個元素也應該是頻繁出現的。集合的構成可以用樹來表示,下面用圖1來說明。

width= 

圖 1 若{c,d,e}頻繁出現,則{cd}{ce}{de},{c}{d}{e}也頻繁出現

width= 

圖 2如果{a,b}不是頻繁集,那麼{abc}{abd}{abe}{abcd}{abce}{abde}{abcde}也都不是頻繁集。

由此可見,如果我們從單一元素所構成的集合下手(也就是上圖中樹的第一層,記為C1),根據“支援度”判別條件對該樹進行“剪枝”,將大大降低計算的次數。

得到C1後,如果根據組合原理直接生成C2然後對每個可能的組合計算“支援度”,計算量依然很大。這裡再次進行剪枝。為了不失一般性,對於Ck-1層中的每個集合先排序,然後將滿足以下條件的集合融合,構成Ck層

$$ a_{i}=b_{i} (for\quad i=1,2,...,k-2) and a_{k-1}\neq b_{k-1} $$

之所以這樣做是因為,根據命題2,如果集合C4層的{acde}是頻繁集,那麼C3層中必定要存在{acd}和{ace}。因此只需在C3成對這兩個集合融合即可,不必再將{ace}和{ade}融合,在C3層對元素排序的目的也正是在此,快速地找到滿足條件的子集並融合,避免重複計算。

優化:

在得到Ck層後,計算其中每個集合的“支援度”時,需要從資料庫中遍歷所有的資料項看是否包含該集合。這裡可以採用Hash表將所有的資料對映到一張表上,以後就不用遍歷整個資料庫而是隻遍歷Hash值相同的所有資料項。

生成規則:

對於前面得到的頻繁項集合中每個元素,其可能生成的規則可以表示為下圖

width= 

圖3 從頻繁項生成規則

以上圖為例來說明,假設由{bcd}生成{a}這一規則不滿足置信度公式,回顧“置信度”的公式,也就是說{bcd}在資料庫中出現的次數偏多,而{a}出現的次數偏少,根據命題1,{bcd}的子集也是頻繁項,根據命題2,{a}的父集也很少出現,從而{bc}生成{ad}等規則的置信度更低,然後將其從集合樹上減去。

總結:

將以上過程聯絡起來,就得到了書上的虛擬碼,我將其用通俗的語言解釋一下:

1. 遍歷資料庫,得到所有資料項構成的並集(也就是得到圖1的C1層)

2. 計算Ck層中每個元素的支援度(該過程可用Hash表優化),刪除不符合的元素,將剩下的元素排序,並加入頻繁項集R

3. 根據融合規則將Ck層的元素融合得到Ck+1,

4. 重複2,3步直到某一層元素融合後得到的是空集

5. 遍歷R中的元素,設該元素為A={a1,a2......,ak}

6. 按照圖 所示方法先生成I1層規則,即{x|x屬於A且≠ai} →{ai}

7. 計算該層所有規則的“置信度”,刪除不符合的規則,將剩下的規則作為結果輸出。

8. 生成下一層的規則,計算“置信度”,輸出結果。

參考文獻:

width=width=

Python原始碼:

from numpy import*import itertools
 
 support_dic ={}#生成原始資料,用於測試def loadDataSet():return[[1,3,4],[2,3,5],[1,2,3,5],[2,5]]#獲取整個資料庫中的一階元素def createC1(dataSet):
     C1 =set([])for item in dataSet:
         C1 = C1.union(set(item))return[frozenset([i])for i in C1]#輸入資料庫(dataset) 和 由第K-1層資料融合後得到的第K層資料集(Ck),#用最小支援度(minSupport)對 Ck 過濾,得到第k層剩下的資料集合(Lk)def getLk(dataset,Ck, minSupport):global support_dic
     Lk={}#計算Ck中每個元素在資料庫中出現次數for item in dataset:forCiinCk:ifCi.issubset(item):ifnotCiinLk:Lk[Ci]=1else:Lk[Ci]+=1#用最小支援度過濾Lk_return=[]forLiinLk:
         support_Li =Lk[Li]/float(len(dataSet))if support_Li >= minSupport:Lk_return.append(Li)
             support_dic[Li]= support_Li
     returnLk_return#將經過支援度過濾後的第K層資料集合(Lk)融合#得到第k+1層原始資料Ck1def genLk1(Lk):Ck1=[]for i in range(len(Lk)-1):for j in range(i +1, len(Lk)):if sorted(list(Lk[i]))[0:-1]== sorted(list(Lk[j]))[0:-1]:Ck1.append(Lk[i]|Lk[j])returnCk1#遍歷所有二階及以上的頻繁項集合def genItem(freqSet, support_dic):for i in range(1, len(freqSet)):for freItem in freqSet[i]:
             genRule(freItem)#輸入一個頻繁項,根據“置信度”生成規則#採用了遞迴,對規則樹進行剪枝def genRule(Item, minConf=0.7):if len(Item)>=2:for element in itertools.combinations(list(Item),1):if support_dic[Item]/float(support_dic[Item- frozenset(element)])>= minConf:print str([Item- frozenset(element)])+"----->"+ str(element)print support_dic[Item]/float(support_dic[Item- frozenset(element)])
                 genRule(Item- frozenset(element))#輸出結果if __name__ =='__main__':
     dataSet = loadDataSet()
     result_list =[]Ck= createC1(dataSet)#迴圈生成頻繁項集合,直至產生空集whileTrue:Lk= getLk(dataSet,Ck,0.5)ifnotLk:break
         result_list.append(Lk)Ck= genLk1(Lk)ifnotCk:break#輸出頻繁項及其“支援度”print support_dic
     #輸出規則
     genItem(result_list, support_dic)