前言算法
想必你们都听过数据挖掘领域那个经典的故事 - "啤酒与尿布" 的故事。网络
那么,具体是怎么从海量销售信息中挖掘出啤酒和尿布之间的关系呢?app
这就是关联分析所要完成的任务了。框架
本文将讲解关联分析领域中最为经典的Apriori算法,并给出具体的代码实现。函数
关联分析领域的一些概念学习
1. 频繁项集: 数据集中常常出如今一块儿的物品的集合。例如 "啤酒和尿布"测试
2. 关联规则: 指两个物品集之间可能存在很强的关系。例如 "{啤酒} -> {尿布}" 就是一条关联规则。网站
3. 支持度: 数据集中,出现了某个物品集的数据项占总数据项的比重。spa
4. 可信度: 这个概念是针对某条关联规则而定的。它是指两个物品集的支持度和其中某个物品集的支持度之比,如 "支持度{啤酒,尿布} / 支持度{尿布}"。code
所以,用这些属于来解释啤酒与尿布的故事,那就是:{啤酒,尿布}是一个频繁项集;"{啤酒} -> {尿布}" 就是一条关联规则;顾客买尿布的同时买啤酒的可能性为 "支持度{啤酒,尿布} / 支持度{尿布}"。
那么对海量的数据,假如要获得支持度大于0.8的全部频繁项集,该怎么作?
若是用蛮力法一个个统计,是根本不现实的,那样计算量实在太大。
本文将要介绍的Apriori关联分析算法意义就在于可以大幅度减小这种状况的计算量,并从频繁项集中高效检索出关联规则,从而大大减小关联规则学习所须要消耗的计算量。
Apriori算法基本原理
若是{0,1}是频繁项集,那么{0}和{1}也都是频繁项集。
这显然是正确的命题。
其逆否命题 - ”若是{0}和{1}不都是频繁项集,那么{0,1}不是频繁项集" 天然也是正确的。-> 这就是 Apriori 算法的核心思想之一。
这样,一旦发现某个集合不是频繁项集,那么它的全部超集也都不是频繁项集,就不用浪费功夫去对它们进行检索了。
检索出频繁项集以后,接下来检索出全部合乎要求的关联规则。
若是某条规则不知足最小可信度要求,那么该规则的全部子集也不会知足。 -> 这是 Apriori 算法的核心思想的另外一部分。
PS:这里务必思考清楚规则是怎么划分的,什么叫某个规则的子集。
这样,和上一步同理,也能高效的从频繁项集中检索出关联规则了。
具体实现将分为频繁集检索和关联规则学习两个部分进行讲解。
频繁项集检索实现思路与实现代码
一种经典的实现方法是 "分级法":
算法框架是在两个大的步骤 - 筛选/过滤之间不断迭代,直到全部状况分析完毕。
每次筛选的结果都要指定元素个数的状况,所以所谓分级,也就是依次讨论指定元素个数状况的项集。
在筛选以后,就是过滤。
过滤有两层意义,一个是项集必须在数据集中存在。这是第一层过滤;还有一层过滤,是指支持度过滤。只有知足支持度要求的项集才能保存下来。
过滤以后,基于过滤集再进行筛选,每次筛选的元素个数都比上一次筛选的元素个数多一个。
而后继续过滤。如此反复,直到最后一次筛选过滤完成。
伪代码实现:
当集合中项的个数大于0时:
构建一个 k 个项组成的候选项集的列表
检查数据以确认每一个项集都是频繁的
保留频繁项集并构建 k+1 项组成的候选项集列表
其中检查每一个项集是否频繁部分的伪代码:
对数据集中的每条交易记录:
对每一个候选集元素:
检查是否为数据集元素,是才保留。
对每一个数据集
若是支持度达到要求才保留
返回过滤后的频繁项集 - 也即过滤集
Python实现及测试代码:
1 def loadDataSet(): 2 '返回测试数据' 3 4 return [[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]] 5 6 #=================================== 7 # 输入: 8 # dataSet: 数据集 9 # 输出: 10 # map(frozenset, C1): 候选集 11 #=================================== 12 def createC1(dataSet): 13 '建立候选集' 14 15 C1 = [] 16 for transaction in dataSet: 17 for item in transaction: 18 if not [item] in C1: 19 C1.append([item]) 20 21 C1.sort() 22 23 # 返回的集合元素都是frozenset类型 -> 由于之后要用来作键。 24 return map(frozenset, C1) 25 26 #============================================= 27 # 输入: 28 # D: 数据集 (set格式) 29 # Ck: 候选集 30 # minSupport: 最小支持度 31 # 输出: 32 # retList: 过滤集 33 # supportData: 支持集(挖掘关联规则时使用) 34 #============================================= 35 def scanD(D, Ck, minSupport): 36 '由候选集获得过滤集' 37 38 # 统计候选元素出现的次数 39 ssCnt = {} 40 for tid in D: 41 for can in Ck: 42 if can.issubset(tid): 43 if not ssCnt.has_key(can): ssCnt[can]=1 44 else: ssCnt[can] += 1 45 46 # 构建过滤集和支持集 47 numItems = float(len(D)) 48 retList = [] 49 supportData = {} 50 for key in ssCnt: 51 support = ssCnt[key]/numItems 52 if support >= minSupport: 53 retList.insert(0,key) 54 supportData[key] = support 55 56 return retList, supportData 57 58 #=================================== 59 # 输入: 60 # Lk: 过滤集 61 # k: 当前项集元素个数 62 # 输出: 63 # retList: 候选集 64 #=================================== 65 def aprioriGen(Lk, k): 66 '由过滤集获得候选集' 67 68 # 重组过滤集,获得新的候选集。 69 retList = [] 70 lenLk = len(Lk) 71 for i in range(lenLk): 72 for j in range(i+1, lenLk): 73 # 留意下重组技巧 74 L1 = list(Lk[i])[:k-2]; L2 = list(Lk[j])[:k-2] 75 L1.sort(); 76 L2.sort() 77 if L1==L2: 78 retList.append(Lk[i] | Lk[j]) 79 80 return retList 81 82 #============================================= 83 # 输入: 84 # dataSet: 数据集 85 # minSupport: 最小支持度 86 # 输出: 87 # L: 频繁集 88 # supportData: 支持集(挖掘关联规则时使用) 89 #============================================= 90 def apriori(dataSet, minSupport = 0.5): 91 '求频繁项集及其对应支持度' 92 93 C1 = createC1(dataSet) 94 D = map(set, dataSet) 95 L1, supportData = scanD(D, C1, minSupport) 96 L = [L1] 97 k = 2 98 while (len(L[k-2]) > 0): 99 Ck = aprioriGen(L[k-2], k) 100 Lk, supK = scanD(D, Ck, minSupport) 101 supportData.update(supK) 102 L.append(Lk) 103 k += 1 104 105 return L, supportData 106 107 def main(): 108 'Apriori频繁集检索' 109 110 L, s = apriori (loadDataSet()) 111 112 print L 113 print s
测试结果:
关联规则学习实现思路与实现代码
上一部分的工做是从数据集中检索出频繁集;而这一部分是根据频繁集学习关联规则。
上一部分的工做是经过筛选集中的元素个数进行分级;而这一部分是针对规则右部的元素个数进行分级。
另外还要注意,只用检索单个频繁集以内的关联规则就能够了。
实现代码:
1 #=================================== 2 # 输入: 3 # L: 频繁集 4 # supportData: 支持集 5 # minConf: 最小可信度 6 # 输出: 7 # bigRuleList: 规则集 8 #=================================== 9 def generateRules(L, supportData, minConf=0.7): 10 '从某个频繁集中学习关联规则' 11 12 bigRuleList = [] 13 for i in range(1, len(L)): 14 for freqSet in L[i]: 15 H1 = [frozenset([item]) for item in freqSet] 16 if (i > 1): 17 rulesFromConseq(freqSet, H1, supportData, bigRuleList, minConf) 18 else: 19 calcConf(freqSet, H1, supportData, bigRuleList, minConf) 20 return bigRuleList 21 22 #=================================== 23 # 输入: 24 # L: 频繁集 25 # supportData: 支持集 26 # minConf: 最小可信度 27 # 输出: 28 # bigRuleList: 规则集 29 #=================================== 30 def calcConf(freqSet, H, supportData, brl, minConf=0.7): 31 '可信度过滤' 32 33 prunedH = [] 34 for conseq in H: 35 conf = supportData[freqSet]/supportData[freqSet-conseq] 36 if conf >= minConf: 37 brl.append((freqSet-conseq, conseq, conf)) 38 prunedH.append(conseq) 39 40 return prunedH 41 42 def rulesFromConseq(freqSet, H, supportData, brl, minConf=0.7): 43 '从某个频繁项集中学习关联规则' 44 45 # 本次学习的规则右部中的元素个数 46 m = len(H[0]) 47 if (len(freqSet) > (m + 1)): 48 # 重组规则右部 49 Hmp1 = aprioriGen(H, m+1) 50 # 规则学习 51 Hmp1 = calcConf(freqSet, Hmp1, supportData, brl, minConf) 52 if (len(Hmp1) > 1): 53 # 递归学习函数 54 rulesFromConseq(freqSet, Hmp1, supportData, brl, minConf) 55 56 def main(): 57 '关联规则学习' 58 59 L, s = apriori (loadDataSet()) 60 61 rules = generateRules(L, s) 62 print rules
测试结果:
测试数据为: [[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]
结果的意思也就是说:1->3,5->2,2->5 的几率为 1。
显然这是和预计相吻合的。
小结
1. Apriori关联算法在网络购物网站中用的很是多,能够基于此算法搭建商品推荐系统。
2. 但Apriori算法也有一个缺点,那就是频繁集的检索速度仍是不够快,由于每级都要从新检索一遍候选集(虽然候选集愈来愈小)。
3. 针对 2 中的问题,下篇文章将介绍一个更为强大的发现频繁集的算法 - FP-growth。(但它不能用来发现关联规则)