Python Apriori算法原理(11)

1,243 阅读4分钟

「这是我参与11月更文挑战的第11天,活动详情查看:2021最后一次更文挑战」 Apriori算法是著名的关联规则挖掘算法。

假如我们在经营一家商品种类并不多的杂货店,我们对哪些经常在一起被购买的商品非常感兴趣。我们只有四种商品:商品0、商品1、商品2、商品3。那么所有可能被一起购买的商品组合都有哪些?这些商品组合可能著有一种商品,比如商品0,也可能包括两种、三种或所有四种商品。但我们不关心某人买了两件商品0以及四件商品2的情况,只关心他购买了一种或多种商品。

下图显示了物品之间所有可能的组合:

  • 图中使用物品的编号0来表示物品0本身。
  • 图中从上往下的第一个集合是ϕ\phi,表示空集或不包含任何物品的集合。
  • 物品集合之间的连线表明两个或者更多集合可以组合形成一个更大的集合。

目标:我们的目标是找到经常在一起购买的物品集合。我们使用集合的支持度来度量其出现的频率。

一个集合的支持度是指有多少比例的交易记录包含该集合。

问题: 如何对一个给定的集合,比如{0,3},来计算其支持度?

  • 我们可以遍历毎条记录并检查该记录包含0和3,如果记录确实同时包含这两项,那么就增加总计数值。在扫描完所有数据之后,使用统计得到的总数除以总的交易记录数,就可以得到支持度。

注意:上述过程和结果只是针对单个集合{0,3}。要获得每种可能集合的支持度就需要多次重复上述过程。我们可以数一下图中的集合数目,会发现即使对于仅有4种物品的集合,也需要遍历数据15次。而随着物品数目的增加遍历次数会急剧增长。对于包含N种物品的数据集共有2N12^{N-1}种项集组合。而且实际上出售10 000或更多种物品的商店并不少见。即使只出售100种商品的商店也会有1.2610301.26 * 10^{30}种可能的项集组合。这样的运算量,其实即使是对于现在的很多计算机而言,也需要很长的时间才能完成运算。

Apriori算法的原理可以帮我们减少可能感兴趣的项集,降低所需的计算时间。

Apriori算法原理:

  • 如果某个项集是频繁的,那么它的所有子集都是频繁的,例如,假设{1,2}是频繁的,那么{1}{2}也一定是频繁的。

  • 将这个原理取反会发现:如果一个项集是非频繁的,那么它的所有超集也是非频繁的

已知项集{2,3}是非频繁的,那么可立即判断出项集{0,2,3}{1,2,3}{0,1,2,3}都是非频繁的,因此这些项集的支持度也就不需要再计算

Apriori算法的一般过程:

  1. 收集数据:使用任意方法。
  2. 准备数据:任何数据类型都可以,因为我们只保存集合。
  3. 分析数据:使用任意方法。
  4. 训练算法:使用Apriori算法来找到频繁项集。
  5. 测试算法:不需要测试过程。
  6. 使用算法:用于发现频繁项集以及物品之间的关联规则。

实现数据集扫描方法:

from numpy import *


def loadDataSet():

加载数据集

 :return: dataset
    '''
    return [[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]

def createC1(dataSet):
    '''
    创建C1候选项集,C1是所有大小为1的候选项集的列表
    :param dataSet:
    :return: C1
    '''
    # C1是所有大小为1的候选项集的列表
    C1 = []
    # 遍历数据集,逐个添加到C1中
    for record in dataSet:
        for item in record:
            if not [item] in C1:
                C1.append([item])
    C1.sort()
    # 使用不变集合存储C1内部的每个候选项集,那么就可以将其作为字典的Key,如果是list类型不能直接作为字典的Key
    return list(map(frozenset, C1))

def scanDataset(dataset, ck, minSupport):
    '''
    扫描数据集,判断频繁项集
    :param dataset:
    :param ck: ck是所有大小为k的候选项集的列表
    :param minSupport: 设置的最小支持度阈值
    :return: 符合条件的项集、每个项集的支持度
    '''
    # 存储项集的出现次数
    selectedSetCount = {}
    for record in dataset:    # 遍历每一条记录
        for candidateSet in ck:
            # 判断当前候选项集是不是当前记录的子集
            if candidateSet.issubset(record):    
                if candidateSet not in selectedSetCount:
                    selectedSetCount[candidateSet] = 1
                else:
                    selectedSetCount[candidateSet] += 1
    # 计算总条目数
    numItems = float(len(dataset))
    # 存储符合条件的项集
    retList = []
    # 存储项集的支持度
    supportData = {}
    for key in selectedSetCount:
        # 计算支持度
        support = selectedSetCount[key] / numItems
        if support >= minSupport:
            retList.insert(0, key)
        supportData[key] = support
    return retList, supportData

if __name__ == '__main__':
    from pprint import pprint
    dataset = loadDataSet()
    c1 = createC1(dataset)
    pprint(scanDataset(dataset, c1, 0.5))