1. 程式人生 > >【機器學習】Apriori演算法——原理及程式碼實現(Python版)

【機器學習】Apriori演算法——原理及程式碼實現(Python版)

Apriopri演算法

Apriori演算法在資料探勘中應用較為廣泛,常用來挖掘屬性與結果之間的相關程度。對於這種尋找資料內部關聯關係的做法,我們稱之為:關聯分析或者關聯規則學習。而Apriori演算法就是其中非常著名的演算法之一。關聯分析,主要是通過演算法在大規模資料集中尋找頻繁項集和關聯規則。

  • 頻繁項集:經常出現在一起的物品或者屬性的集合
  • 關聯規則:物品或者屬性之間存在的內在關係(統計學上的關係)

所以,我們常見的Apriori演算法中的主要包含兩大模組內容,一塊是尋找頻繁項集的函式模組,一塊是探索關聯規則的函式模組。

支援度與置信度

支援度與置信度是實現Apriori演算法無法迴避的兩個概念,支援度用來尋找頻繁項集,而置信度用來確定關聯規則。具體用處,在後續原理章節,進行介紹。

  • 支援度:頻繁項集在全體資料樣本中所佔的比例
    support(X, Y) = P(X, Y) = \frac{number(X, Y)}{number(all\ sample)}support(X,Y)=P(X,Y)=number(all sample)number(X,Y)
  • 置信度:體現為一個數據出現後,另一個數據出現的概率,或者說資料的條件概率
    confidence(X \Rightarrow Y) = P(Y | X) = \frac{P(X,Y)}{P(X)} =\frac{number(X,Y)}{number(X)}confidence(XY)=P(YX)=P(X)P(X,Y)=number(X)number(X,Y)

Apriori演算法原理

以商品購買為例,假設一家商店,出售四種商品,分別為商品0,商品1,商品2,商品3。我們希望通過挖掘買家購買商品的訂單資料,來進行商品之間的組合促銷或者說是擺放位置。那麼商品之間可能的組合如下圖所示:
圖片描述
針對這些商品,我們的目標是:從大量購買資料中找到經常一起被購買的商品。在尋找頻繁項集(即經常出現的商品組合)的過程中,我們採用支援度(support)來過濾商品組合,即頻繁項集。針對四中商品,我們要在整體資料集上進行15次輪詢,才可以統計出每個頻繁項集的支援度。試想,如果資料量較大,且商品種類不止四中的情況下,難道依舊採用逐個輪詢的方式進行統計嗎?那麼帶來的運算量也是巨大的,並且隨著商品種類的增加,頻繁項集的組合種類也將變為2^N-12N1種,隨著種類的增加,那麼帶來的運算代價呈現指數型增加。為了解決這個問題,研究人員便在Apriori原理的基礎上設計了Apriori演算法。
Apriori原理如下:如果某個項集是頻繁的,那麼它的所有子集也是頻繁的。反過來,如果一個項集是非頻繁集,那麼它的所有超集(包含該非頻繁集的父集)也是非頻繁的


於是,可以將上圖進行適當的優化,如下所示:
圖片描述
根據Apriori原理,我們知道陰影項集{2,3}是非頻繁的,那麼它的所有超集,也都是非頻繁的,如上圖灰色所示。在實際計算過程中,一旦計算出{2,3}的支援度不滿足最小支援度,那麼就不需要再計算{0,2,3}、{1,2,3}和{0,1,2,3}的支援度,因為它們也都是非頻繁集。

Apriori演算法實現

上面的部分也已經說了,Apriori演算法主要有兩部分組成:

  • 發現頻繁項集
  • 找出關聯規則

本部分將從兩個方面來實現程式碼,具體如下所示:

