数据挖掘十大经典算法之Apriori算法

数据挖掘十大经典算法之Apriori算法

概述

Apriori是关联规则模型中的经典算法,是由R.Agrawal和R.Srikant于1994年提出的为布尔关联规则挖掘频繁项集的原创性算法。Apriori使用一种称作逐层搜索的迭代方法,k项集用于搜索k+1项集。该算法主要用于在交易数据、关联数据或其他信息载体中,查找存在于项目集合或对象集合之间的频繁模式、关联性或因果结构。

重要概念

  • N项集
    表示由N个元素组成的元素集合(N为大于0的整数)
  • N项集的支持度
    表示在所有样本中,能够匹配特定N项集要求的样本数量,它也可以表示成百分比的形式。
  • 频繁N项集(L[n])
    表示满足指定的最小支持度的所有N项集
  • 候选N项集(C[n])
    它由频繁N-1项集L[n-1]生成,是计算频繁N项集的基础。C[n]必须保证包括所有的频繁N项集。

重要性质

频繁项集的所有非空子集也必须是频繁的。即如果项集A不满足最小支持度阈值MinSupport,则A不是频繁的,如果将项集B添加到项集A中,也就是A B也不可能是频繁的。该性质是一种反单调性的性质,也就是说如果一个集合不能通过测试,则它的所有超集也都不能通过相同的测试。

基本实现原理

  1. 首先寻找L[1](即频繁1项集);
  2. 在L[k]的基础上生成候选频繁k+1项集C[k+1];
  3. 用事务数据库D中的事务对所有C[k+1]进行支持度测试以寻找频繁项集L[k+1],计算每个候选频繁项集的支持度,如果大于最小支持度,则加入到L[k+1];
  4. 如果L[k+1]为空集,则结束,L[1] ∪ L[2] ∪ …即为结果;否则转2继续。

伪代码形式

Apriori算法
输入:数据集D;最小支持度minsupport
输出:频繁项集L
L[1] = {频繁1项集}//初始化频繁项集L[1](也称单品项集)
for(k=2;L[k-1]不为空;k++){
    C[k] = candidate_gen(L[k-1]);//根据L[k-1]产生新的候选频繁项集C[k]
    For all transactions t ϵ D; {//对所有的交易记录做循环
        C = subset(C[k],t);//找出当前交易记录t和候选频繁项集C[k]的交集
        For all candidates c ϵC do//对存在的候选频繁项集的交集进行支持度计数
        c.count ++;
    }
    L[k] = {c ϵ C[k] | c.count >= minsup};//保留大于最小支持度的频繁项集到L[k];
}
Answer = L=∪L[k]=L[1] ∪ L[2] ∪ ... ∪ L[k]

