Apriori算法进行关联分析

1. 使用Apriori算法来发现频繁集

1.1 关联分析

关联分析:是一种在大规模数据集中寻找有趣关系的任务。这些关系可以有两种形式:频繁项集或者关联规则。频繁项集(frequent item sets)是经常出现在一块的物品的集合,关联规则(association rules)暗示两种物品之间可能存在很强的关系。


而有趣、频繁、有趣的关系这些量化的工具就是支持度和可信度

  • 一个项集的支持度(support):被定义为数据集中包含该项集的记录所占的比例。支持度是针对项集来说的,因此可以定义一个最小支持度,而只保留满足最小支持度的项集。
  • 可信度或置信度(confidence):是针对一条关联规则来定义的。这条规则的可信度被定义为“支持度({尿布,葡萄酒})/支持度({尿布})”。

{豆奶}支持度 = 4/5

对候选项计算支持度 = {当前元素出现的次数}/集合的个数

以下是计算关联规则
    
    
        可信度(置信度)值的计算:
            {尿布} —> {葡萄酒}  --   支持度({尿布,葡萄酒}) / 支持度({尿布}) = 可信度
            
            {2} —> {3,5}  -- S({2,3,5}) / S({2,3,5} - {3,5}) = 可信度

1.2 Apriori原理

这里写图片描述

对于上图的集合数目,会发现即使对于仅有4种物品的集合,也需要遍历数据15次。而随着物品数目的增加遍历次数会急剧增长。对于包含—物品的数据集共有2N12N−1种项集组合,需要很长的时间才能完成运算。

为了降低所需的计算时间,有了Apriori原理,Apriori原理是说如果某个项集是频繁的,那么它的所有子集也是频繁的。如果反过来就是说如果一个项集是非频繁集,那么它的所有超集也是非频繁的。

关联分析的目标包括两项:发现频繁项集和发现关联规则。首先需要找到频繁项集,然后才能获得关联规则。 
生成候选集:

对数据集中的每条交易记录tran 
对每个候选项集can: 
检查一下can是否是tran的子集: 
如果是,则增加can的计数值 
对每个候选项集: 
如果其支持度不低于最小值,则保留该项集 
返回所有频繁项集列表。

完整的Apriori算法:

当集合中项的个数大于0时 
构建一个k个项组成的候选项集的列表 
检查数据以确认每个项集都是频繁的 
保留频繁项集并构建k+1项组成的候选项集的列表


import numpy as np
from time import sleep


# 代表四组数据
def loadDataSet():
    return [[1, 3, 4],
            [2, 3, 5],
            [1, 2, 3, 5],
            [2, 5]]


# 构建候选项集合
def createC1(dataSet):
    C1 = []
    for transaction in dataSet:
        for item in transaction:
            if not [item] in C1:
                C1.append([item])

    C1.sort()

    return list(map(frozenset, C1))


'''
     方法:计算所有元素的支持度

     参数:
       D  数据集
       Ck   候选项集合
       minSupport  最小的支持度


     返回值:
       大于等于minSupport候选项的集合
       所有候选项的支持度
'''


def scanD(D, Ck, minSupport):
    # 记录所有的候选项,在所有的数据集中出现的次数
    ssCnt = {}

    # print(type(D))  map
    # print(type(Ck))  map
    # print(type(minSupport)) float
    # print(list(D))

    # 计算每一个候选项,出现的次数
    for tid in D:
        for can in Ck:
            if can.issubset(tid):
                # if not ssCnt.has_key(can):  python2.7
                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:
            # 在下标为零的前面插入key
            retList.insert(0, key)
        supportData[key] = support

    return retList, supportData


'''
    创建候选项集CK

    LK:频繁项集列表
    K:项集元素个数

'''


def aprioriGen(Lk, k):
    retList = []

    lenLk = len(Lk)

    # print(lenLk)

    # 遍历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()
            print(retList)
            print(L1)
            print(L2)

            if L1 == L2:
                retList.append(Lk[i] | Lk[j])

    return retList


