我们很多人可能知道“啤酒与尿布”的故事(如果不知道课百度下)。这就是关联分析中最有名的例子。
1、什么是关联分析?
关联分析是一种在大规模数据集中寻找有趣关系的任务,这些关系可以有两种形式:频繁项集或者关联规则。
频繁项集(frequent item set):是经常出现在一块的物品集合
关联规则(association rules):暗示两种物品之间可能存在很强的关系
举个例子:
但是我们如何定义这些关系呢?当寻找频繁项集时,频繁(frequent)的定义是什么?那么我们就要讲到支持度和可信度。
支持度(support):该数据集中包含该项集的记录所占的比例。从上面例子中可以得到:{豆奶}的支持度是4/5,{豆奶,尿布}的支持度是3/5。支持度是针对项集来说的,只保留满足最小支持度的项集。
可信度(confidence):是针对一条诸如{尿布}-->{葡萄酒}的关联规则来定义的。这条规则的可信度被定义为:
“支持度({尿布,葡萄酒})/支持度({尿布})”
支持度和可信度是用来量化关联分析是否成功的方法。假设想找到支持度大于0.8的所有项集,应该如何去做?一个办法就是生产一个物品所有可能组合的清单,然后对每一种组合统计它出现的频繁程度。
但是当物品成千上万时,上述做法非常非常慢。真对这个问题,我们引进Apriori原理。
2、Apriori原理:
原理作用:这个原理是针对计算支持度时候,要遍历所有的数据,这样会造成计算量过大,非常耗时,使用Apriori原理,可以减少遍历次数,减少时间。
原理内容:Apriori原理是说如果某个项集是频繁的,那么它的所有子集也是频繁的。
也就是说:如果{0,1}是频繁的,那么{0},{1}也一定是频繁的,反过来也就是说:如果一个项集是非频繁集,那么它的所有超集也是非频繁的。(这个很有用)
例如:我么如果知道{2,3}是非频繁的,那么{0,1,2,3}{1,2,3}{0,2,3}也是非频繁的。也就是说一旦计算出{2,3}的支持度,知道他是非频繁的之后,就不需要在计算{0,1,2,3}{1,2,3}{0,2,3}的支持度了。
3、使用Apriori算法来发现频繁集
关联分析包括两项:发现频繁项集和发现关联规则。我们需要先找到频繁项集,然后才能获得关联规则,所以我们先说怎么样发现频繁项集。
Apriori算法:该算法的的输入参数分别是最小支持度和数据集。该算法首先会生成所有单个物品的项集列表。接着扫描交易记录查看哪些项集满足最小支持度要求,那些不满足支持度的集合会被去掉。然后,对剩下来的集合进行组合以生成包含两个元素的项集。接下来,在重新扫描交易记录,去掉不满足最小支持度的项集。该过程重复进行直到所有项集都被去掉。
下面我们需要创建一些辅助函数:
<span style="color:#ff0000;">#加载数据</span>
def loadDataSet():
return [[1,3,4],[2,3,5],[1,2,3,5],[2,5]]
<span style="color:#ff0000;">#把所有的数据的单项放入C1</span>
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)
<span style="color:#ff0000;">#将大于minsupport的返回</span>
def scanD(D, Ck, minSupport):
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
我们可以测试一下上面函数的返回值,通过加载 loadDataSet中的数据
<span style="font-size:18px;">>>> dataSet = apriori.loadDataSet()
>>> dataSet
[[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]
>>> C1 = apriori.createC1(dataSet)
>>> C1
[frozenset([1]), frozenset([2]), frozenset([3]), frozenset([4]), frozenset([5])]
>>> D = map(set,dataSet)
>>> D
[set([1, 3, 4]), set([2, 3, 5]), set([1, 2, 3, 5]), set([2, 5])]
>>> L1,suppData = apriori.scanD(D,C1,0.5)
>>> L1
[frozenset([1]), frozenset([3]), frozenset([2]), frozenset([5])]
>>> suppData
{frozenset([4]): 0.25, frozenset([5]): 0.75, frozenset([2]): 0.75, frozenset([3]): 0.75, frozenset([1]): 0.5}
>>> </span>
创建了这些辅助函数,下面我们要组织完整的Apriori算法
<span style="font-size:18px;">#创建候选项集,函数的作用是:假如我输入[frozenset([1]), frozenset([3]), frozenset([2]), frozenset([5])]</span>
<span style="font-size:18px;"><span style="white-space:pre"> </span> 输出[frozenset([1, 3]), frozenset([1, 2]), frozenset([1, 5]), frozenset([2, 3]), <span style="white-space:pre"> </span>frozenset([3, 5]), frozenset([2, 5])],依次类推
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];
print("L1:",L1,"L2:",L2)
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</span>
我们可以通过代码对以上函数进行测试:
<span style="font-size:18px;">>>> L,suppData=apriori.apriori(dataSet)
('L1:', [], 'L2:', [])
('L1:', [], 'L2:', [])
('L1:', [], 'L2:', [])
('L1:', [], 'L2:', [])
('L1:', [], 'L2:', [])
('L1:', [], 'L2:', [])
('L1:', [1], 'L2:', [2])
('L1:', [1], 'L2:', [2])
('L1:', [1], 'L2:', [3])
('L1:', [2], 'L2:', [2])
('L1:', [2], 'L2:', [3])
('L1:', [2], 'L2:', [3])
>>> L
[[frozenset([1]), frozenset([3]), frozenset([2]), frozenset([5])], [frozenset([1, 3]), frozenset([2, 5]), frozenset([2, 3]), frozenset([3, 5])], [frozenset([2, 3, 5])], []]
>>> apriori.aprioriGen(L[0],2)
('L1:', [], 'L2:', [])
('L1:', [], 'L2:', [])
('L1:', [], 'L2:', [])
('L1:', [], 'L2:', [])
('L1:', [], 'L2:', [])
('L1:', [], 'L2:', [])
[frozenset([1, 3]), frozenset([1, 2]), frozenset([1, 5]), frozenset([2, 3]), frozenset([3, 5]), frozenset([2, 5])]
</span><pre name="code" class="python">>>> suppData
{frozenset([5]): 0.75, frozenset([3]): 0.75, frozenset([2, 3, 5]): 0.5, frozenset([1, 2]): 0.25, frozenset([1, 5]): 0.25, frozenset([3, 5]): 0.5, frozenset([4]): 0.25, frozenset([2, 3]): 0.5, frozenset([2, 5]): 0.75, frozenset([1]): 0.5, frozenset([1, 3]): 0.5, frozenset([2]): 0.75}
>>>
现在我们就利用Apriori算法得到了频繁项集。和他的支持度
4、下面我们要做的就是从频繁项集中挖掘关联规则
要找到关联规则,我们首先从一个频繁项集开始。在上面我们给出了频繁项集的量化定义,即它满足最小支持度的要求,对于关联规则我们也有类似的量化方法,即可信度。
通过观察我们可以知道,如果某条规则并不满足最小的可信度要求,那么该规则的所有子集也不满足最小可信度要求。例如:0,1,2--》3不满足最小可信度要求,那么就知道任何左部为{0,1,2}子集的规则也不会满足最小可信度要求。可以通过这个性质来减少需要测试的规则数目。
关联规则生成函数:
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, br1, minConf=0.7):
prunedH = []
for conseq in H:
conf = supportData[freqSet]/supportData[freqSet-conseq]
if conf >= minConf:
print(freqSet-conseq,'-->',conseq,'conf:',conf)
br1.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, H, supportData, br1, minConf)
结果:
>>> reload(apriori)
<module 'apriori' from 'C:\Python27\apriori.py'>
>>> rules = apriori.generateRules(L,suppData)
(frozenset([1]), '-->', frozenset([3]), 'conf:', 1.0)
(frozenset([5]), '-->', frozenset([2]), 'conf:', 1.0)
(frozenset([2]), '-->', frozenset([5]), 'conf:', 1.0)
('L1:', [], 'L2:', [])
('L1:', [], 'L2:', [])
('L1:', [], 'L2:', [])
>>> rules
[(frozenset([1]), frozenset([3]), 1.0), (frozenset([5]), frozenset([2]), 1.0), (frozenset([2]), frozenset([5]), 1.0)]
一旦降低可信度的阈值,可以获得更多的规则。
总结:虽然我们用Apriori原理来减少在数据库上进行检查的集合的数目。这样可以提高速度。
但是,每次增加频繁项集的大小,Apriori算法都会重新扫描整个数据集,当数据集很大的时候,还是会降低频 繁项集的发现的速度。如果有需要,可以看下FPfrowth算法,该算法对数据库进行两次遍历,能够显著加快发 现频繁项集的速度。