本人看過的關聯規則博文,很少有清晰的把關聯規則的算法說很明白的,希望讀者讀完本文可以有新的收獲。本文是在默認讀者有相關機器學習算法基礎的,總結和提升對關聯規則代碼實現的理解,並介紹相關案例。語言:python
一 引言
關聯規則起初是在購物籃分析中發現的,沃爾瑪超市在美國某地區啤酒和尿布放在一起賣,這種關聯規則有利於市場營銷決策的制定。
關聯規則是非監督學習的一種。
二 兩個重要的概念
我們認定滿足支持度和置信度的規則是有趣的,
支持度:P(A),及項集A出現的概率(頻數);
置信度:P(B / A), 條件概率; P(B / A)= P(AB)/P(A),所以支持度可以用來計算置信度,代碼是學習算法最好的途徑。
三 Apriori算法
網上很多文章介紹Apriori算法都是雲里霧里,下面梳理一下脈絡。
核心是apriori原理:如果某個項集是頻繁的,那么它是所有子集也是頻繁的。
apriori原理的精妙在於他的逆否命題,若子集不是頻繁的,則所有包含它的項集都是不頻繁的。這樣剪掉不頻繁項集時,就可以同時剪掉很多包含這個項集的不頻繁的項集了。若蠻力查找大頻項集,時間復雜度是指數型,例如4個項集,它的所有組合的復雜度是15。同理,可以剪掉后件不滿足置信度規則時,同時剪掉后件包含這個規則后件的規則。形象的說,看下圖:
eg
項集{0,1,2,3},計算所有的組合情況如下圖,好像一個格,時間復雜度是指數型的,按照項集元素數由小到大計算每個組合的支持度,{2,3}黑色圈不滿足最小支持度,由apriori定理,則所有以它為子集的項集均不滿足最小支持度,需要剪掉。
下面繼續考慮置信度,規則{012}->{3}不滿足最小置信度,由apriori定理,所有后件包含這條規則后件的規則需要剪掉,即二后件規則:{01}->{23},{02}->{1,3},{12}->{03}和三后件規則{0}->{123},{1}->{023},{2}->{013}。
讀到這里有關聯規則基礎的人應該會有感悟,通過下面代碼介紹可以細致的明白apriori算法的機制。
四 apriori實現
1 加載demo數據集(可以改成真實數據集,讀文件):
from numpy import * def loadDataSet(): return [[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]
2 生成一頻繁項集作為起始點,即長度為1的頻繁集,圖中的第一層:使用frozenset結構是因為set不可以作為dict的關鍵字
def createC1(dataSet):#create one item C1 = [] for transaction in dataSet: for item in transaction: if not [item] in C1: C1.append([item]) C1.sort() # print C1 return map(frozenset, C1)#use frozen set so we #can use it as a key in a dict
3 計算Ck支持度,並剪掉不滿足最小指出的項集。
def scanD(D, Ck, minSupport):#create one or more big frequence item ssCnt = {} for tid in D: for can in Ck: if can.issubset(tid): if not ssCnt.has_key(can): 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
4 由m頻繁項集生成m+1頻繁項集:舉例,Ck為1頻繁項集{01}{12}{02},生成{012},有個技巧,只合並前m-2項一樣的項集,這樣不會重復操作,如這個例子,{01}和{12}不合並,{12}{02}也不合並,只有{01}{02}合並,生成最后的{012};否則前幾個合並都是重復的。值得注意的是0-3項集要排好序,否則前k-2個沒有比較的必要。
def aprioriGen(Lk, k): #creates Ck retList = [] lenLk = len(Lk) # print 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() # print L1,'--',L2 if L1==L2: #if first k-2 elements are equal retList.append(Lk[i] | Lk[j]) #set union # print retList return retList
5 生成打頻項集的算法:
思路,生成1頻項集,根據最小支持度過濾;依次由m頻項集生成m+1頻項集,直到m+1頻項集為空停止迭代。
def apriori(dataSet, minSupport = 0.5): C1 = createC1(dataSet) D = map(set, dataSet) L1, supportData = scanD(D, C1, minSupport)#create one big frequence item L1 # print L1 L = [L1] k = 2 # print L while (len(L[k-2]) > 0): # print 'L[k-2]',L[k-2] # print L Ck = aprioriGen(L[k-2], k) Lk, supK = scanD(D, Ck, minSupport)#scan DB to get Lk supportData.update(supK) L.append(Lk) k += 1 return L, supportData
以上都是大頻項集的生成算法,下面繼續有趣的關聯規則的發現算法:
6 計算后件為H的規則的置信度,代碼可以看出只是一個條件概率公式而已;根據最小置信度,篩選出有趣的規則;
def calcConf(freqSet, H, supportData, brl, minConf=0.7): prunedH = [] #create new list to return for conseq in H: conf = supportData[freqSet]/supportData[freqSet-conseq] #calc confidence if conf >= minConf: brl.append((freqSet-conseq, conseq, conf)) prunedH.append(conseq) return prunedH
7 由后件數為m的規則集生成后件為后件數為m+1的規則集,並計算置信度;遞歸到沒有可以合並的規則集停止;
直觀的過程可以查看上圖的格,eq {23}->{01}和{12}->{03} 合並為{2}->{013};因為標紅處前k-2相同,為了避免重復的合並操作,同上面打大頻項集合並。
def rulesFromConseq(freqSet, H, supportData, brl, minConf=0.7):if (len(freqSet) > (m + 1)): #try further merging Hmp1 = aprioriGen(H, m+1)#create Hm+1 new candidates Hmp1 = calcConf(freqSet, Hmp1, supportData, brl, minConf)if (len(Hmp1) > 1): #need at least two sets to merge rulesFromConseq(freqSet, Hmp1, supportData, brl, minConf)
8 產生關聯規則的最后算法:
思路,由於規則的前后件均不能為空,所以只有二頻繁項集才能產生關聯規則;
1)首先由二頻繁項集生成規則集,遍歷所有的二頻繁項集(每個元素輪流作為后件),根據最小置信度過濾規則集;
eg 二頻繁項集{12},則計算規則{1}->{2}和{2}->{1}的置信度;
2)依次迭代,在三大頻項集生成規則集(每個元素輪流作為后件),需要考慮規則的合並,
eg 三大頻項集{123},則{12}->{3},{13}->{2},{23}->{1},此外考慮合並,{1}->{23},{2}->{13},{3}->{12},還要繼續合並:根據后件,前k-2個同的合並,本例前k-2個同的個數為0,所以停止,復雜的情況看步驟7;
def generateRules(L, supportData, minConf=0.7): #supportData is a dict coming from scanD bigRuleList = [] for i in range(1, len(L)):#only get the sets with two or more items 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
五 應用
關聯規則可以應用到哪些問題呢?
購物籃分析,搜索引擎的查詢詞,國會投票,毒蘑菇的相似特征提取等;
六 毒蘑菇的相似特征提取
毒蘑菇部分數據集如下:
1 3 9 13 23 25 34 36 38 40 52 54 59 63 67 76 85 86 90 93 98 107 113 2 3 9 14 23 26 34 36 39 40 52 55 59 63 67 76 85 86 90 93 99 108 114 2 4 9 15 23 27 34 36 39 41 52 55 59 63 67 76 85 86 90 93 99 108 115 1 3 10 15 23 25 34 36 38 41 52 54 59 63 67 76 85 86 90 93 98 107 113 2 3 9 16 24 28 34 37 39 40 53 54 59 63 67 76 85 86 90 94 99 109 114 2 3 10 14 23 26 34 36 39 41 52 55 59 63 67 76 85 86 90 93 98 108 114 2 4 9 15 23 26 34 36 39 42 52 55 59 63 67 76 85 86 90 93 98 108 115 2 4 10 15 23 27 34 36 39 41 52 55 59 63 67 76 85 86 90 93 99 107 115 1 3 10 15 23 25 34 36 38 43 52 54 59 63 67 76 85 86 90 93 98 110 114 2 4 9 14 23 26 34 36 39 42 52 55 59 63 67 76 85 86 90 93 98 107 115 2 3 10 14 23 27 34 36 39 42 52 55 59 63 67 76 85 86 90 93 99 108 114 2 3 10 14 23 26 34 36 39 41 52 55 59 63 67 76 85 86 90 93 98 107 115
每一行代表一個蘑菇的特征,第一列是決策類,1代表有毒,2代表五毒;
加載數據集:
dataset=[line.split() for line in open('mashroom.dat'),readline()]
查找大頻率項集:
L,supp=apriori(dataset,0.3)
apriori函數在算法部分已經實現,直接調用即可。
有時候我們只需要查找大頻項集,並不需要關聯規則,具體問題具體分析即可。
七 總結
整體算法就有兩個核心,1計算滿足最小支持度的大頻率項集,2挖掘滿足最小置信度的有趣規則;暴力遍歷每種組 合的方式指數級,利用了apriori定理,剪掉了不滿足要求的小項集和小后件規則的同時,剪掉包含他們的大頻度項集和大后件規則;還有一點主意的是。按 照圖中格的形式,一層一層有小到大迭代。