Apriori算法——深度剖析及源码实现

Apriori算法是一种经典的关联数据挖掘算法,由Agrawal和R. Srikant于1994年提出。文章介绍了Apriori的基本概念,包括项集、支持度和置信度,以及Apriori定理。通过Apriori算法流程的深度剖析,展示了如何通过连接步和剪枝步减少计算量。此外,文章还提到了Apriori算法的局限性,并对比了FP-growth算法的高效性。最后,提供了Java版的Apriori算法源码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引言

在一家超市中,人们发现了一个特别有趣的现象:尿布与啤酒这两种风马牛不相及的商品居然摆在一起。但这一奇怪的举措却使尿布和啤酒的稍量大幅增加了。这可不是一个笑话,而是一直被商家所津津乐道的发生在美国沃尔玛连锁超市的真实案例。原来,美国的妇女通常在家照顾孩子,所以她们经常会嘱咐丈夫在下班回家的路上为孩子买尿布,而丈夫在买尿布的同时又会顺手购买自己爱喝的啤酒。这个发现为商家带来了大量的利润,但是如何从浩如烟海却又杂乱无章的数据中,发现啤酒和尿布销售之间的联系?

这一问题就是在我之前的文章《浅谈数据挖掘与机器学习》中介绍的关联问题。本文给大家分享的是关联数据挖掘中的经典算法Apriori的深度剖析,并在文末附上Java源码实现。除了Apriori算法外,还有例如FP-growth、E-clat等关联数据挖掘算法。若有机会,我将在后面为大家分享。(PS:在第一节全国高校云计算应用创新大赛中有一个题目即是进行频繁项集的并行化挖掘。博主当时这一题的得分相当高,并在初赛中获得了全国第二名的成绩。相关在Spark上的并行化算法请参考博主之前的文章《Aprior并行化算法在Spark上的实现》


Apriori算法

Apriori算法由Agrawal和R. Srikant于1994年提出,为布尔关联规则挖掘频繁项集的原创性算法。并在2006年12月被国际权威的学术组织ICDM评为数据挖掘领域的十大经典算法。

基本概念

在介绍具体算法之前,先解释一下几个重要概念。在这里我们借助下图辅助说明,以便大家更好理解。图中的每一行代表一次购买清单。(感谢也爱数据挖掘图片贡献)
这里写图片描述

  1. 项集
    项的集合称为项集,包含k个项的项集称为k-项集。
  2. 支持度
    support(X–>Y) = |X交Y|/N=集合X与Y中的项在一条记录中同时出现的次数/数据记录的次数。例如:support({啤酒}–>{尿布}) = 啤酒和尿布同时出现的次数/数据记录数 = 3/5=60%。
  3. 置信度
    confidence(X–>Y) = |X交Y|/|X| = 集合X与集合Y中的项在一条记录中同时出现的次数/集合X出现的次数,也可理解为包含集合X的项集中集合Y出现的比例 。例如:confidence({啤酒}–>{尿布}) = 啤酒和尿布同时出现的次数/啤酒出现的次数=3/3=100%;confidence({尿布}–>{啤酒}) = 啤酒和尿布同时出现的次数/尿布出现的次数 = 3/4 = 75%。
  4. 频繁项集
    如果一个项集在原始数据中出现的次数或频率满足最小支持度,则称这个项集为频繁项集。

Apriori定理

  1. 如果一个集合是频繁项集,则它的所有子集都是频繁项集
    举例:假设一个集合{A,B}是频繁项集,即A、B同时出现在一条记录的次数大于等于最小支持度min_support,则它的子集{A},{B}出现次数必定大于等于min_support,即它的子集都是频繁项集。
  2. 如果一个集合不是频繁项集,则它的所有超集都不是频繁项集
    举例:假设集合{A}不是频繁项集,即A出现的次数小于min_support,则它的任何超集如{A,B}出现的次数必定小于min_support,因此其超集必定也不是频繁项集。

Apriori算法流程

有一个简单而粗鲁的方法可以找出所需要的规则,那就是穷举项集的所有组合,并测试每个组合是否满足条件,一个元素个数为n的项集的组合个数为2^n-1(除去空集),所需要的时间复杂度明显为O(2^N)。试想,对于一个拥有10000件商品的超市,用指数时间复杂度的算法不能在可接受的时间内解决问题。怎样减少计算量,快速挖掘出满足条件的关联规则是关联挖掘需要解决的主要问题。

我们发现对于{啤酒–>尿布},{尿布–>啤酒}这两个规则的支持度实际上只需要计算{尿布,啤酒}的支持度,即它们交集的支持度。同时,我们也观察到,根据上述Apriori定理,当一个项集的子集不是频繁项集的时候,我们可以断定这个项集也不是频繁项集,终止计算,这样可以节省大量计算资源。于是我们把关联规则挖掘分两步进行:

  1. 连接步
    L(k-1)与其自身进行连接,产生候选项集C(k)。一般对所有项按照项值排序之后,L(k-1)中某个元素与其中另一个元素可以执行连接操作的前提是它们的前k-2个项是相同的,而最后一个元素不同。例如:项集{I1,I2}与{I1,I5}连接之后产生的项集是{I1,I2,I5},而项集{I1,I2}与{I3,I4}不能进行连接操作。
  2. 剪枝步
    候选集C(k)中的元素可以是频繁项集,也可以不是。但所有的频繁k-项集一定包含在C(k)中,所以,C(k)是L(k)的超集。由前面的介绍可知,若暴力计算,其耗费的计算资源是相当巨大的。因此,需要进行剪枝。删除C(k)中不是频繁项集的元素,依据就是前面提到的Apriori定理:如果一个子集是非频繁的,那么它的超集也一定是非频繁的。这在Apriori算法里面很重要。如此,如果一个候选(k-1)-子集不在L(k-1)中,那么该候选k-项集也不可能是频繁的,可以直接从C(k)中删除。

但即使这样,Apriori的缺点仍然十分明显,在每生成一个新的频繁项集的时候,算法都需要扫描一遍数据库。而FP-growth在整个过程中只需要扫描一遍数据库,效率大大提升。

综上所述,整个Apriori算法的计算流程如下:

  1. 扫描数据库,生成候选1-项集和频繁1-项集。
  2. 从2-项集开始循环,由频繁k-1项集生成频繁频繁k项集。
    2.1 对项集中的所有项进行排序
    2.2 连接步
    2.3 剪枝步
    2.4 扫描数据库,计算2.3中过滤后的k-项集的支持度,舍弃掉不满足最小支持度的项集,生成频繁k-项集。
  3. 当前生成的频繁k-项集中只有一个项集时循环结束

下图演示了Apriori算法的过程,注意看由二级频繁项集生成三级候选项集时,没有{牛奶,面包,啤酒},那是因为{面包,啤酒}不是二级频繁项集,这里利用了Apriori定理。最后生成三级频繁项集后,没有更高一级的候选项集,因此整个算法结束,{牛奶,面包,尿布}是最大频繁子集。
这里写图片描述


源码实现(Java版)

/**
 * @name:Apriori algorithm
 * @author:希慕_North
 * @function:Frequent pattern mining
 */

package DM.Algorithm;


import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class Apriori {

    private final static int SUPPORT = 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值