关联规则 & Apriori算法

associati analysis

关联分析用来在大规模数据中发现某些潜在的关系,这些关系可以有两种形式

  • 频繁项集:经常出现在一起的东西的集合
  • 关联规则:两种东西之间存在强关系

concept

频繁项集中的频繁如何来度量呢?

支持度可信度来度量

项集:不同物品组成的集合

**支持度(support)**表示X,Y同时出现的概率,公式表示如下

img

**置信度(confidence)**表示发生X的集合中,出现Y的概率,即

img

提升度(lift),衡量X,Y之间的相关性,如果lift>1表示两者之间存在关联性(会一起出现);lift=1表示两者之间没有关系(两个事件相互独立);lift<1则表示两者之间可能存在替代性(一方出现,另一方就不出现了)。

img

所有的关联规则算法大致分成两个阶段:

  1. 根据minSupport的阈值来挖掘频繁集
  2. 定义confidence(X <= Y) 的阈值来挖掘关联规则

搜寻频繁项集—Apriori

Apriori是一个基于搜索的算法,是一个候选集生成的算法,用的是广度优先搜索, 用来发现频繁项集的算法

图解Apriori算法的过程:

img

我觉得这个图对于Apriori算法的描述已经十分的清晰了

apriori算法有两个先验条件

  1. 如果一个项集不是频繁项集,那么这个项集的超集(父集)也不是频繁项集
  2. 如果一个项集是频繁项集,那么这个项集的子集也是频繁项集

apriori算法有两个输入:最小支持度minSupport,数据集

Apriori算法中的频繁项集产生方法

在Apriori算法中,用到了两条先验原理:<1>如果一个项集不是频繁项集,那么该项集的超集也必定不是频繁项集;<2>如果一个项集是频繁项集,那么该项集的子集也是频繁项集,利用这两条先验原理可以大大较少候选频繁项集的数量。

Apriori算法的思想就是:生成项集中所有子集的全排列,对这些全排列进行逐一遍历,与数据集中的每个样本进行逐一比对,记录符合支持度的子集

比如我们数据集中有 4 个商品,商品0, 商品1,商品2,商品3,那么他们可以写成一个集合{0, 1, 2, 3},那么此时就要生成{0}, {1}, {2}, {3}, {0,1}, {0,2}, {0,3}…{0,1,2,3},然后计算每一个的支持度,保留符合支持度的集合,这些集合就是频繁项集

在这里插入图片描述

Apriori的算法步骤:

  1. 生成所有项集的全排列,遍历整个数据集,找出所有的 1-项(项集中只包含一个物品)的项集,如[{A}, {B}, {C}](因为只有知道了所有的1-项后面才能生成2-项,直到不能生成项为止),根据最小支持度minSupport筛选出的频繁项集。
  2. 遍历前一步得到的频繁项集列表,生成 2-项项集,再遍历整个数据集,找出2-项的频繁项集,一直到不能生成频繁项集为止

注意:第二步中,由(k-1)-项 生成 k-项的项集,要对(k-1)-项自身的集合进行遍历,在自身的集合中找出两个集合进行并集处理,来得到 k-项的项集,但这个两个要进行并集的(k-1)-项的项集必须保证前 k-2 项是相同的,比如设当前的 k=3,有一个项集{A, B, C} 和一个项集{A, D, E},他们的并集就是{A, B, C, D, E},此时这个项集是k=5了,不满足k=4,要保证前k-2项相同如{A, B, C} 和 {A, B, D},并集才能生成{A, B, C, D}, 所以在由(k-1)-项的项集生成k-项的项集时,需要对其进行排序

生成关联规则

关联规则是从频繁项集中提取的,因为在Apriori算法中已经记录了每个频繁项集及其对应的支持度 S u p p o r t Support Support,所以生成关联规则的时候,只需遍历所有的频繁项集即可,不用重新遍历数据集。

生成关联规则的步骤:

关联规则的生成算法的每一步的作用对象,是一个频繁项集!!!一个频繁项集!!!,思想也是一样的,对这个频繁项集,找出它所有的 “全排列”(也是先找到当前操作的频繁项集freqSet的所有1-项项集,根据所有的1-项项集得到freqSet与其的差集,然后生成所有的2-项,根据所有的1-项项集得到freqSet与其的差集,直到不能生成为止)如{A, B, C},就要生成 { A } → { B , C } {\{A}\}\rightarrow{\{B, C\}} {A}{B,C}, { B } → { A , C } {\{B}\}\rightarrow{\{A, C\}} {B}{A,C} { A , B } → { C } {\{A,B}\}\rightarrow{\{C\}} {A,B}{C}等,直到枚举完所有的可能,值得注意的是: { A } → { B , C } {\{A}\}\rightarrow{\{B, C\}} {A}{B,C} { B , C } → { A } {\{B,C}\}\rightarrow{\{A\}} {B,C}{A}是两个不同的关联规则,根据置信度Confidence的定义,他们的分母分别是 C o u n t ( A ) Count(A) Count(A) C o u n t ( B , C ) Count(B,C) Count(B,C),是不一样的。

