Apriori算法の学习 附代码实现

一.聚类分析概述

1.关联分析概述

关联分析(association analysis)是一种在大规模数据集中寻找有趣关系的非监督学习算法。这种关系可以有两种形式:频繁项集或者关联规则。频繁项集(frequent item sets)是经常出现在一块的物品的集合,关联规则(association rules)暗示两种物品之间可能存在很强的关系。
关联分析的一个典型例子是购物篮分析。下面我们一起来看一个简单例子。
在这里插入图片描述

交易号码代表交易流水号,商品代表一个顾客一次购买的全部商品。 在这里需要明确几个定义:

  • 事务:每一条交易称为一个事务。例如,在这个例子中包含了5个事务。
  • 项:交易的每一个物品称为一个项,例如豆奶、尿布等。
  • 项集:包含零个或者多个项的集合叫做项集,例如 {豆奶,莴苣}。
  • k-项集:包含k个项的项集叫做k-项集。例如 {豆奶} 叫做1-项集,{豆奶,尿布,啤酒} 叫做3-项集。
  • 前件和后件:对于规则{尿布}→{啤酒},{尿布} 叫做前件,{啤酒} 叫做后件。

2.频繁项集的评估标准

频繁项集是经常出现在一块的物品的集合,那么问题就来了:第一,当数据量非常大的时候,我们无法凭肉眼找出 经常出现在一起的物品,这就催生了关联规则挖掘算法,比如 Apriori、PrefixSpan、CBA 等。第二是我们缺乏一 个频繁项集的标准。比如10条记录,里面A和B同时出现了三次,那么我们能不能说A和B一起构成频繁项集呢?因此我们需要一个评估频繁项集的标准。

常用的频繁项集的评估标准有支持度,置信度和提升度三个。

2.1 支持度

支持度就是几个关联的数据在数据集中出现的次数占总数据集的比重。或者说几个数据关联出现的概率。如果我们 有两个想分析关联性的数据X和Y,则对应的支持度为:
在这里插入图片描述
比如上面例子中,在5条交易记录中{尿布, 啤酒}总共出现了3次,所以
在这里插入图片描述
一般来说,支持度高的数据不一定构成频繁项集,但是支持度太低的数据肯定不构成频繁项集。 另外,支持度是针 对项集来说的,因此,可以定义一个小支持度,而只保留满足小支持度的项集,起到一个项集过滤的作用。

2.2 置信度

置信度体现了一个数据出现后,另一个数据出现的概率,或者说数据的条件概率。如果我们有两个想分析关联性的 数据X和Y,X对Y的置信度为
在这里插入图片描述
比如上面例子中,豆奶对莴苣的置信度=豆奶和莴苣同时出现的概率/莴苣出现的概率,则有
在这里插入图片描述
关于支持度和置信度:
支持度是一种重要度量,因为支持度很低的规则可能只是偶然出现。从商务角度来看,低支持度的规则多半也是无意义的,因为对顾客很少同时购买的商品进行促销可能并无益处。因此,支持度通常用来删去那些无意义的规则。此外,支持度还具有一种期望的性质,可以用于关联规则的有效发现。
置信度度量通过规则进行推理具有可靠性。对于给定的规则X→Y,置信度越高,Y在包含X的事务中出现 的可能性就越大**。**置信度也可以估计Y在给定X下的条件概率。
由关联规则作出的推论并不必然蕴涵因果关系。它只表示规则前件和后件同 时出现的一种概率。

2.3 提升度

提升度表示含有Y的条件下同时含有X的概率,与X总体发生的概率之比,也就是X对Y的置信度与X总体发生的概率之比,即:
在这里插入图片描述
提升度体现了X和Y之间的关联关系,提升度如果大于1则表示Y=>X是有效的强关联规则,如果小于等于1则表示是无效的强关联规则。其中如果是等于1,则表示X和Y独立,此时 在这里插入图片描述

3.关联规则的发现

给定事务的集合T,关联规则发现是指找出支持度大于等于 并且置信度大于等于 的所有规则, 其中 和 是对应的支持度和置信度阈值。挖掘关联规则的一种原始方法是:计算每个可能规则的 支持度和置信度。但是这种方法的代价很高,令人望而却步,因为可以从数据集提取的规则的数目达指数级。更具 体地说,从包含d个项的数据集提取的可能规则的总数为:
在这里插入图片描述
【公式证明过程】可参考:https://blog.youkuaiyun.com/wxbmelisky/article/details/48268833