candidate_gen(L[k-1])
输入:(k-1)-项集
输出:k-候选集C[k]
for all itemset p ∈ L[k-1]
    for all itemset q ∈ L[k-1]
        if(p.item1 = q.item1,p.item2=q.item2,...,p.item(k-2)=q.item(k-2),p.item(k-1)<q.item(k-1))
            c=p∞q;
        if(has_infrequent_subset(c,L[k-1]) delete c;
        else add c to C[k];
    End for
End for
return C[k];

has_infrequent_subset(c,L[k-1])
输入:一个k-项集c,(k-1)-项集L[k-1]
输出:c是否从候选集中删除
for all (k-1)-subsets of c
    if S ∉ L[k-1]
        return true;
return false;

Apriori算法的缺点

  • 多次扫描事务数据库,需要很大的I/O负载。

    对每次k循环,候选集C[k]中的每个元素都必须通过扫描数据库一次来验证其是否加入L[k]。加入一个频繁大项集包含10个项,那么就至少需要扫描事务数据库10次。

  • 可能产生庞大的候选集。

    由L[k-1]产生k-候选集C[k]是指数增长的,例如 104 个频繁1-项集就有可能产生将近 107 个元素的2-候选集。如此庞大的候选集对时间和主存空间都是一种挑战。

Apriori算法的改进

  1. 基于散列(Hash)的方法
    1995年,Park等提出了一种基于散列(Hash)技术产生频繁项集的算法。这种方法把扫描的项目放到不同的Hash桶中,每个频繁项最多只能放在一个特定的桶里,这样可以对每个桶中的频繁项自己进行测试,减少了候选频繁项集产生的代价。

  2. 事务压缩
    事务压缩是指压缩未来迭代扫描的事务数。由于不包含任何频繁k-项集的事务是不可能包含任何频繁(k+1)-项集的,因此这种事务在后续的考虑中可以加上标记或者直接删除,因此产生j-项集(j>k)的数据库扫描不再需要它们。

  3. 基于数据划分(Partition)的方法
    Apriori算法在执行过程中首先生成候选集,然后再进行剪枝。可是生成的候选集并不都是有效的,有些候选集根本不是事务数据的项目集。因此,候选集的产生具有很大的代价。特别是内存空间不够导致数据库与内存之间不断交换数据,会使算法的效率变得很差。
    把数据划分应用到关联规则挖掘中,可以改善关联规则挖掘在大容量数据集中的适应性。其根本思想是把大容量数据库从逻辑上分成几个互不相交的块,每块应用挖掘算法(如Apriori算法)生成局部的频繁项集,然后把这些局部的频繁项集作为候选的全局频繁项目集,通过测试它们的支持度来得到最终的全局频繁项目集。

  4. 基于采样(Sampling)的方法
    基于采样的方法是Toivonen于1996年提出的,这个算法的基本思想是:选取给定数据D的随机样本S,然后在S而不是D中搜索频繁项集。用这种方法是牺牲了一些精度换取有效性。样本S的大小选取使得可以在内存搜索S中的频繁项集。这样只需要扫描一次S中的事务。由于算法只是搜索S中的数据,因此可能会丢失一些全局频繁项集。为了减少这样的情况,使用比最小支持度低的支持度阈值来找出局部于S的频繁项集(记做 LS )。然后,数据库的其余部分用于计算 LS 中每个项集的实际频率。使用一种机制来确定是否所有的频繁项集都包含在 LS 中。如果 LS 实际包含了D中的所有频繁项集,则只需扫描一次D。否则,可以做第二次扫描来找出第一次扫描时遗漏的频繁项集。

实例分析

下面使用宠物商店为例来介绍Apriori算法的实际应用。
假设目前宠物商店的交易系统中只有下表1中的几张顾客购物清单。
表1 顾客购物清单

交易订单号顾客购物清单
001鸟、鸟笼、鸟食、猫粮、猫砂
002鱼食、鸟、鸟笼
003猫粮、狗粮、宠物玩具
004鸟、鸟笼、鸟食
005猫粮、猫砂、宠物玩具

我们的目标是利用过去顾客已购买商品的历史信息作为参考,推荐他们可能感兴趣的其他商品。因此在这里,宠物商店的管理者经过仔细考虑和讨论,确定只要一个商品组合的购买比例占到订单交易笔数的40%(即支持度),即可认为该商品组合中的商品集合是具有较强的购买关联性。因此,如果顾客在购物篮中加入了组合其中的一种商品,则系统就会立刻将组合中的其他商品推荐给顾客。

下面模拟Apriori算法完成这项工作:
(1)计算单项商品的频繁项集L[1]
首先要找到单项商品的候选商品频繁项集C[1],然后过滤掉其中支持度小于40%的商品频繁项集从而得到L[1],如表2所示。
表2 计算频繁项集L[1]

商品频繁项集C[1]存在的交易订单号支持度是够保留该商品
001,002,0043/5 x 100% > 40%
鸟笼001,002,0043/5 x 100% > 40%
鸟食001,0042/5 x 100% = 40%
猫粮001,003,0053/5 x 100% > 40%
猫砂001,0052/5 x 100% = 40%
鱼食0021/5 x 100% < 40%
狗粮0031/5 x 100% < 40%
宠物玩具003,0052/5 x 100% = 40%

得到频繁项集L[1] = {{鸟},{鸟笼},{鸟食},{猫粮},{猫砂},{宠物玩具}}。

(2)根据频繁项集L[1]生成候选频繁项集C[2]
顾名思义。C[2]即为商品数量为2的候选频繁项集,它是由单品频繁项集L[1]中的频繁项集两两组合而来的,这里直接给出结果。
得到候选频繁项集C[2] = {{鸟,鸟笼},{鸟,鸟食},{鸟,猫粮},{鸟,猫砂},{鸟,宠物玩具},{鸟笼,鸟食},{鸟笼,猫粮},{鸟笼,猫砂},{鸟笼,宠物玩具},{鸟食,猫粮},{鸟食,猫砂},{鸟食,宠物玩具},{猫粮,猫砂},{猫粮,宠物玩具},{猫砂,宠物玩具}}。

(3)计算单项商品频繁项集L[2]
利用候选频繁项集C[2]进行计算,过滤掉支持度小于40%的商品频繁项集得到L[2],如表3所示。
表3 计算频繁项集L[2]

商品频繁项集C[2]存在的交易订单号支持度是否保留该商品
鸟,鸟笼001,002,0043/5 x 100% > 40%
鸟,鸟食001,0042/5 x 100% = 40%
鸟,猫粮0011/5 x 100% < 40%
鸟,猫砂0011/5 x 100% < 40%
鸟,宠物玩具0/5 x 100% < 40%
鸟笼,鸟食001,0042/5 x 100% = 40%
鸟笼,猫粮0011/5 x 100% < 40%
鸟笼,猫砂0011/5 x 100% < 40%
鸟笼,宠物玩具0/5 x 100% < 40%
鸟食,猫粮0011/5 x 100% < 40%
鸟食,猫砂0011/5 x 100% < 40%
鸟食,宠物玩具0/5 x 100% < 40%
猫粮,猫砂001,0052/5 x 100% = 40%
猫粮,宠物玩具003,0052/5 x 100% = 40%
猫砂,宠物玩具0051/5 x 100% < 40%

得到频繁项集L[2] = {{鸟,鸟笼},{鸟,鸟食},{鸟笼,鸟食},{猫粮,猫砂},{猫粮,宠物玩具}}。

(4)根据频繁项集L[2]生成候选频繁项集C[3]
本步和第二步中C[2]的生成规则是一样的,这里强调一点,在Apriori算法中只允许差异为1的不同项集进行组合。例如在本次计算C[3]的候选频繁项集时,{鸟,鸟笼}和{鸟,鸟食}之间是可以组合的,而{鸟,鸟笼}和{猫粮,猫砂}由于这两个频繁项集的差异为2(超过了一个元素),则不能进行组合。
得到候选频繁项集C[3] = {{鸟,鸟笼,鸟食},{猫粮,猫砂,宠物玩具}}。

(5)计算单项商品频繁项集L[3]
表4 计算频繁项集L[3]

商品频繁项集C[3]存在的交易订单号支持度
鸟,鸟笼,鸟食001,0042/5 x 100% = 40%
猫粮,猫砂,宠物玩具0051/5 x 100% < 40%

得到频繁项集L[3] = {{鸟,鸟笼,鸟食}}。

(6)循环终结条件及获得最终的商品频繁项集
由于C4应由L[3]中的频繁项集组合而来,但L[3]中目前已没有可供继续连接的频繁项集,因此循环到此结束。

根据Apriori算法,本例中最终的商品频繁项集为L[1] ∪ L[2] ∪ L[3] ={{鸟},{鸟笼},{鸟食},{猫粮},{猫砂},{宠物玩具},{鸟,鸟笼},{鸟,鸟食},{鸟笼,鸟食},{猫粮,猫砂},{猫粮,宠物玩具},{鸟,鸟笼,鸟食}}。

根据本例中宠物商店的需求,对客户兴趣度有意义的商品关联数据为{鸟,鸟笼},{鸟,鸟食},{鸟笼,鸟食},{猫粮,猫砂},{猫粮,宠物玩具},{鸟,鸟笼,鸟食}。因此,宠物商店可根据以上数据来进行商品推荐。

Python实现Apriori算法

我们用字母形象化地表示上述所有商品,如下:
A:鸟类 B:鸟笼 C:鸟食 D:猫粮 E:猫砂 F:鱼食 G:宠物玩具 H:狗粮

#!coding=utf-8
import copy

#初始化数据集,计算单项的出现频率,存入项字典中
def caculate_item_frequency(data_set):
    freq_set_dict={}
    for t in data_set:
        for i in t:
            if i in freq_set_dict.keys():
                freq_set_dict[i] += 1
            else:
                freq_set_dict[i] = 1
    return freq_set_dict

#根据k-1项集生成候选k项集
def generate_candidate_set(frequency):
    candidate_set=[]
    k=len(frequency[0]) + 1
    for f1 in frequency:
        for f2 in frequency:
            if f1[k-2] < f2[k-2]:
                c = copy.copy(f1)
                c.append(f2[k-2])
                flag = True
                for i in range(0,k-1):
                    s=copy.copy(c)
                    s.pop(i)
                    if s not in frequency:
                        flag=False
                        break
                if flag and c not in candidate_set:
                    candidate_set.append(c)
    return candidate_set

# 比较两列表是否相等
def compare_list(A,B):
    if len(A) > len(B):
        for b in B:
            if b not in A:
                return False
    else:
        for a in A:
            if a not in B:
                return False
    return True

#Apriori算法入口
def apriori(data_set,minsupport):
    candidate_set=[]    #候选频繁项集
    #计算所有支持度不小于minsupport的频繁1项集
    total_items = len(T)
    item_frequency_dict=caculate_item_frequency(T)
    keys = item_frequency_dict.keys();
    keys.sort()
    candidate_set.append(keys)
    frequent_set=[[]]
    for item in candidate_set[0]:
        if item_frequency_dict[item] * 1.0 / total_items >= minsupport:
            frequent_set[0].append([item])

    #计算频繁k项集
    k=1
    while frequent_set[k-1] != []:
         #用k-1频繁项集生成候选频繁k项集
        candidate_set.append(generate_candidate_set(frequent_set[k-1]))
        #这里加入一个空集合放入frequent_set,用意在于如果有频繁k项集,
        #就将所有的频繁k项集存放在这个空集合当中,如果没有频繁k项集则当作遍历结束条件
        frequent_set.append([])
        #遍历候选k项集
        for c in candidate_set[k]:
            count = 0
            #遍历数据集,找出所有支持度不小于minsupport的候选项,将其加入到频繁k项集中
            for t in data_set:
                if compare_list(c,t):
                    count += 1
            if count * 1.0 / total_items >= minsupport:
                frequent_set[k].append(c)
        k += 1
    #打印输出所有频繁项集
    outcome_set=[]
    for f in frequent_set:
        for x in f:
            outcome_set.append(x)
    return outcome_set;

#T=[['A','B','C','D'],['B','C','E'],['A','B','C','E'],['B','D','E'],['A','B','C','D']]
T=[['A','B','C','D','E'],['A','B','F'],['D','G','H'],['A','B','C'],['D','E','G']]
print apriori(T,0.4)
    原文作者:常用算法
    原文地址: https://blog.csdn.net/jlqcloud/article/details/45952923
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