Apriori算法与关联分析
1.原理
- 关联分析:在大数据的集合里寻找一些关系。关系有两种形式:频繁项(经常出现在一起的物品的集合)和关联规则(两种物品可能存在的强关系)。判断关系的两个标准是支持度和可信度。支持度定义为数据集中包含该项的记录所占的比例。可信度是关于关联规则的标准,如可信度 = 支 持 度 A , B 支 持 度 A =\frac{支持度{A,B}}{支持度{A}} =支持度A支持度A,B。
- 使用贪心算法列举所有物品的组合的运算量过于庞大,Apriori算法可以更快的找到合适的集合。Apriori算法的原理是:如果某个项集是频繁的,那么它所有的子集也是频繁的。相应地,如果一个项集是非频繁的,那么它所有的超集也是非频繁的。根据以上的理论,可以在合理时间内找出频繁项集。
2.代码
(1)Apriori算法
-
Apriori算法是找到频繁项集的一种方法。首先生成单个物品的列表,根据交易记录判断哪些项集满足最小支持度,对剩下的集合进行组合生成两个元素的项集,再扫描交易记录,等等重复进行直到所有项集被去除。
生成候选项集的伪代码: 对数据集中每条交易记录: 对每个候选项集: 该项集是否是该条交易记录的子集,是则增加计数 对每个候选项集: 如果其支持度不低于最小值,则保留 返回频繁项集列表
# 生成候选集C1(交易记录中存在的所有物品的集合)
def createC1(dataset):
C1 = []
for transacation in dataset:
for item in transacation:
if not [item] in C1:
C1.append([item])
C1.sort()
return list(map(frozenset, C1)) # 得到不可变且无重复的候选集
# 判断是否满足最小支持度,生成合适的候选集L1与其支持度
def scanD(D, Ck, minSupport):
ssCnt = {}
for trans in D:
for can in Ck:
if can.issubset(trans): # 若原候选集的元素是交易记录的子集
if can not in ssCnt: # 计算候选集的元素在交易记录中出现的次数
ssCnt[can] = 1
else:
ssCnt[can] += 1
item_num = float(len(D)) # 交易记录的数量
retList = []
supportData = {}
for key in ssCnt:
support = ssCnt[key] / item_num # 每个键的支持度
if support >= minSupport: # 保留大于最低支持度的键
retList.insert(0, key)
supportData[key] = support
return retList, supportData
if __name__ == "__main__":
dataset = [[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]
C1 = createC1(dataset)
D = list(map(set,dataset))
L1 ,support= scanD(D,C1,0.5)
print(L1)
-
再过滤集合,得到最后的候选集合
伪代码: 当集合中项的个数大于0时: 构建由k项组成的候选项集的列表 检查数据,确定每个项集是频繁的 保留频繁项集,构建由k+1项组成的候选集的列表
# 组合元素
def aprioriGen(Lk, k):
retList = []
lenLk = len(Lk)
for i in range(lenLk):
for j in range(i + 1, lenLk): # 两两比较Lk元素以减少重复次数
"""元素本身长度为1时,直接两两组合,不会重复(list(Lk[i])[:k - 2]得到空集,即为相同集合);
当元素本身长度大于1时,两两组合容易形成重复的组合,所以通过比较前k-2个值,相同的形成集合,这样不会造成重复
此外,前k-2个值不同的元素的组合会直接被排除,因为其组合必定有一个子集非频繁,例如{12}与{23}的组合{123}会被排除,
因为其子集{13}不在原列表内,说明{13}不频繁,从而{123}不频繁"""
L1 = list(Lk[i])[:k - 2]
L2 = list(Lk[j])[:k - 2]
L1.sort()
L2.sort()
if L1 == L2: # 排序后若前 k-2个元素都相等,则合并为一个大小为k的集合
retList.append(Lk[i] | Lk[j]) # “|”:集合合集
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) # 将上一个加入到L的列表元素进行组合,得到不重复的组合的列表
Lk, suppk = scanD(D, Ck, minSupport) # 剔除上面列表的不满足支持度的组合
supportData.update(suppk) # 增加上面的组合列表的支持度
L.append(Lk) #增加组合列表
k += 1
return L, supportData
if __name__ == "__main__":
dataset = [[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]
L, support = apriori(dataset, minSupport=0.7)
print(L)
(2)关联分析
- 对于关联规则,通过可信度进行量化。一条关联规则 P → H P → H P→H的可信度定义为 s u p p o r t ( P ∪ H ) / s u p p o r t ( P ) support(P∪H)/support(P) support(P∪H)/support(P)。同样地,如果某条规则不满足最低可信度要求,那么该规则的所有子集也不满足最低可信度的要求。根据该性质,我们可以减少进行测试的规则数目。
# 生成最终的关联规则
def generateRules(L, supportData, minConf=0.7):
bigRuleList = []
for i in range(1, len(L)): # 遍历频繁项集(关联规则无法从单元素中获得,所以不包含L[0])
for freqSet in L[i]:
H1 = [frozenset([item]) for item in freqSet] # 对每个频繁项集生成只包含单元素集合的列表
print('H1:', H1, 'freqset', freqSet, 'lenf', len(freqSet))
if i > 1: # 超过两个元素则需要两两组合后再得到关联规则
rulesFromConseq(freqSet, H1, supportData, bigRuleList, minConf)
else: # 若上面的列表只包含两个元素,直接得到关联规则
calConf(freqSet, H1, supportData, bigRuleList, minConf)
return bigRuleList
# 返回所有满足最低可信度的元素与关联规则
def calConf(freqSet, H, supportData, br1, minConf=0.7):
pruneH = []
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)) # 添加相应关联规则
pruneH.append(conseq)
return pruneH, br1
# 生成组合元素的关联规则
def rulesFromConseq(freSet, H, supportData, br1, minConf=0.7):
m = len(H[0])
"""m为H中元素的长度,由于需要找到将H中元素进行组合后的关联规则,所以要对H中的元素进行组合,
这样使得组合后元素的长度为m+1。如果freSet的长度<= m+1,则无意义。
如freSet=frozenset({2,3,5}),H=[frozenset({2,3}),frozenset({3,5},frozenset({2,5})]
len(freSet)=3,m=len(H[0])=2,如果再递归,则下一个H为frozenset({2,3,5}),与freSet相同
找它们的关联规则毫无意义,因此需要大于m+1"""
if len(freSet) > m + 1: # freSet的长度需要满足条件
Hmp1 = aprioriGen(H, m + 1) # 返回H的元素两两组合的列表
Hmp1 = calConf(freSet, Hmp1, supportData, br1, minConf) # 得到上述列表元素的关联规则
if len(Hmp1) > 1: # 如果组合后不是一个元素(还可以继续组合),继续组合
rulesFromConseq(freSet, Hmp1, supportData, br1, minConf)
if __name__ == "__main__":
dataset = [[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]
Lk, support = apriori(dataset, minSupport=0.5)
rules = generateRules(Lk, support, minConf=0.5)
print(rules)