关联分析力图在大规模数据集中寻找关系,这些关系有两种形式:频繁项集、关联规则。
频繁项集:经常出现在一块的物品的集合
关联规则:两种物品之间可能存在的关系
支持度:数据集上包含该项集的记录所占的比例
可信度/置信度:针对物品A-->物品B的关联规则来定义。物品A-->物品B的置信度 = 支持度(A | B)/支持度(A)
Apriori算法原理:如果某个项集是频繁的,则它的所有子集也是频繁的。==》如果某个项集是非频繁的,则它的所有超集也是非频繁的。
寻找频繁项集:
辅助函数:
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)) #frozenset创建不可变集合
def scanD(D, Ck, minSupport): #数据集,候选项集列表,最小支持度,过滤不符合最小支持度的项集
ssCnt = {}
for tid in D:
for can in Ck:
if can.issubset(tid):
if can not 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
完整的Apriori算法:
def aprioriGen(Lk, k): #参数为当前频繁项集列表,需要生成的新的频繁项集的元素个数。因为每次生成的新的项元素个数比当前多1,所以[:k-2]表示当前项除了最后一个元素,即为[:,-1]
retList = []
print(Lk)
print('k:',k)
lenLk = len(Lk)
for i in range(lenLk):
for j in range(i+1, lenLk):
print('i:',Lk[i])
print('j:',Lk[j])
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]) #集合操作符,求并集
print(retList)
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
测试:
if __name__=='__main__':
dataSet=loadDataSet()
L,suppData=apriori(dataSet,0.5)
print(L)
print(suppData)
输出:
[[frozenset({1}), frozenset({3}), frozenset({2}), frozenset({5})], [frozenset({3, 5}), frozenset({1, 3}), frozenset({2, 5}), frozenset({2, 3})], [frozenset({2, 3, 5})], []]
{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}
从频繁项集中挖掘关联规则:
如果某条规则不满足最小可信度要求,则该规则的所有子集也不满足最小可信度要求。
分级法:首先从一个频繁项集开始,接着创建一个规则列表,其中规则右部只包含一个元素,然后对这些元素进行测试。接下来合并所有剩余规则来创建一个新的规则列表,其中规则右部包含两个元素。
关联规则生成函数:
def generateRules(L, supportData, minConf=0.7): #频繁项集列表,支持度列表,最小可信度
bigRuleList = [] #包含可信度的规则列表
for i in range(1, len(L)): #因为无法对只有单个元素的频繁项集构建规则,智能从L[1]开始,L[1]是包含两个元素的频繁项集列表
for freqSet in L[i]: #遍历当前L[i]的所有频繁项集
H1 = [frozenset([item]) for item in freqSet] #从频繁项集中得到元素列表
if (i > 1): #i>1时,频繁项集至少有三个元素
rulesFromConseq(freqSet, H1, supportData, bigRuleList, minConf) #对H1进一步合并成含有两个元素的频繁项集列表
else: #频繁项集有两个元素,则计算可信度
calcConf(freqSet, H1, supportData, bigRuleList, minConf)
return bigRuleList
def calcConf(freqSet, H, supportData, brl, minConf=0.7): #计算规则的可信度并且找到满足最小可信度要求的规则;函数内部没有重置brl,所以brl可以保存满足条件的规则。
prunedH = [] #保存满足最小可信度的规则列表
for conseq in H: #
conf = supportData[freqSet]/supportData[freqSet-conseq] #计算可信度
print(freqSet - conseq, '====>', conseq, '可信度:', conf)
if conf >= minConf:
# print(freqSet-conseq,'====>',conseq,'可信度:',conf)
brl.append((freqSet-conseq, conseq, conf)) #对列表brl补充,brl即为前面通过检查的bigRuleList
prunedH.append(conseq)
print('prunedH:',prunedH)
return prunedH
def rulesFromConseq(freqSet, H, supportData, brl, minConf=0.7): #频繁项集,可以出现在规则右面的元素列表H
m=len(H[0]) #第一次调用时m=1
if len(freqSet) > m + 1: #如果频繁项集的元素个数大于H的元素长度加1,例如freqSet=[2,3,5],H=[2,3,5]时,3>1+1,此时把H的元素融合成[[2,3],[2,5],[3,5]]
Hmp1 = aprioriGen(H, m+1) #Hmpl=[[2,3],[2,5],[3,5]]
print('freqSet:',freqSet)
print('Hmp1:',Hmp1)
Hmp1 = calcConf(freqSet, Hmp1, supportData, brl, minConf)
if len(Hmp1) > 1: #如果不止一条规则满足要求,则使用Hmp1迭代调用rulesFromConseq来判断是否可以进一步组合这些规则
rulesFromConseq(freqSet, Hmp1, supportData, brl, minConf)
测试:
if __name__=='__main__':
dataSet=loadDataSet()
L,suppData=apriori(dataSet,0.5)
rules=generateRules(L,suppData,0.7)
print(rules)
输出:
frozenset({5}) ====> frozenset({3}) 可信度: 0.6666666666666666 frozenset({3}) ====> frozenset({5}) 可信度: 0.6666666666666666 prunedH: [] frozenset({3}) ====> frozenset({1}) 可信度: 0.6666666666666666 frozenset({1}) ====> frozenset({3}) 可信度: 1.0 prunedH: [frozenset({3})] frozenset({5}) ====> frozenset({2}) 可信度: 1.0 frozenset({2}) ====> frozenset({5}) 可信度: 1.0 prunedH: [frozenset({2}), frozenset({5})] frozenset({3}) ====> frozenset({2}) 可信度: 0.6666666666666666 frozenset({2}) ====> frozenset({3}) 可信度: 0.6666666666666666 prunedH: [] freqSet: frozenset({2, 3, 5}) Hmp1: [frozenset({2, 3}), frozenset({2, 5}), frozenset({3, 5})] frozenset({5}) ====> frozenset({2, 3}) 可信度: 0.6666666666666666 frozenset({3}) ====> frozenset({2, 5}) 可信度: 0.6666666666666666 frozenset({2}) ====> frozenset({3, 5}) 可信度: 0.6666666666666666 prunedH: [] [(frozenset({1}), frozenset({3}), 1.0), (frozenset({5}), frozenset({2}), 1.0), (frozenset({2}), frozenset({5}), 1.0)]
上述代码有逻辑错误:
在函数rulesFromConseq中,如果H有三个元素,例如原文中的H=[2,3,5],freqSet=[2,3,5],程序会将H融合成[[2,3],[2,5],[3,5]],不会校验[2,3]==>[5]、[2,5]==>[3]、[3,5]==>[2]的可信度,所以结果少了这些规则。
修改:
def generateRules(L, supportData, minConf=0.7): #频繁项集列表,支持度列表,最小可信度
bigRuleList = [] #包含可信度的规则列表
for i in range(1, len(L)): #因为无法对只有单个元素的频繁项集构建规则,智能从L[1]开始,L[1]是包含两个元素的频繁项集列表
for freqSet in L[i]: #遍历当前L[i]的所有频繁项集
H1 = [frozenset([item]) for item in freqSet] #从频繁项集中得到元素列表
if (i > 1): #i>1时,频繁项集至少有三个元素
H1 = calcConf(freqSet, H1, supportData, bigRuleList, minConf) #计算可信度,过滤可信度低的候选规则
rulesFromConseq(freqSet, H1, supportData, bigRuleList, minConf) #对H1进一步合并成含有两个元素的频繁项集列表
else: #频繁项集有两个元素,则计算可信度
calcConf(freqSet, H1, supportData, bigRuleList, minConf)
return bigRuleList
测试输出:
[(frozenset({1}), frozenset({3}), 1.0), (frozenset({5}), frozenset({2}), 1.0), (frozenset({2}), frozenset({5}), 1.0), (frozenset({3, 5}), frozenset({2}), 1.0), (frozenset({2, 3}), frozenset({5}), 1.0)]
if…else…多余,修改为下:
def generateRules(L, supportData, minConf=0.7): #频繁项集列表,支持度列表,最小可信度
bigRuleList = [] #包含可信度的规则列表
for i in range(1, len(L)): #因为无法对只有单个元素的频繁项集构建规则,只能从L[1]开始,L[1]是包含两个元素的频繁项集列表
for freqSet in L[i]: #遍历当前L[i]的所有频繁项集
H1 = [frozenset([item]) for item in freqSet] #从频繁项集中得到元素列表
rulesFromConseq(freqSet, H1, supportData, bigRuleList, minConf) #对H1进一步合并成含有两个元素的频繁项集列表
return bigRuleList
def rulesFromConseq(freqSet, H, supportData, brl, minConf=0.7): #频繁项集,可以出现在规则右面的元素列表H
m=len(H[0]) #第一次调用时m=1
if len(freqSet) > m: #如果频繁项集的元素个数大于H的元素长度,例如freqSet=[2,3,5],H=[2,3,5]时,3>1,此时把H的元素融合成[[2,3],[2,5],[3,5]]
# print('freqSet:',freqSet)
# print('Hmp1:',Hmp1)
Hmp1 = calcConf(freqSet, H, supportData, brl, minConf)
if len(Hmp1) > 1: #如果不止一条规则满足要求,则使用Hmp1迭代调用rulesFromConseq来判断是否可以进一步组合这些规则
Hmp1=aprioriGen(Hmp1,m+1)
rulesFromConseq(freqSet, Hmp1, supportData, brl, minConf) #递归
进一步修改rulesFromConseq里的递归函数如下:
def rulesFromConseq(freqSet, H, supportData, brl, minConf=0.7):
m = len(H[0])
while (len(freqSet) > m): # 判断长度 > m,这时即可求H的可信度
H = calcConf(freqSet, H, supportData, brl, minConf)
if (len(H) > 1): # 判断求完可信度后是否还有可信度大于阈值的项用来生成下一层H
H = aprioriGen(H, m + 1)
m += 1
else: # 不能继续生成下一层候选关联规则,提前退出循环
break