發現頻繁項集
def createC1(dataSet): C1=[] for transaction in dataSet: for item in transaction: if not [item] in C1: C1.append([item]) C1.sort() return list(map(frozenset,C1)) def scanD(D,CK,minSupport): ssCnt = {} for tid in D: for can in CK: if can.issubset(tid): if not can in ssCnt:ssCnt[can]=1 else:ssCnt[can]+=1 numItems = float(len(D)) retList = [] supportData={} for key in ssCnt: support = ssCnt[key]/numItems if support>=minSupport: retList.insert(0,key) supportData[key]=support return retList,supportData 電動叉車 #頻繁項集兩兩組合 def aprioriGen(Lk,k): retList=[] lenLk = len(Lk) for i in range(lenLk): for j in range(i+1,lenLk): L1=list(Lk[i])[:k-2];L2=list(Lk[j])[:k-2] L1.sort();L2.sort() if L1==L2: retList.append(Lk[i]|Lk[j]) return retList def apriori(dataSet,minSupport=0.5): C1=createC1(dataSet) D=list(map(set,dataSet)) L1,supportData =scanD(D,C1,minSupport) L=[L1] k=2 while(len(L[k-2])>0): CK = aprioriGen(L[k-2],k) Lk,supK = scanD(D,CK,minSupport) supportData.update(supK) L.append(Lk) k+=1 return L,supportData 
找出關聯規則
#規則計算的主函式
def generateRules(L,supportData,minConf=0.7): bigRuleList = [] for i in range(1,len(L)): for freqSet in L[i]: H1 = [frozenset([item]) for item in freqSet] if(i>1): rulesFromConseq(freqSet,H1,supportData,bigRuleList,minConf) else: calcConf(freqSet,H1,supportData,bigRuleList,minConf) return bigRuleList def calcConf(freqSet,H,supportData,brl,minConf=0.7): prunedH=[] for conseq in H: conf = supportData[freqSet]/supportData[freqSet-conseq] if conf>=minConf: print (freqSet-conseq,'--->',conseq,'conf:',conf) brl.append((freqSet-conseq,conseq,conf)) prunedH.append(conseq) return prunedH def rulesFromConseq(freqSet,H,supportData,brl,minConf=0.7): m = len(H[0]) if (len(freqSet)>(m+1)): Hmp1 = aprioriGen(H,m+1) Hmp1 = calcConf(freqSet,Hmp1,supportData,brl,minConf) if(len(Hmp1)>1): rulesFromConseq(freqSet,Hmp1,supportData,brl,minConf) 

整合整個程式碼如下所示:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Fri Nov 30 16:38:01 2018

@author: lxh
"""

def loadDataSet(): return [[1,3,4],[2,3,5],[1,2,3,5],[2,5]] def createC1(dataSet): C1=[] for transaction in dataSet: for item in transaction: if not [item] in C1: C1.append([item]) C1.sort() return list(map(frozenset,C1)) def scanD(D,CK,minSupport): ssCnt = {} for tid in D: for can in CK: if can.issubset(tid): if not can in ssCnt:ssCnt[can]=1 else:ssCnt[can]+=1 numItems = float(len(D)) retList = [] supportData={} for key in ssCnt: support = ssCnt[key]/numItems if support>=minSupport: retList.insert(0,key) supportData[key]=support return retList,supportData #頻繁項集兩兩組合 def aprioriGen(Lk,k): retList=[] lenLk = len(Lk) for i in range(lenLk): for j in range(i+1,lenLk): L1=list(Lk[i])[:k-2];L2=list(Lk[j])[:k-2] L1.sort();L2.sort() if L1==L2: retList.append(Lk[i]|Lk[j]) return retList def apriori(dataSet,minSupport=0.5): C1=createC1(dataSet) D=list(map(set,dataSet)) L1,supportData =scanD(D,C1,minSupport) L=[L1] k=2 while(len(L[k-2])>0): CK = aprioriGen(L[k-2],k) Lk,supK = scanD(D,CK,minSupport) supportData.update(supK) L.append(Lk) k+=1 return L,supportData #規則計算的主函式 def generateRules(L,supportData,minConf=0.7): bigRuleList = [] for i in range(1,len(L)): for freqSet in L[i]: H1 = [frozenset([item]) for item in freqSet] if(i>1): rulesFromConseq(freqSet,H1,supportData,bigRuleList,minConf) else: calcConf(freqSet,H1,supportData,bigRuleList,minConf) return bigRuleList def calcConf(freqSet,H,supportData,brl,minConf=0.7): prunedH=[] for conseq in H: conf = supportData[freqSet]/supportData[freqSet-conseq] if conf>=minConf: print (freqSet-conseq,'--->',conseq,'conf:',conf) brl.append((freqSet-conseq,conseq,conf)) prunedH.append(conseq) return prunedH def rulesFromConseq(freqSet,H,supportData,brl,minConf=0.7): m = len(H[0]) if (len(freqSet)>(m+1)): Hmp1 = aprioriGen(H,m+1) Hmp1 = calcConf(freqSet,Hmp1,supportData,brl,minConf) if(len(Hmp1)>1): rulesFromConseq(freqSet,Hmp1,supportData,brl,minConf) if __name__=='__main__': dataSet=loadDataSet() L,supportData=apriori(dataSet) rules = generateRules(L,supportData,minConf=0.7) 

輸出結果如下所示:
圖片描述