关联分析是一种在大规模数据集中寻找有趣关系的任务。这些关系可以有两种形式:频繁集或者关联规则。频繁项集是经常出现在一起的物品的集合,关联规则暗示两种物品之间可能存在很强的关系。下面的例子说明两种概念。
交易号码 | 商品 |
---|---|
0 | 豆奶,莴苣 |
1 | 莴苣,尿布,葡萄酒,甜菜 |
2 | 豆奶,尿布,葡萄酒,橙汁 |
3 | 莴苣,豆奶,尿布,葡萄酒 |
4 | 莴苣,豆奶,尿布,葡萄酒 |
上述表格中,集合{葡萄酒,尿布,豆奶}就是一个频繁项集的例子。
一个项集的支持度(support)被定义为数据集中包含该项集的记录所占的比例,从上述表格中可以看出,{莴苣}的支持度为4/5,{豆奶,莴苣}的支持度为3/5。支持度是针对项集来说的,因此可以定义一个最小支持度,而只保留满足最小支持度的项集。
可信度或置信度(confidence)是针对一条诸如{尿布}→{葡萄酒}的关联规则来定义的。这条规则的可信度被“支持度({尿布,葡萄酒})/支持度({尿布})”。从表上可以看到,由于{尿布,葡萄酒}的支持度为3/5,尿布的支持度为4/5,所以“尿布→葡萄酒”的可信度为3/4=0.75。
Apriori原理:如果某个项集是频繁的,那么它的所有子集也是频繁的,也就是说,如果一个项集是非频繁集,那么它的所有超集也是非频繁的,不需要算非频繁的支持度。
生成候选项集
伪代码:
对数据集中的每条交易记录tran
对每个候选项集can :
检查一下can是否是tran的子集:
如果是,则增加can的计数值
对每个候选项集:
如果其支持度不低于最小值,则保留该项集
返回所有频繁项集列表
Apriori算法中的辅助函数
def loadDataSet():
return [[1,3,4],[2,3,5],[1,2,3,5],[2,5]] #创建一个用于测试的简单数据集
#创建C1是大小为1的所有候选项集的集合
def creatC1(dataSet):
C1 = [] #创建一个空列表,用来存储所有不重复的项值
for transaction in dataSet: #遍历数据集中所有交易记录
for item in transaction: #对每一条记录,遍历记录中的每一个项
if not [item] in C1:
C1.append([item]) #如果某个物品没有在C1中出现,添加到只包含该物品项的一个列表中
C1.sort()
return list(map(frozenset,C1)) #对C1中每个项构建一个不变集合
#扫描数据集来判断这些只有一个元素的项集是否满足最小支持度的要求
def scanD(D, Ck ,minSupport):
ssCnt = {} #创建一个空字典
for tid in D: #遍历数据集中的所有交易记录
for can in Ck: #遍历C1中的所有候选集
if can.issubset(tid):
if not can in ssCnt:
ssCnt[can] = 1
else: ssCnt[can] += 1 #如果C1中的集合是记录的一部分,增加字典中对应的计数值
numItem = float(len(D))
retList = [] #创建一个空列表,用于存储满足最小支持度要求的集合
supportData = {}
for key in ssCnt:
support = ssCnt[key]/numItem #计算每个元素的支持度
if support > minSupport:
retList .insert(0,key) #满足最小支持度,添加到retList集合的首部
supportData [key] = support
return retList,supportData
测试上述函数:
if __name__ == '__main__':
dataSet = loadDataSet()
print(dataSet)
C1 = creatC1(dataSet)
print(C1)
D = list(map(set,dataSet))
print(D)
L1,suppData0 = scanD(D,C1,0.5)
print(L1)
运行结果:
[[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]
[frozenset({1}), frozenset({2}), frozenset({3}), frozenset({4}), frozenset({5})]
[{1, 3, 4}, {2, 3, 5}, {1, 2, 3, 5}, {2, 5}]
[frozenset({3}), frozenset({2}), frozenset({5})]
完整的Apriori算法
伪代码
当集合中项的个数大于0时
构建一个k个项组成的候选集的列表
检查数据以确认每个项集都是频繁的
保留频繁项集并构建k+1项组成的候选集的列表
在之前的代码中加入以下代码
def apriorGen(Lk,k):
retlist = []
lenLk = len(Lk) #计算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] #比较Lk中的每一个元素与其他元素
L1.sort();L2.sort()
if L1 == L2:
retlist.append(Lk[i] | Lk[j]) #如果两个集合的前面k-2个元素都相等,那么就将这两个集合合成一个大小为k的集合
return retlist
def apriori(dataSet, minSupport =0.3):
C1 = creatC1(dataSet)
D = list(map(set,dataSet))
L1,suppData = scanD(D,C1,minSupport)
L = [L1]
k=2
while (len(L[k-2])>0):
Ck = apriorGen(L[k-2],k)
Lk,supK = scanD(D,Ck,minSupport)
suppData.update(supK)
L.append(Lk)
k +=1
return L,suppData
测试代码:
L,suppData = apriori(dataSet)
运行结果:
[[frozenset({1}), frozenset({3}), frozenset({2}), frozenset({5})], [frozenset({3, 5}), frozenset({1, 3}), frozenset({2, 5}), frozenset({2, 3})], [frozenset({2, 3, 5})], []]
从频繁项集中挖掘关联规则,某个元素或者某个元素集合可能会推导出另一个元素,从杂货店的例子中可以得到,如果有一个频繁项集{豆奶,莴苣},那么就可能有一条关联规则“豆奶→莴苣”。这意味着如果有人购买了豆奶,那么在统计上他会购买莴苣的概率较大。但是,这一条反过来并不总是成立。
每个频繁项集产生许多的关联规则,如果能减少规则的数目来确保问题的可解性,那么计算起来就会好很多。可以通过观察得到,如果某条规则并不满足最小可信度要求,那么该规则的所有子集也不会满足最小可信度要求。
关联规则生成函数(在之前的代码中加入此函数)
def generaeRules(L,supportData ,minconfig = 0.7): #三个参数分别是频繁项集列表、包含那些频繁项集支持数据的字典、最小可信度阈值
bigRuleList = []
for i in range(1,len(L)):
for freqSet in L[i]:
H1 = [frozenset([item]) for item in freqSet] #遍历L中的每一个频繁项集并对每个频繁项集创建只包含当元素的集合列表H1
if (i >1):
rulesFromConseq(freqSet,H1,supportData,bigRuleList,minconfig)
else:
calcConf(freqSet,H1,supportData,bigRuleList,minconfig)
return bigRuleList
def calcConf(freqSet,H,supportData,brl,minConf= 0.7):
prunedH = []
for conseq in H:
conf = supportData[freqSet]/supportData[freqSet-conseq] #遍历H中所有项集并计算他们的可信度值
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,brl,minConf = 0.7):
m = len(H[0]) #先计算H中的频繁集大小m
if(len(freqSet)>(m+1)): #查看频繁项集是否达到可以移除大小为m的子集,如果可以,将其移除
Hmp1 = apriorGen(H,m+1)
Hmp1 = calcConf(freqSet,Hmp1,supportData,brl,minConf)
if(len(Hmp1)>1):
rulesFromConseq(freqSet,Hmp1,supportData,brl,minConf)
测试代码
rules = generaeRules(L,suppData,0.7)
print(rules)
运行结果
frozenset({1}) ---> frozenset({3}) conf: 1.0
frozenset({5}) ---> frozenset({2}) conf: 1.0
frozenset({2}) ---> frozenset({5}) conf: 1.0
[(frozenset({1}), frozenset({3}), 1.0), (frozenset({5}), frozenset({2}), 1.0), (frozenset({2}), frozenset({5}), 1.0)]