摘要:本文對Apriori算法進行了簡單介紹,並通過Python進行實現,進而結合UCI數據庫中的肋形蘑菇數據集對算法進行驗證。
“啤酒與尿布”的例子相信很多人都聽說過吧,故事是這樣的:在一家超市中,人們發現了一個特別有趣的現象,尿布與啤酒這兩種風馬牛不相及的商品居然擺在一起。但這一奇怪的舉措居然使尿布和啤酒的銷量大幅增加了。這可不是一個笑話,而是一直被商家所津津樂道的發生在美國沃爾瑪連鎖超市的真實案例。原來,美國的婦女通常在家照顧孩子,所以她們經常會囑咐丈夫在下班回家的路上為孩子買尿布,而丈夫在買尿布的同時又會順手購買自己愛喝的啤酒。這個發現為商家帶來了大量的利潤,但是如何從浩如煙海卻又雜亂無章的數據中,發現啤酒和尿布銷售之間的聯系呢?這種從大規模的數據中發現物品間隱含關系的方法被稱為關聯分析,也就是本文要主要研究的一種常用的分析方法,Apriori算法是最著名的關聯規則挖掘算法之一。下面就圍繞該算法展開學習。
一 關聯分析
關聯分析是一種在大規模數據集中尋找有趣關系的任務。這些任務有兩種形式:頻繁項集和關聯規則。頻繁項集是經常出現在一塊的物品的集合;關聯規則暗示的是兩種物品之間可能存在很強的關系。可以結合某家店的交易清單來說明這兩個概念:
交易號碼 |
商品 |
0 |
豆奶,草莓 |
1 |
草莓,尿布,啤酒,辣椒醬 |
2 |
豆奶,尿布,黃瓜,餅干 |
3 |
黃瓜,餅干,尿布,啤酒 |
4 |
黃瓜,啤酒,尿布,黃瓜 |
頻繁項集指的就是那些經常一起出現的物品集合,比如{啤酒,尿布,餅干}就是頻繁項集中的一個例子,而根據上表也可以找到尿布->啤酒這樣的關聯規則。
而我們是要通過關聯分析大規模數據從而發現數據之間存在的有趣關系,那么問題來了,什么樣的關系是有趣的呢?而這個有趣又是怎么定義的呢?我們可以通過支持度(support)和可信度(置信度confidence)來定義。一個項集的支持度指的是數據集中包含該項集記錄所占的比例,上例中{豆奶}的支持度是2/5,{啤酒,尿布}的支持度是3/5;可信度是針對於像{尿布}->{啤酒}這樣的關聯規則來定義的,定義為:支持度({尿布,葡萄酒})/支持度(尿布)。
二 Apriori原理
上述我們通過支持度和可信度來定義發現數據之間存在的關系。我們知道,在商品列表中,可能存在單一商品組成的頻繁項集,當然也存在兩個以及兩個以上的商品組成的頻繁項集。而在計算一個頻繁項集的支持度時,通常需要遍歷所有的商品列表求得,對於列表數目較少的情況該方法無疑是沒問題的,但當列表數目成千上萬時,計算量過大,這種方法勢必是不適用的。
那么如何解決上述問題呢,Apriori原理可以解決!Apriori原理是說如果某個項集是頻繁的,那么它的所有子集勢必也是頻繁的。這個原理從表面上看沒什么大用,但是反過來,如果一個項集是非頻繁項集,那么它所對應的超集就全都是非頻繁項集。這樣在確定了一個項集是非頻繁項集了之后,它所對應的超集的支持度我們就可以不去計算了,這在很大程度上避免了項集數目的指數增長,可以更加合理的計算頻繁項集。
三 Apriori算法
(1)使用Apriori算法來發現頻繁項集
Apriori算法是用來發現頻繁項集的一種方法。Apriori算法的兩個輸入參數分別是最小支持度和數據集。該算法首先生成所有單個物品的項集列表,遍歷之后去掉不滿足最小支持度要求的項集;接下來對剩下的集合進行組合生成包含兩個元素的項集,去掉不滿足最小支持度的項集;重復該過程直到去掉所有不滿足最小支持度的項集。
首先采用python生成所有的單個物品所對應的項集,並構建一個得到頻繁項集的函數,代碼如下:
# -*- coding: cp936 -*- ''' Apriori算法 Ben 2015.09.28 ''' #coding:utf-8 from numpy import * def loadData(): 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 map(frozenset,c1) def scanD(D,Ck,minSupport): ssCnt = {} for tid in D: for can in Ck: if can.issubset(tid):#判斷tid是否在can中 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
對上述代碼進行測試:
#test dataSet = loadData() c1 = createC1(dataSet) D = map(set,dataSet) L1,supportData = scanD(D,c1,0.5) print L1 print 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 = 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
這樣就對得到頻繁項集的思想進行了實現,下面驗證:
dataSet = loadData() minSupport = 0.5 a,b = apriori(dataSet,minSupport) print a print b
結果為所有頻繁項集以及其所對應的支持度,符合預期。
(2)從頻繁項集中挖掘關聯規則
頻繁項集可以使用Apriori算法尋找,當然下來就是要找出關聯規則了。我們知道,假設有一個頻繁項集,它們之間就有可能有一條關聯規則,即可以表示為:"...—>...",但反過來並不一定成立(其中箭頭左邊對應的集合為前件,箭頭右邊對應的集合為后件)。
在上一節,我們使用最小支持度來量化頻繁項集,對應的,采用可信度來量化關聯規則。其中一條規則p—>H的可信度定義為:support(P|H)/support(P),為找到其中的關聯規則,我們可以先生成一個可能的規則列表,然后測試每條規則的可信度,結合可信度的最小要求,得到關聯規則。同尋找頻繁項集類似,我們可以為每個頻繁項集產生許多關聯規則,這樣就會有很多的關聯規則產生。結合Apriori原理,如果某條規則不滿足最小可信度要求,那么該規則的所有子集也就不滿足最小可信度要求,據此我們可以減少需要測試的規則數目,簡化問題。
尋找關聯規則的思想是:從一個頻繁項集開始,創建一個規則列表,首先將規則的右邊限定為一個元素,對這些規則進行測試,接下來合並剩下的規則來創建一個新的規則列表,規則的右邊限定為兩個元素,就這樣一步一步實現,代碼如下:
#使用關聯規則生成函數 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,br1,minConf = 0.7): m = len(H[0]) if (len(freqSet)>(m + 1)): Hmp1 = aprioriGen(H,m+1) Hmp1 = calcConf(freqSet,Hmp1,supportData,br1,minConf) if (len(Hmp1) > 1): rulesFromConseq(freqSet,Hmp1,supportData,br1,minConf)
接下來對上述的程序進行測試:
#test dataSet = loadData() minSupport = 0.5 L,suppData = apriori(dataSet,minSupport) rules = generateRules(L,suppData,minConf = 0.5) print rules
上述程序的結果表明該算法在小數據集中可以實現,其中更換可信度閾值minConf可以獲得不同的關聯規則。
四 發現毒蘑菇的特征
上面我們已經將Apriori算法應用到小數據集上,在本節我們將算法應用到真實數據上。有時我們尋找的不是頻繁項集,而是對某些特定的有規律的特征。在本節中,我們會尋找毒蘑菇的一些公共特征,從而發現哪些特征是毒蘑菇特有的,我們從UCI數據庫中尋找數據集mushroom.dat,其中第一個特征表示有毒或者沒毒,2表示有毒。下面進行測試:
mushDatSet = [line.split() for line in open('mushroom.dat').readlines()] L,supportData = apriori(mushDatSet,minSupport = 0.3) for item in L[1]: if item.intersection('2'): print item
結果如下:
frozenset(['2', '59']) frozenset(['39', '2']) frozenset(['2', '67']) frozenset(['2', '34']) frozenset(['2', '23']) frozenset(['2', '86']) frozenset(['76', '2']) frozenset(['90', '2']) frozenset(['2', '53']) frozenset(['93', '2']) frozenset(['63', '2']) frozenset(['2', '28']) frozenset(['2', '85']) frozenset(['2', '36'])
這樣就可以發現毒蘑菇相關的特征。
這就是我對該算法的理解和總結,難免有錯,還望大家不吝賜教~