在生成频繁项集时,可以依据两条先验规则减少计算量,而在提取关联规则时,只有一条规则可以利用:如果规则 X → Y X\rightarrow Y XY不满足置信度要求,那么 X − X ‘ → Y + X ‘ X-X^{`}\rightarrow Y+X^{`} XXY+X也不满足置信度要求,其中 X ‘ X^{`} X X X X的子集。这条规则可以这样理解,假设置信度阈值为 α \alpha α,则有
S ( X , Y ) S ( X ) &lt; α \frac{S(X,Y)}{S(X)}&lt;\alpha S(X)S(X,Y)<α
由于 X ‘ X^{`} X X X X的子集,因此 X ‘ X^{`} X 的支持度一定不小于X,假设 X ‘ = X + k X^{`}=X+k X=X+k ,则有
S ( X − X ‘ , Y + X ‘ ) S ( X − X ‘ ) = S ( X , Y ) S ( X ) + k &lt; S ( X , Y ) S ( X ) &lt; α \frac{S(X-X^{`},Y+X^{`})}{S(X-X^{`})}=\frac{S(X,Y)}{S(X)+k}&lt;\frac{S(X,Y)}{S(X)}&lt;\alpha S(XX)S(XX,Y+X)=S(X)+kS(X,Y)<S(X)S(X,Y)<α
img

代码实现:

'''
1.找到频繁项集
2.发现关联规则

Apriori 参数: 最小支持度support, 数据集
'''
from numpy import *


# todo:寻找频繁项集

# 加载数据集
def loadDataSet():
    return [[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]


# 生成所有单个物品的项集列表
def creatC1(dataSet):
    '''
    dataSet 二维数组
    '''
    C1 = list()
    for transaction in dataSet:
        for item in transaction:
            if [item] not in C1:
                C1.append([item])
    C1.sort()

    # 使用frozenset为了后面可以将这些值作为字典的键
    return list(map(frozenset, C1))


# 过滤掉不符合最小支持度的集合
# 返回 频繁项集列表 retList, 所有元素的支持度子字典 
def scanD(D, Ck, minSupport):
    '''
    Arg:
        D:DataSet
        Ck:第k个候选项集 [{}, {}, {}]
        因为后面肯定是从候选项集1,即候选项集中每一项都是1个元素,
        到候选项集2,候选项集中每一项都是2个元素
    
    variable:
        ssCnt <dict> :计算候选项集中的每一项在数据集中出现的次数,后面用来计算 支持度support
        supportData <dict> :键为一个项集,值为该项集的支持度support
        retList <list> :将所有支持度大于阈值的项集即频繁项集保存到此列表 [{'a', 'b'}, {'a', 'c'}]

    return:
        retList
        supportData
    '''
    ssCnt = dict()
    # 把数据集和候选项集 Ck 的每一条数据进行逐一比对
    for tid in D:
        for can in Ck:
            # 判断子集
            if can.issubset(tid):
                if can not in ssCnt:
                    ssCnt[can] = 1
                else:
                    ssCnt[can] += 1

    numItems = float(len(D))
    retList = []
    supportData = dict()
    for key in ssCnt:
        support = ssCnt[key] / numItems
        if support >= minSupport:
            retList.insert(0, key)
        supportData[key] = support
    return retList, supportData


# 将 L_{k-1} 的项集列表转换为 Lk 的项集列表
def aprioriGen(Lk, k):
    '''
    Arg:
        Lk 频繁项集列表  [{'a', 'b'}, {'a', 'c'}, {'b', 'c'}]
        k 项集元素的个数

    todo:
        从每个项集中元素个数为 k-1 个的候选项集,生成每个项集中元素个数为 k 个的候选项集

    attention:
        要生成Lk,要遍历 L_{k-1} 中的每个元素,两两进行并集操作,但是为了保证并集得到的集合的元素个数为 k 个,
        就要保证 L_{k-1} 中前 k-2 个元素都是相同的,如果不相同如 L3 中的一个 {'a', 'b', 'c} 和 L3 中的另一个
        {'b', 'f', 'd'} 就会生成一个 L5 的集合 {'a', 'b', 'c', 'd', 'f'},而 {'a', 'b', 'c} 和 {'e', 'b', 'c}
        就能生成一个 L4 的集合 {'a', 'b', 'c', 'd'},所以要对 L_{k-1} 中的前 k-2 个元素进行排序并比对
    '''
    retList = []
    lenLk = len(Lk)
    for i in range(lenLk):
        for j in range(i+1, lenLk):
            L1 = list(Lk[i])[:k-2]
            L2 = list(Lk[j])[:k-2]
            L1.sort()
            L2.sort()
            if L1 == L2:
                # 求两者的并集
                retList.append(Lk[i] | Lk[j])
    return retList


def apriori(dataSet, minSupport=0.5):
    '''
    Arg:
        dataSet: 二维数组
        minSupport: 最小支持度

    variable:
        C: 项集列表,每一项为一个集合
        D: 对数据集<二维数组>的每一行进行集合处理
        L: 存储所有频繁项集的列表

    algorithm:
        从每一项为 1个元素的集合的候选集生成频繁项集,一直到 k个元素的集合的候选集生成频繁项集
        直到不能在生成频繁项集为止

    '''
    C1 = creatC1(dataSet)
    D = map(set, dataSet)
    print('D=', D)
    L1, supportData = scanD(D, C1, minSupport)
    L = [L1]
    k = 2
    while (len(L[k-2]) > 0):
        print('k=', k, L, L[k-2])
        Ck = aprioriGen(L[k-2], k)
        print('Ck', Ck)

        Lk, supK = scanD(D, Ck, minSupport)
        supportData.update(supK)
        if len(Lk) == 0:
            break
        L.append(Lk)
        k += 1
    return L, supportData

# todo: 提取关联规则

def calcConf(freqSet, H, supportData, brl, minConf=0.7):
    """calcConf(对两个元素的频繁项,计算可信度,例如: {1,2}/{1} 或者 {1,2}/{2} 看是否满足条件)

    Args:
        freqSet 频繁项集中的元素,例如: frozenset([1, 3])    
        H 频繁项集中的元素的集合,例如: [frozenset([1]), frozenset([3])]
        supportData 所有元素的支持度的字典
        brl即 bigrulelist 用来记录所有的关联规则[(freqSet-conseq, conseq, conf)]
        minConf 最小可信度
    Returns:
        prunedH 记录 可信度大于阈值的集合
    """
    prunedH = []
    for conseq in H:
        conf = supportData[freqSet] / supportData[freqSet-conseq]
        if conf >= minConf:
            print(freqSet - conseq, '-->', conseq, 'conf:', conf)
            brl.append((freqSet-conseq, conseq, conf))
            prunedH.append(conseq)

    return prunedH # 满足最小置信度要求的每一项为单个元素的集合


def rulesFromConseq(freqSet, H, supportData, brl, minConf=0.7):
    """rulesFromConseq

    Args:
        freqSet 频繁项集中的元素,例如: frozenset([2, 3, 5])    
        H 频繁项集中的元素的集合,例如: [frozenset([2]), frozenset([3]), frozenset([5])]
        supportData 所有元素的支持度的字典
        brl即 bigrulelist 用来记录所有的关联规则[(freqSet-conseq, conseq, conf)]
        minConf 最小可信度
    """
    # H[0] 是 freqSet 的元素组合的第一个元素,并且 H 中所有元素的长度都一样,长度由 aprioriGen(H, m+1) 这里的 m + 1 来控制
    # 该函数递归时,H[0] 的长度从 1 开始增长 1 2 3 ...
    # 假设 freqSet = frozenset([2, 3, 5]), H = [frozenset([2]), frozenset([3]), frozenset([5])]
    # 那么 m = len(H[0]) 的递归的值依次为 1 2
    # 在 m = 2 时, 跳出该递归。假设再递归一次,那么 H[0] = frozenset([2, 3, 5]),freqSet = frozenset([2, 3, 5]) ,没必要再计算 freqSet 与 H[0] 的关联规则了。
    m = len(H[0]) # 获得 Lk 的长度
    # 如果当前长度大于 Lk 的长度就不用在递归下去了
    if (len(freqSet) > (m + 1)):
        print('freqSet*******', len(freqSet), m+1, freqSet, H, H[0])
        Hmp1 = aprioriGen(H, m+1)
        Hmp1 = calcConf(freqSet, Hmp1, supportData, brl, minConf)
        # 到最后穷举完所有的组合 len(Hmp1)就等于 1,剩下它自己了
        if(len(Hmp1) > 1):
            rulesFromConseq(freqSet, Hmp1, supportData, brl, minConf)


def generateRules(L, supportData, minConf=0.7):
    '''
    Arg:
        L: 频繁项集列表
    '''
    bigRuleList = []
    for i in range(1, len(L)):
        for freqSet in L[i]:
            H1 = [frozenset([item]) for item in freqSet]
            if (i > 1):
                rulesFromConseq(freqSet, H1, supportData, bigRuleList, minConf)
            else:
                calcConf(freqSet, H1, supportData, bigRuleList, minConf)
    return bigRuleList

使用mlxtend库实现

https://blog.csdn.net/qq_36523839/article/details/83960195

参考博客:
https://blog.csdn.net/huguozhiengr/article/details/82869591
https://ailearning.apachecn.org/#/docs/ml/11.%E4%BD%BF%E7%94%A8Apriori%E7%AE%97%E6%B3%95%E8%BF%9B%E8%A1%8C%E5%85%B3%E8%81%94%E5%88%86%E6%9E%90

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值