对于我们前面例子,里面一共有6中商品,提取的可能规则数为602,也就是说对于只有6 种商品的小数据集都需要计算602条规则的支持度和置信度。使用minsup= 20%和minconf=50%,80%以上的规则 将被丢弃,使得大部分计算是无用的开销。为了避免进行不必要的计算,事先对规则剪枝,而无须计算它们的支持 度和置信度的值将是有益的。 因此,大多数关联规则挖掘算法通常采用的一种策略是,将关联规则挖掘任务分解为 如下两个主要的子任务。

  • 频繁项集の产生
    其目标是找出所有满足最小支持度的所有项集,这些项集成为频繁项集。
  • 强关联规则の产生
    这里的目标是从上一步发现的频繁项集中找出所有满足最小置信度的所有项集,从而提取出高置信度的规则,这些规则叫做强关联规则。
    通常,频繁项集产生所需的计算开销远大于产生规则所需的计算开销。那有没有办法可以减少这种无用的计算呢? 有。下面这两种方法可以降低产生频繁项集的计算复杂度:
    (1)减少候选项集的数目M。
    (2)减少比较次数。替代将每个候选项集与每个事务相匹配,可以使用更高级的数据结构,或者存储候选项集或 者压缩数据集,来减少比较次数。
    这些策略将在Apriori算法基本思想中进行讨论。

二.Apriori算法原理

先验原理如果一个项集是频繁的,那么它的所有子集也一定是频繁的。
我们以同学同时学习为例,假设A,B,C三个同学一起出现在教室的情况满足最小支持度,那么无论是A,B,C中任意一个或者任意两个同学出现在教室的情况也一定满足最小支持度。
下图中显示了物品之间的所有可能的组合。其中0表示同学A,1表示同学B,图中表示4个同学的例子,同学集合之间的连线表明两个或者更多集合可以组合形成一个更大的集合。
根据先验原理,假如 {1,2,3} 是频繁项集,那么它的所有子集(下图中橘色项集)一定也是频繁的。
在这里插入图片描述

这个先验原理直观上并没有什么帮助,但是反过来看就有用了,也就是说如果一个项集是非频繁项集,那么它的所 有超集也是非频繁的(如下图所示)。
在这里插入图片描述
上图中,已知阴影项集 {2,3} 是非频繁的。利用这个知识,我们就知道项集{0,2,3} ,{1,2,3}以及{0,1,2,3}也是非频 繁的。这也就是说,一旦计算出了{2,3}的支持度,知道它是非频繁的之后,就不需要再计算{0,2,3}、{1,2,3}和 {0,1,2,3}的支持度,因为我们知道这些集合不会满足我们的要求。使用该原理就可以避免项集数目的指数增长,从 而在合理时间内计算出频繁项集。

三.使用Apriori算法发现频繁项集

关联分析的目的包括两项:发现频繁项集和发现关联规则。首先需要找到频繁项集,然后才能获得关联规则。
Apriori 算法过程
在这里插入图片描述
C1,C2,…,Ck分别表示1-项集,2-项集,…,k-项集;
L1,L2,…,Lk分别表示有k个数据项的频繁项集。
Scan表示数据集扫描函数。该函数起到的作用是支持度过滤,满足小支持度的项集才留下,不满足小支持度的 项集直接舍掉。
下面我们用python代码来实现这一过程

1.生成候选项集

按照常理,我们先来写一个数据集扫描大概的伪代码:

对数据集中的每条数据transaction
对每个候选项集can
	检查can是否是transaction的子集:
	如果是,则增加can的计数
对每个候选项集:
	如果支持度大于等于最小支持度,保留该项集
	返回所有频繁项集列表

首先,我们创建一个简单的数据集,便于我们理解
函数1:创建一个用于测试的数据集

def loadData():
    return [[1,2,5], [2,4], [2,3],[1,2,4], [1,3], [2,3],[1,3], [1,2,3,5], [1,2,3]]

函数2:构建第一个候选集合C1
由于算法一开始是从输入数据集中提取候选项集列表,所以这里需要一个特殊的函数来处理——frozenset类型。 frozenset是指被“冰冻”的集合,也就是用户不能修改他们。这里必须要用frozenset而非set类型,是因为后面我们 要将这些集合作为字典键值使用。

该函数流程是:首先创建一个空列表,用来储存所有不重复的项值。接下来遍历数据集中所有的交易记录,对每一 条交易记录,遍历记录中的每一个项。如果该项没有在C1中出现过,那么就把它添加到C1中,这里需要注意的 是,并非简单地添加物品项,而是添加只包含该物品项的一个集合(此处用集合或者列表都可以)。循环完毕后, 对整个C1进行排序并将其中每个单元素集合映射到frozenset(),后返回frozenset的列表。

def creatC1(dataset):
    c1=[]
    for substr in dataset:
        for item in substr:
            if not [item] in c1:
                c1.append([item])
    c1.sort()
    return list(map(frozenset,c1))

函数3:生成满足小支持度的频繁项集L1。
C1是大小为1的所有候选项集的集合,Apriori算法首先构建集合C1,然后扫描数据集来判断这些只有一个元素的项 集是否满足小支持度的要求。那些满足低要求的项集构成集合L1。

def scanD(D , ck , minSupport):
    ssCnt = {}
	#计算出现的频率
    for tid in D:
        for can in ck:
            if can.issubset(tid):
                if not can in ssCnt:
                    ssCnt[can] = 1
                else:
                    ssCnt[can] += 1
    numItems = float(len(D))
    retList = []
    supportData = {}
    for key in ssCnt:
        support = ssCnt[key]/numItems #计算支持度
        if support >= minSupport:
            retList.insert(0,key) #个人强迫症,喜欢按顺序从头插入
        supportData[key] = support
    return  retList,supportData

