关联性在生活中经常被用到,例如超市的商品摆放,牙膏和牙刷放一起,针和线放一块,根据顾客的喜好和习惯尽可能的方便顾客;再例如大型游乐设施附近和儿童玩具、冰激凌、饮料在一起;再例如之前算法岭回归,就是解决复共线性的问题,复共线性就是有关联的特征项。Apriori算法就是用来解决这个问题的。
Apriori算法是一种挖掘关联规则的频繁项集算法,其核心思想是通过候选集生成和情节的向下封闭检测两个阶段来挖掘频繁项集。而且算法已经被广泛的应用到商业、网络安全等各个领域。
优点:易编码实现
缺点:在大数据上可能比较慢
支持度:一个项集的支持度被定义为数据集中包含该项集的记录所占比例,要两个项集有关联的前提是两个项集出现的比较频繁,支持度可以变相的看做频繁率
置信度或可信度: 拿A-B的关联置信度来说,关联规则的可信度就是,支持度(a,b)/支持度(a)。
支持度和置信度是用来量化关联分析是否成功的关键
例如购物分析:牛奶 ⇒ 面包
例子:[支持度:30%,置信度:40%]
支持度30%:意味着30%顾客同时购买牛奶和面包
置信度40%:意味着购买牛奶的顾客40%也购买面包
生成候选项集:
对数据中的每条交易记录tran
对每个候选项集can:
检查一下can是否是tran的子集:
如果是,则添加can的计数值
对每个候选项集:
如果其支持度不低于最小值,保留该项
返回频繁项列表
Apriori算法:
当集合中项的个数大于0时
构建一个k个项组织成的候选集的列表
检查数据以确认每个项集都是频繁集
保留频繁集并构建k+1项组织成的候选项集列表
剪枝:想想算法比较简单,复杂度却比较高,但是做过剪枝之后就变得没那么花费时间了。每个频繁项集产生许多的关联规则,如果能减少规则数目该多好啊。—->如果某条规则并不满足最小可信度的要求,那么该规则的所有子集也不会满足最小可信度。假设有4个项集,1->2不满足最小可信度,那么(1->2,3)(1->2,3,4)(1,3->2)等等都不符合要求了,继而减少了很多关联规则。
# coding=utf-8
import numpy as np
def loadDataSet():
dataArr = [[1,3,4], [2,3,5], [1,2,3,5], [2,5]]
return dataArr
def creatC1(dataArr):#候选数据项总共有多少单项
C1 = []
for i in dataArr:
for j in i:
if not [j] in C1:
C1.append([j])
C1.sort()
return map(frozenset, C1)#frozenset为冻结集合
'''找出频繁项和各项支持率'''
def scanD(D, CK, minSupport):#数据集, 候选项集,最小支持率
ssCnt = {}
for i in D:
for j in CK:
if j.issubset(i):#j中的元素是否都在i
if not ssCnt.has_key(j):
ssCnt[j] = 1
else:
ssCnt[j] += 1
#print ssCnt#建立一个候选项的数目字典
numItems = float(len(D))
retlist = []
supportData = {}
for key in ssCnt:
support = float(ssCnt[key]/numItems)
if support >= minSupport:
retlist.insert(0, key)#保留下来的候选项
supportData[key] = support#候选项的数目字典的支持率
return retlist, supportData
''' 将Lk中的前k-2项中相同的合并 '''
def aprioriGen(Lk, k):
retlist = []
for i in range(len(Lk)):
for j in range(i+1, len(Lk)):
Lk1 = list(Lk[i])[:k-2]
Lk2 = list(Lk[j])[:k-2]
Lk1.sort()
Lk2.sort()
if Lk1 == Lk2:
retlist.append(Lk[i]|Lk[j])
#print retlist
return retlist
''' 按照符合一定要求的频繁项拆分出所有的可能性并计算各种支持率 '''
def apriori(dataArr, minSupport):
C1 = creatC1(dataArr)
#print C1
D = map(set, dataArr)
L1 , supportData = scanD(D, C1, minSupport)
print "L1:",L1
L = [L1]
print "L:",L
k = 2
while len(L[k-2]) > 0:
print "L[%d]:"%(k-2), L[k-2]
Ck = aprioriGen(L[k-2], k)#合并项
Lk , newsSupportData= scanD(D, Ck, minSupport)#合并项中的频繁项
supportData.update(newsSupportData)#更新各项支持率
L.append(Lk)#添加频繁项
k += 1
return L, supportData
'''主要函数'''
def generateRules(L, supportData, minConf):#总候选数据项,支持度字典,最小可信度
bigRuleList = []
for i in range(1, len(L)):
for j in L[i]:
H1 = [frozenset([item]) for item in j]
if i>1:
#超过2个元素,先分割
rulesFromConseq(j, H1, supportData, bigRuleList, minConf)
else:
#只有一对数据的时候直接计算
calcConf(j, H1, supportData, bigRuleList, minConf)
return bigRuleList
'''计算可信度'''
def calcConf(freqSet, H, supportData, brl, minConf):#要计算的候选项,总候选数据项,关系字典,支持度字典,最小可信度
prunedH = []
for conseq in H:
conf = supportData[freqSet]/supportData[freqSet-conseq]
if conf >= minConf:
print freqSet-conseq,"-->",conseq, " ", conf
brl.append([freqSet-conseq, conseq, conf])
prunedH.append(conseq)
return prunedH
'''拆分多于2个元素的数据项'''
def rulesFromConseq(freqSet, H, supportData, brl, minConf):#要计算的候选项,总候选数据项,关系字典,支持度字典,最小可信度
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)
def main():
dataArr = loadDataSet()
L, supportData = apriori(dataArr, 0.5)
rules = generateRules(L, supportData, 0.5)
print rules
if __name__ == "__main__":
main()
其实priori算法并不难,比较好理解,但是代码方面比较繁琐罢了,说一下我对这个算法的理解吧!
拿超市的例子来说一下吧,首先我们要得到所有商品,再得到每件商品的购买率和所有组合的购买率即支持率,然后开始算法计算每个组合的可信度。说着比较简单,如果按照这样做就吃亏了,上面的代码是做了优化的,优化的部分是计算可信度的地方,还是举例子来说吧,比如商品A和商品B没有关联,那么两个关联项只要分别包含A和B就说明没有关联了,机A-B无关联,AC-BD就没关联,ACD-B就没关联。仔细想想这样优化还是挺有道理的。