# 找到所有的频繁项集
def apriori(dataSet, minSupport=0.5):
    # C1是候选项集列表
    C1 = createC1(dataSet)

    # 数据集中,集合的个数
    D = list(map(set, dataSet))

    # 根据候选项,和数据集D,计算大于等于minSupport候选项的集合,所有候选项的支持度
    L1, supportData = scanD(D, C1, minSupport)

    # print(L1)
    L = [L1]
    # print(L)

    k = 2

    while (len(L[k - 2]) > 0):
        Ck = aprioriGen(L[k - 2], k)

        Lk, supK = scanD(D, Ck, minSupport)  # 得到Lk,即得到满足最小支持度的项集
        # print('Lk_2:', Lk)
        print('supK:', supK)
        print('-----------')
        supportData.update(supK)  # 这里是更新supportData字典,即加入supk,得到总的支持度字典
        # print('supportData_2:', supportData)
        L.append(Lk)  # 得到总的满足支持度要求的项集集合
        # print('L_2:', L)
        k += 1

    return L, supportData




if __name__ == '__main__':
    dataSet = loadDataSet()


    D = map(set, dataSet)

    C1 = createC1(dataSet)

    retList, supportData = scanD(list(D), C1, 0.5)
    print(retList)
    print(supportData)



输出:

[frozenset({5}), frozenset({2}), frozenset({3}), frozenset({1})]
{frozenset({1}): 0.5, frozenset({3}): 0.75, frozenset({4}): 0.25, frozenset({2}): 0.75, frozenset({5}): 0.75}

从运行结果可以看出具体的运行过程,结合下图理解。

这里写图片描述

注意:

  • frozenset是指被“冰冻”的集合,就是说它们是不可改变的,即用户不能修改它们。这里必须要使用frozenset而不是set类型,因为之后必须要将这些集合作为字典键值使用,使用frozenset可以实现这一点,而set却做不到。
  • Python不能创建只有一个整数的集合,因此这里实现必须使用列表。这就是我们使用一个由单物品列表组成的大列表的原因。最后,对大列表进行排序并将其中的每个单元素列表映射到frozenset(),最后返回frozenset的列表。
  • 上面的k-2有点让人疑惑。接下来再进一步讨论细节。当利用{0}、{1}、{2}构建{0,1}、{0,2}、{1,2}时,这实际上是将单个项组合到一块。现在如果想利用{0,1}、{0,2}、{1,2}来创建三元素项集应该怎么做?如果将每两个集合合并,就会得到{0,1,2}、{0,1,2}、{0,l,2}。也就是说,同样的结果集合会重复3次。接下来需要扫描三元素项集列表来得到非重复结果,我们要做的是确保遍历列表的次数最少。现在,如果比较集合{0,1}、{0,2},{1,2}的第1个元素并只对第1个元素相同的集合求并操作,又会得到什么结果?{0,l,2},而且只有一次操作!这样就不需要遍历列表来寻找非重复值。
  • 将两个集合合成一个大小为k的集合。这里使用集合的并操作来完成,在python中对应操作符 |


import numpy as np
from time import sleep


# 代表四组数据
def loadDataSet():
    return [[1, 3, 4],
            [2, 3, 5],
            [1, 2, 3, 5],
            [2, 5]]


# 构建候选项集合
def createC1(dataSet):
    C1 = []
    for transaction in dataSet:
        for item in transaction:
            if not [item] in C1:
                C1.append([item])

    C1.sort()

    return list(map(frozenset, C1))


'''
     方法:计算所有元素的支持度

     参数:
       D  数据集
       Ck   候选项集合
       minSupport  最小的支持度


     返回值:
       大于等于minSupport候选项的集合
       所有候选项的支持度
'''


def scanD(D, Ck, minSupport):
    # 记录所有的候选项,在所有的数据集中出现的次数
    ssCnt = {}

    # print(type(D))  map
    # print(type(Ck))  map
    # print(type(minSupport)) float
    # print(list(D))

    # 计算每一个候选项,出现的次数
    for tid in D:
        for can in Ck:
            if can.issubset(tid):
                # if not ssCnt.has_key(can):  python2.7
                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:
            # 在下标为零的前面插入key
            retList.insert(0, key)
        supportData[key] = support

    return retList, supportData


'''
    创建候选项集CK

    LK:频繁项集列表
    K:项集元素个数

'''


def aprioriGen(Lk, k):
    retList = []

    lenLk = len(Lk)

    # print(lenLk)

    # 遍历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()
            print(retList)
            print(L1)
            print(L2)

            if L1 == L2:
                retList.append(Lk[i] | Lk[j])

    return retList