2.组织完整的Apriori算法

伪代码如下:

当集合中项的个数大于0的时候:
	构建一个k-项集组成的列表:
	检查数据,确保每个项集都是频繁的(scan函数)
	保留频繁项集,构建k+1项集组成列表

**函数1.**输入参数为频繁项集Lk与项集元素个数k,输出为retlist.

def aprioriGen(Lk,k):
    retlist = []
    lenLk = len(Lk)
    for i in range(lenLk):
        for j in range(i+1,lenLk):
         #前k-2个项相同时,将两个集合合并
            L1 = list(Lk[i])[:k-2]
            L2 = list(Lk[j])[:k-2]
            # print(Lk)
            # print(list(Lk[i]))
            # print(list(Lk[j]))
            # print(k)
            # print(L1)
            # print(L2)
            L1.sort()
            L2.sort()
            if L1==L2:
                retlist.append(Lk[i]|Lk[j])
    return retlist

这里的K-2我们可以这样理解:如果我们现在是2-项集{1,3},{1,4},我们要合并这两项的话,我们合并出来是3-项集,也就是我们只需要看其中一项是不是相同,如果相同我们就有理由将其合并起来,如果经过排序之后第一项都不相同我们可以认为这两个合并起来是个4-项集,所以我们不能合并。特例一般化我们可以提取规律:我们只需要比较前k-2项,如果前k-2项相同,那么我们就有理由将其合并,这样就不需要遍历列表来寻找非重复值了。

函数2:根据数据集和支持度,返回所有的频繁项集,以及所有项集的支持度。

def apiori(dataset,minsupport=0.5):
    c1 = creatC1(dataset)
    D = list(map(set,dataset))
    L1,support = scanD(D,c1,minsupport)
    L = [L1]
    k =2
    while(len(L[k-2]) >0 ):
        Ck = aprioriGen(L[k-2],k)
        Lk,supK = scanD(D,Ck,minsupport)
        support.update(supK)
        L.append(Lk)
        k+=1
    return L,support

再用个图来表示我们的算法实现过程:
在这里插入图片描述

四.挖掘关联规则

1.编写计算置信度函数

def calcConf(freqSet, H, supportData, brl, minConf=0.3):
    minseq = [] #返回一个满足最小可信度要求的规则列表
    for conseq in H:

        conf = supportData[freqSet]/supportData[freqSet-conseq] #freqset = 2,3  freqset-consq = 3 conseq = 2
        if conf >= minConf:
            print(freqSet-conseq,'-->',conseq,'conf:',conf)
            brl.append((freqSet-conseq,conseq,conf)) #在3(freqset-consq)发生概率下 发生2(conseq)的概率
            minseq.append(conseq)
    return minseq

思路就是遍历一遍频繁项集,将其中满足最小支持度的拆开,计算是否满足最小置信度,例如freqset = 2,3 那么freqset-consq = 3,conseq = 2 这样计算的就是在3发生的概率下,2发生的概率 也就是3->2的置信度

2.编写并组织完整的挖掘关联规则算法

函数1递归进行合并并算置信度以求取关联规则

def rulesFromConseq(freqSet, H, supportdata, brl, minConf=0.3):
    m = len(H[0])
    #fre 2,3,5
    #H 2  3   5
    if(len(freqSet) > m+1):
        temp1 = aprioriGen(H , m+1) # 23 25 35
        # print('2')
        temp1 = calcConf(freqSet,temp1,supportdata,brl,minConf)
        if(len(temp1) > 1):
            rulesFromConseq(freqSet, temp1, supportdata, brl, minConf)

当为2项集时,直接计算置信度,当项集数大于2时再通过递归进行计算。
下面这行代码是排除了{{2,3},{5}}这种情况,我们不可能把{5}也放入前件,这样的话后件就没有元素了。

if(len(freqSet) > m+1):

函数2 输入频繁项集以及支持度的列表,返回强关联项集

def generateRules(L , supportdata,minConf = 0.3):
    bigRuleList = []
    for i in range(1,len(L)):
        for freqSet in L[i]:
            H1 = [frozenset([item]) for item in freqSet]
            if(i > 1):#频繁项集元素超过2,进一步合并
                rulesFromConseq(freqSet,H1,supportdata,bigRuleList)
            else: #后件为1直接算
                # print('1')
                calcConf(freqSet,H1,supportdata,bigRuleList)
    return bigRuleList

最后调用函数就可以看到答案结果了!!!

dataset = loadData()
L,supportdata = apiori(dataset,0.2)
print(L)   #[[frozenset({5}), frozenset({2}), frozenset({3}), frozenset({1})], [frozenset({2, 3}), frozenset({3, 5}), frozenset({2, 5}), frozenset({1, 3})], [frozenset({2, 3, 5})], []]
# print(len(L))
generateRules(L,supportdata)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值