关联规则挖掘(二):Apriori算法
1 算法简介
在数据挖掘领域,Apriori算法是挖掘关联规则的经典算法。Apriori算法采用的是自底向上的方法,从1-频繁集开始,逐步找出高阶频繁集。
它的基本流程是:第一次扫描交易数据库D时,产生1-频繁集。在此基础上经过连接、修剪产生2-频繁集。以此类推,直到无法产生更高阶的频繁集为止。在第k次循环中,也就是产生k-频繁集的时候,首先产生k-候选集,k-候选集中每一个项集都是对两个只有一个项不同的属于k-1频繁集的项集连接产生的,k-候选集经过筛选后产生k-频繁集。
2 理论基础
首先来看一个频繁集的性质。
定理:如果项目集X是频繁集,那么它的非空子集都是频繁集。
根据定理,已知一个k-频繁集的项集X,X的所有k-1阶子集都肯定是频繁集,也就肯定可以找到两个k-1频繁集的项集,它们只有一项不同,且连接后等于X。这证明了通过连接k-1频繁集产生的k-候选集覆盖了k-频繁集。同时,如果k-候选集中的项集Y,包含有某个k-1阶子集不属于k-1频繁集,那么Y就不可能是频繁集,应该从候选集中裁剪掉。Apriori算法就是利用了频繁集的这个性质。
3 算法伪代码
这是Apriori算法的主函数,它的输入是交易数据库D和最小支持度,最终输出频繁集L。函数第一步是扫描数据库产生1-频繁集,这只要统计每个项目出现的次数就可以了。然后依次产生2阶,3阶,……,k阶频繁集,k频繁集为空则算法停止。apriori_gen函数的功能是根据k-1频繁集产生k-候选集。接着扫描交易数据库里的每一笔交易,调用subset函数产生候选集的子集,这个子集里的每一个项集都是此次交易的子集,并对子集里的每一个项集的计数增一。最后统计候选集里所有项集的计数,将未达到最小支持度标准的项集删去,得到新的频繁集。
可以看到每一次循环,都必须遍历交易数据库;而且对于每一个交易,也要遍历候选集来增加计数,当候选集很大时这也是很大的开销。
输入:交易数据库D,最小支持度SUPmin。 输出:频繁集L L1=find_frequent_1_itemset(D);//产生1-频繁集 for(k=2;Lk-1!=⌀;k++){ Ck=apriori_gen(Lk-1);//产生k-候选集 for each transaction t in D{ Ct=subset(Ck,t);//Ct是Ck中被t包含的候选集的集合 for each candidate c in Ct c.count++; } Lk={c∈Ck|c.count>=SUPmin}; } L=⋃Lk;
apriori_gen的功能是根据k-1频繁集产生k-候选集。函数是一个二重循环,当遇到两个项集,它们的前k-2项都相同,只有最后一项不同时,就对它们进行连接操作,产生一个k阶的项集(这么做的理论依据如前所述,任何一个k阶的频繁集一定能找到两个满足此条件的k-1阶子频繁集)。新产生的k阶项集可能包含有不是频繁集的子集,遇到这样的情况应该将此项集从候选集中裁剪掉,避免无谓的开销,这就是has_infrequent_subset函数做的工作。
输入:(k-1)-频繁集Lk-1。 输出:k-候选集Ck。 for each itemset p in Lk-1 for each itemset q in Lk-1 if(p.item1=q.item1&p.item2=q.item2&…&p.itemk-2=q.itemk-2&p.itemk-1 <q.itemk-1){c=p.concat(q. itemk-1);//连接p和q if !has_infequent_subset(c, Lk-1) add c to Ck; } return Ck;has_infrequent_subset函数的功能就是判断一个项集是否包含有不是频繁集的子集。函数很简单,遍历候选项集c的k(实际上只需要遍历k-2个)个k-1阶子集,依次判断是否频繁集。
输入:一个k-候选项集c,(k-1)-频繁集Lk-1。 输出:c是否从候选集删除 for each (k-1)-subsets s of c if s not in Lk-1 return true; return false;4 例
继续使用关联规则挖掘(一):基本概念中的引例来看看Apriori算法的执行过程。输入引例中的交易数据库,设置最小支持度为0.3。
表1 某超市的交易数据库
交易号TID | 顾客购买的商品 | 交易号TID | 顾客购买的商品 |
T1 | bread, cream, milk, tea | T6 | bread, tea |
T2 | bread, cream, milk | T7 | beer, milk, tea |
T3 | cake, milk | T8 | bread, tea |
T4 | milk, tea | T9 | bread, cream, milk, tea |
T5 | bread, cake, milk | T10 | bread, milk, tea |
4.1 产生1-频繁集
扫描交易数据库,统计得各个项目出现的次数,bread是7次,cream是3次,milk是8次,tea是7次,cake是2次,beer是1次。容易知道1-频繁集包括{bread},{cream},{ milk},{ tea}。
4.2 产生2-频繁集
对1-频繁集进行连接,得到候选集为{bread,cream},{bread,milk},{bread,tea},{cream,milk}, {cream,tea},{milk,tea}。扫描交易数据库,得到它们的支持度分别为0.3,0.5,0.5,0.3,0.2,0.5。于是得到2-频繁集是{bread,cream},{bread,milk},{bread,tea},{cream,milk} ,{milk,tea}。
4.3 产生3-频繁集
对2-频繁集进行连接,并删掉其中包含有子集不是频繁集的项集(比如{bread,cream,tea}有子集{cream,tea}不是频繁集,所以删去),得到的候选集是{bread,cream,milk},{bread,milk,tea}。扫描交易数据库,它们的支持度是0.3,0.3。因此3-频繁集为{bread,cream,milk},{bread,milk,tea}。
4.4 产生4-频繁集
对3-频繁集进行连接,候选集为空,因此4-频繁集为空,算法结束。
最终产生了所有的频繁集{bread},{cream},{ milk},{ tea},{bread,cream},{bread,milk},{bread,tea},{cream,milk} ,{milk,tea},{bread,cream,milk},{bread,milk,tea}。
6 优化
Apriori算法的缺陷是需要多次扫描数据库,而且可能产生非常大的候选集,降低了算法的性能。因此有不少关于Apriori算法的优化方法,其中一个是基于数据分割的优化方法。首先把大容量的数据库从逻辑上分为几个互不相交的块,每块都应用Apriori算法产生局部的频繁集,然后测试它们的支持度来得到最终的全局频繁集。这种方法减少了候选集对内存的负担,而且支持并行挖掘。
参考文献:
[1] 韩慧等。《数据仓库与数据挖掘》。清华大学出版社,2009。
[2] 毛国君等。 《数据挖掘原理与算法》。清华大学出版社,2007。
[3] Apriori algorithm.
http://en.wikipedia.org/wiki/Apriori_algorithm, 2011.