# 找到所有的频繁项集
def apriori(dataSet, minSupport=0.5):
    # C1是候选项集列表
    C1 = createC1(dataSet)

    # 数据集中,集合的个数
    D = list(map(set, dataSet))

    # 根据候选项,和数据集D,计算大于等于minSupport候选项的集合,所有候选项的支持度
    L1, supportData = scanD(D, C1, minSupport)

    # print(L1)
    L = [L1]
    # print(L)

    k = 2

    while (len(L[k - 2]) > 0):
        Ck = aprioriGen(L[k - 2], k)

        Lk, supK = scanD(D, Ck, minSupport)  # 得到Lk,即得到满足最小支持度的项集
        # print('Lk_2:', Lk)
        print('supK:', supK)
        print('-----------')
        supportData.update(supK)  # 这里是更新supportData字典,即加入supk,得到总的支持度字典
        # print('supportData_2:', supportData)
        L.append(Lk)  # 得到总的满足支持度要求的项集集合
        # print('L_2:', L)
        k += 1

    return L, supportData


'''
    以下是计算关联规则


        可信度(置信度)值的计算:
            {尿布} —> {葡萄酒}  --   支持度({尿布,葡萄酒}) / 支持度({尿布}) = 可信度

            {2} —> {3,5}  -- S({2,3,5}) / S({2,3,5} - {3,5}) = 可信度


'''


# 生成候选规则集合(计算可信度值)
def calcConf(freqSet, H, supportData, brl, minConf=0.7):
    prunedH = []  # 空列表保存满足最小可信度要求的规则列表
    # print(freqSet) #frozenset({3, 5})
    # print(H) #[frozenset({3}), frozenset({5})]

    '''
        字典的运算:
            {1,3} - {3} = {1}
    '''

    for conseq in H:  # 遍历H中的所有项集,并计算他们的可信度值
        # supportData[freqSet]是频繁集的值,也就是支持度,freqSet是相应的键,freqSet-conseq集合相减
        print('P -->H:', freqSet - conseq, '-->', conseq)
        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):
    # print(freqSet) #frozenset({2, 3, 5})
    # print(H) #[frozenset({2}), frozenset({3}), frozenset({5})]

    m = len(H[0])  # 首先计算拆分的频繁项集的大小
    if (len(freqSet) > (m + 1)):  # 尝试进一步合并
        # Hmp1  [frozenset({2, 3}), frozenset({2, 5}), frozenset({3, 5})]
        Hmp1 = aprioriGen(H, m + 1)  # 创建Hm+1条新候选规则,生成H中元素的无重复组合
        Hmp1 = calcConf(freqSet, Hmp1, supportData, brl, minConf)
        print('Hmp1_2:', Hmp1)
        if (len(Hmp1) > 1):  # 使用hmp迭代调用函数rulesFromConseq()来判断是否可以进一步组合规则
            rulesFromConseq(freqSet, Hmp1, supportData, brl, minConf)


'''
    generateRules -- 关联规则生成函数

    频繁项集列表,包含频繁项集支持数据的字典 
    L: 频繁项集列表
    supportData: 所有频繁项集的支持度 (包含频繁项集支持数据的字典 )
    minConf: 最小可信度阈值

'''


def generateRules(L, supportData, minConf=0.7):
    bigRuleList = []

    for i in range(1, len(L)):  # only get the sets with two or more items
        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




if __name__ == '__main__':
    dataSet = loadDataSet()



    L, supportData = apriori(dataSet)
    print('大话')
    print(L)
    print('大话')
    rules = generateRules(L, supportData, minConf=0.5)
    print('大话')
    print(rules)  #只打印最后的rules


大话   
[(frozenset({3}), frozenset({2}), 0.6666666666666666), (frozenset({2}), frozenset({3}), 0.6666666666666666), (frozenset({5}), frozenset({3}), 0.6666666666666666), (frozenset({3}), frozenset({5}), 0.6666666666666666), (frozenset({5}), frozenset({2}), 1.0), (frozenset({2}), frozenset({5}), 1.0), (frozenset({3}), frozenset({1}), 0.6666666666666666), (frozenset({1}), frozenset({3}), 1.0), (frozenset({5}), frozenset({2, 3}), 0.6666666666666666), (frozenset({3}), frozenset({2, 5}), 0.6666666666666666), (frozenset({2}), frozenset({3, 5}), 0.6666666666666666)]


关于支持度的计算:





  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值