Apriori算法

Apriori 算法

Apriori 算法是一种发现频繁项集的基本算法。

一、基本概念

1、项与项集(item and itemsets)
设itemset={item_1, item_2, …, item_m}是所有项的集合,其中,item_n(n=1,2,…,m)成为项。项的集合称为项集(itemset),包含k个项的项集称为k项集(k-itemset)。
2、事物与事物集
一个事务T是一个项集,它是itemset的一个子集,每个事务均与一个唯一标识符Tid相联系。不同的事务一起组成了事务集D,它构成了关联规则发现的事务数据库。
3、关联规则(association rule)
关联规则是形如A=>B的蕴涵式,其中A、B均为itemset的子集且均不为空集,而A交B为空。
4、支持度(support)
关联规则的支持度定义如下:

**

support(A => B) = P(A | B)

$P(A\bigcup B)$表示事务包含集合A和B的并(即包含A和B中的每个项)的概率。注意与$P(A or B)$区别,后者表示事务包含A或B的概率。

5、置信度(confidence)
关联规则的置信度定义如下:
confidence(A => B) = P(B | A) 
P(B | A) = support_count(A | B)/supportcount(A)
6、项集的频度(support count)
包含项集的事务数,简称为项集的频度、支持度计数或计数。
7、频繁项集(frequent itemset)
如果项集I的相对支持度满足事先定义好的最小支持度阈值(min_sup)(即I的出现频度大于相应的最小出现频度(支持度计数)阈值),则I是频繁项集。
8、强关联规则
满足最小支持度和最小置信度的关联规则,即待挖掘的关联规则。

二、一个例子

下面通过一个例子解释上面的概念

现在有一个超市的销售记录数据集:

TID 商品列表
T100 牛奶, 面包, 尿布
T200 面包, 啤酒
T300 面包, 黄油
T400 牛奶, 面包, 啤酒
T500 牛奶, 黄油
T600 面包, 黄油
T700 牛奶, 黄油
T800 牛奶, 面包, 黄油, 尿布
T900 牛奶, 面包, 黄油

该表就是一个 事务集,每一条销售记录就是一个 事务,用TID唯一标志。牛奶、面包这些商品就是一个 项(item),{牛奶,面包,尿布,啤酒,黄油}表示所有项的 项集(itemset)。每一个事务都是项集的子集,例如T100表示的就是含有三个项的项集,称之为 3项集(3-itemset)。一个2项集{牛奶,面包}在事务集中出现了4次,该项集的 项集频度(support count) 就是4。我们可以设置一个最小频度的阈值min_sup,保留项集频度大于min_sup的项集,过滤出来的项集就是 频繁项集(frequent itemset)。一般来说买完面包一般都会买牛奶,这个购买模式就可以用 关联规则(association rule) 来表示:

牛奶$\Rightarrow$面包[suppor = 44%;confidence = 67%]

该关联规则的支持度代表同时在一个事务中出现的频次占事务总数的44%,置信度表示购买面包的顾客有60%也购买了牛奶。若该关联规则的支持度和置信度满足最小支持度和最小置信度阈值,那么该关联规则就是我们要找的 强关联规则(big rule)

三、Python实现Apriori算法

1、算法步骤

Apriori算法主要用来找出事务集中的频繁项集。
(1)由于刚开始还没有产生频繁项集,我们首先扫描事务集D生成候选1项集C1。生成C1的过程中同时对每一个1项集出现的频次计数,得到项集的频度。
    for i in data_set:
        for j in i:
            j = frozenset({j})
            if j not in candidate_itemset:
                candidate_itemset[j] = 1
            else:
                candidate_itemset[j] += 1

这里候选项集及其项集频度用python中的字典存储,项集的数据类型是集合(set),项数据类型是字符串(str)。由于字典的key只能是不可更改的数据类型,所以项集存入字典时需要变成frozenset类型。

(2)有了C1就可以通过迭代得到Lk了。
    for i in frozenset(Ck.keys()):
        if Ck[i] < min_sup:
            Ck.pop(i)
    Lk = list(Ck.keys())
    frequent_itemset.update(Ck)
    
    Ck.clear()
    len_Lk = len(Lk)
    for i in range(len_Lk):
        for j in range(1,len_Lk):
            L1 = list(Lk[i])
            L2 = list(Lk[j])
            L1.sort()
            L2.sort()
            if (L1[:-2] == L2[:-2]) & (L1 != L2):
                Cki = set(L1) | set(L2)
                
                if not is_pruning(Cki, Lk):
                    CKi = frozenset(Cki)
                    Ck[CKi] = 0

    for i in Ck:
        item = list(i)
        for j in data_set:
            if set(item) & set(j) == set(item):
                Ck[i] += 1

迭代部分的输入为C(k-2),输出为C(k),当C(k)为空集合时迭代结束,表示已经找到了所有的频繁项集。迭代过程可由下图表示

graph LR
删除-->Lk-1
Lk-1-->链接
链接-->枝剪
枝剪-->Ck
Ck-->计数
计数-->删除

首先删除不满足最小支持度阈值的候选项集,留下的就是频繁项集;将得到的频繁(k-1)项集添加到L(k-1)和频繁项集的合集frequent_itemset中;然后对L(k-1)做链接操作,对k-1项集两两取出,逐对排序、判断是否能链接。链接的条件为:排序后的两个频繁项集的前k-2项要相同;若能链接,就生成k项集,然后做枝剪操作,看此k项集的(k-1)项子集是不是频繁项(在L(k-1)中);若没有被枝剪,就添加到候选k项集的集合Ck中;最后对Ck中的后选项集计数,将Ck再次传入迭代函数。

(3)迭代结束后,所有符合条件的频繁项集就都存入了字典frequent_itemset中。最后通过频繁项集得到强关联规则。
    for i in range(len(frequent_itemset)):
        itemset_1 = set(list(frequent_itemset.keys())[i])
        for j in range(1, len(frequent_itemset)):
            itemset_2 = set(list(frequent_itemset.keys())[j])
            if (not itemset_1 & itemset_2) and (frozenset(itemset_1|itemset_2) in frequent_itemset):
                confidence = frequent_itemset[frozenset(itemset_1|itemset_2)]/frequent_itemset[frozenset(itemset_1)]
                if (confidence > min_conf) and ([itemset_1, itemset_2] not in big_rules_list):
                    big_rules_list.append([itemset_1, itemset_2])
                    confidence_list.append(confidence)

依然是从频繁项集的集合中依次取出两个频繁项集来判断。若取出的两个频繁项集是A和B,能产生相关联规则A=>B的条件是:
(1)A与B不能有交集,即A & B为空。
(2)A与B的并集也是频繁项集,即A | B在频繁项集的集合中。
(3)A与B的置信度大于等于最小置信度阈值。求解A与B的置信度,简单来说就是用A与B的并集的频繁项集频度除以A的频繁项集频度。

2、程序运行结果

Apriori算法

程序源码:https://github.com/huwenhao1127/DataMining/tree/master/Frequent Itemsets Mining