Apriori算法进行关联分析实战

技术交流QQ群:1027579432,欢迎你的加入!

使用Apriori算法进行关联分析(层次聚类)

一、基础知识

  • 1.关联分析定义及存在的问题
    • 定义:从大规模的数据集中寻找物品间的隐含关系,被称为关联分析或关联规则学习。
    • 关联分析存在的主要问题:主要问题在于寻找不同物品的组合是一项很耗时的任务,所需要的计算代价很高,暴力方法无法解决这个问题,所以使用更加合理的方法在合理的时间范围内找到频繁项集
  • 2.Apriori算法的优缺点及适用场合
    • 优点:容易编码实现
    • 缺点:在大数据集上可能较慢
    • 适用场合:数值型或标称型数据
  • 3.关联分析中的基本概念
    • 关联分析是一种在大规模数据集上寻找有趣关系的任务。这些关系可以有两种形式来表示,频繁项集和关联规则
    • 频繁项集:经常出现在一块的物品的集合。
    • 关联规则:暗示两种物品之间可能存在很强的关系。
  • 4.频繁项集和关联规则的解释
    • 下面的所有记录中,集合{葡萄酒、尿布、豆奶}就是一个频繁集;也可以找出像尿布->葡萄酒的关联规则,这意味着,当有人购买尿布时,那么他很有可能会买葡萄酒。
          记录1    豆奶,莴苣
          记录2    莴苣,尿布,葡萄酒,甜菜
          记录3    豆奶,尿布,葡萄酒,橙汁
          记录4    莴苣,豆奶,尿布,葡萄酒
          记录5    莴苣,豆奶,尿布,橙汁
      
  • 5.频繁的定义
    • 何谓有趣关系?有什么来定义有趣?当寻找频繁项集时,频繁的定义是什么?可以使用支持度和可信度来描述频繁。
    • 一个项集的支持度:数据集中包含该项集的记录所占有的比例。例如,上例中{豆奶}的支持度是4/5,{豆奶,尿布}的支持度是3/5。支持度是针对项集来说的,因此可以定义一个最小支持度,而只保留满足最小支持度的项集。
    • 一条关联规则的可信度/置信度:是针对某条关联规则来定义的。例如,上例中像关联规则{尿布}->{葡萄酒}这条关联规则的可信度被定义为支持度({尿布,葡萄酒}})=3/5 / 支持度({尿布})=4/5,可知尿布->葡萄酒的可信度是0.75,这意味着,对于包含所有尿布的记录,我们的规则对其中的75%都是适用的。

二、Apriori算法原理

  • 假设经营一家商店,商品的种类并不多。我们的目标是找出顾客经常一起购买的商品集合,目前只有四种商品,即0,1,2,3。?表示空集集不包括任何商品的集合。商品集合之间的连线表示两个或更多集合可以组合成一个更大的集合。
  • 上面我们说过,使用支持度来表示频繁的定义。因此,一个集合的支持度表示为有多少比例的记录包含该集合。例如,给定集合{0,3},如何计算其支持度?我们首先遍历,每条记录并检查此记录是否同时包含0和3,如果同时包含了这两个商品,则就增加计数值。在扫描完所有的数据后,使用统计得到的总数除以总的记录数目,就可以得到支持度。
  • 存在的问题:上面只是针对单个集合{0,3},要获得每种可能集合的支持度就需要多次重复上述过程。根据下图,可以发现,仅有4种商品的集合,也需要遍历数据15次。随着商品种类的增加,遍历次数就会剧烈增加。对于包含N种商品的数据集,一共有2^N-1种项集的组合。需要很长的时间才能计算完。
    所有可能的项集组合.jpg
  • 超集:如果集合S2中每个元素都在集合S1中,而且集合S1中可能包含集合S2中没有的元素,则集合S1是S2的一个超集,集合S2是S1的子集。
    超集.jpg
  • 为了降低所需的计算时间,使用Apriori算法可以减少感兴趣的项集。Apriori原理:如果某个项集是频繁的,那么它的所有子集也是频繁的。例如,集合{0,1}是频繁的,那么{0}、{1}也一定是频繁的。即如果一个项集的非频繁集,它的所有超集也是非频繁的。例如下图中已知集合{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原理就可以避免项集数目的指数增长,从而在合理的时间内计算出频繁集。
    非频繁.jpg

三、使用Apriori算法来发现频繁集

  • 1.关联分析的目标是两个:发现频繁集和发现关联规则。首先需要找到频繁项集,然后才能获得关联规则。
  • 2.Apriori算法发现频繁集的步骤:Apriori算法的两个输入参数分别是最小支持度、数据集。首先会生成所有单个物品的项集列表;接着扫描记录来查看哪些项集满足最小支持度要求,那些不满足最小支持度的集合会被去掉。然后,对剩下的集合进行组合来生成包含两个元素的项集。接着,在重新扫描记录,去掉不满足最小支持度的项集。该过程重复进行,直到所有项集都被去掉。具体代码如下:
  • 加载数据集
        def loadDataSet():
            return [[1, 3, 4], [2, 3, 5], [1, 2, 3, 5], [2, 5]]
    
  • 构建集合C1,C1是大小为1的所有候选项集的集合,如{{1},{2},{3},{4},{5}}
        def createC1(dataSet):
            C1 = []
            for record in dataSet:
                for item in record:
                    if not [item] in C1:
                        C1.append([item])
            C1.sort()
            return list(map(frozenset, C1))  # frozenset是对C1进行冰冻,使C1不能进行修改
    
  • 从C1中生成L1(L1是满足最小支持度的要求的项集构成的集合)
    def scanD(D, Ck, minSupport):
        """
            D:数据集
            Ck:候选项集列表
            minSupport:感兴趣项集的最小支持度
            返回值:retList---L1;supportData---包含支持度值的字典
        """
        ssCnt = {}  # 创建一个空字典,字典的key就是C1中的集合,value是C1中的集合在所有记录中出现的次数
        for record in D:   # 遍历数据集中的每条记录
            for can in Ck:  # 遍历C1中的所有候选项集
                if can.issubset(record):   # 如果C1中的集合是记录中的一部分,则增加字典中对应的计数值;
                    if not can in ssCnt:
                        ssCnt[can] = 1  # 字典的key就是集合
                    else:
                        ssCnt[can] += 1
        numItems = float(len(D))  # 总的样本数
        print("总的记录数:", numItems)
        retList = []  # 创建一个空列表,此列表包含满足最小支持度的集合
        supportData = {}  # 最频繁项集的支持度
        for key in ssCnt:  # 遍历字典中的每个元素,并计算其最小支持度
            support = ssCnt[key] / numItems  # 计算支持度
            if support >= minSupport:  # 如果C1中的支持度满足最小支持度的要求,就将字典中的元素加入retList中
                retList.insert(0, key)  # 在列表的首部插入新的集合
            supportData[key] = support  # 最频繁项集的支持度
        return retList, supportData
    
  • 进行合并操作,创建候选项集Ck
    def aprioriGen(Lk, k):   # 创建候选项集Ck,对L1中的元素两两组合,得出候选项集C2
        """
            频繁项集列表LK,项集元素个数K
        """
        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
    
  • Apriori核心程序
    def apriori(dataSet, minSupport=0.5):
        C1 = createC1(dataSet)
        D = list(map(set, dataSet))
        L1, supportData0 = scanD(D, C1, minSupport)
        L = [L1]
        k = 2
        while(len(L[k - 2]) > 0):
            Ck = aprioriGen(L[k - 2], k)
            Lk, supportDatak = scanD(D, Ck, minSupport)
            supportData0.update(supportDatak)
            L.append(Lk)
            k += 1
        return L, supportData0
    
  • 3.从频繁项集中挖掘关联规则
  • 从频繁项集中挖掘关联规则的步骤:首先从一个频繁项集开始,创建一个关联规则表,每条关联规则的右部只含有一个元素。然后,对这些关联规则进行测试,如果每条关联规则的可信度不满足最小的可信度minConf要求,就去掉此条关联规则。最后,合并剩余的关联规则,创建一个新的关联规则表,其中关联规则的右部包含两个元素。重复上面的步骤,直到关联规则表为空时停止。具体代码如下:
  • 关联规则表的生成
    def generateRules(L, supportData, minConf=0.7):
        """
            L:频繁项集
            supportData:包含那些频繁项集支持数据的字典
            minConf:最小的可信度
            返回:一个包含可信度的规则列表bigRuleList
        """
        bigRuleList = []  # 初始化存放所有关联规则的列表
        for i in range(1, len(L)):
            for freqSet in L[i]:  # 最开始的关联规则表中,每条关联规则freqSet的右部H1只有一个元素
                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
    
  • 可信度的计算,例如存在一条关联规则{0,1,2}-->{3, 5},则可信度的计算为:{0,1,2,3,5}的支持度 / {0,1,2}的支持度。其中下面程序中freqSet就是集合{0,1,2,3,5};H就是集合{3, 5};conseq就是{3}、{5}
    def calcConf(freqSet, H, supportData, brl, minConf=0.7):
        prunedH = []
        for conseq in H:
            conf = supportData[freqSet] / supportData[freqSet - conseq]  # 计算每条关联规则的可信度
            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):
        m = len(H[0])
        if (len(freqSet) > (m + 1)):  # 合并关联规则
            Hmp1 = aprioriGen(H, m + 1)  # 创建新的频繁项集
            Hmp1 = calcConf(freqSet, Hmp1, supportData, brl, minConf)
            if (len(Hmp1) > 1):  # 新的关联规则表中,每条关联规则中的右边Hmp1必须包含两个元素
                rulesFromConseq(freqSet, Hmp1, supportData, brl, minConf)
    
  • 打印关联规则
    # 打印关联规则
    def pntRules(ruleList, itemMeaning):
        for ruleTup in ruleList:
            for item in ruleTup[0]:
                print(itemMeaning[item])
            print("           -------->")
            for item in ruleTup[1]:
                print(itemMeaning[item])
            print("可信度: %f" % ruleTup[2])
    

四.所有代码如下:

```
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # @Date    : 2019-05-18 11:30:01
    # @Author  : cdl (1217096231@qq.com)
    # @Link    : https://github.com/cdlwhm1217096231/python3_spider
    # @Version : $Id$

    from numpy import *


    # 层次聚类,基于查找关联规则Apriori算法


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


    # 构建集合C1,C1是大小为1的所有候选项集的集合,如{{1},{2},{3},{4},{5}}
    def createC1(dataSet):
        C1 = []
        for record in dataSet:
            for item in record:
                if not [item] in C1:
                    C1.append([item])
        C1.sort()
        return list(map(frozenset, C1))  # frozenset是对C1进行冰冻,使C1不能进行修改


    # 从C1中生成L1(L1是满足最小支持度的要求的项集构成的集合)
    def scanD(D, Ck, minSupport):
        """
            D:数据集
            Ck:候选项集列表
            minSupport:感兴趣项集的最小支持度
            返回值:retList---L1;supportData---包含支持度值的字典
        """
        ssCnt = {}  # 创建一个空字典,字典的key就是C1中的集合,value是C1中的集合在所有记录中出现的次数
        for record in D:   # 遍历数据集中的每条记录
            for can in Ck:  # 遍历C1中的所有候选项集
                if can.issubset(record):   # 如果C1中的集合是记录中的一部分,则增加字典中对应的计数值;
                    if not can in ssCnt:
                        ssCnt[can] = 1  # 字典的key就是集合
                    else:
                        ssCnt[can] += 1
        numItems = float(len(D))  # 总的样本数
        print("总的记录数:", numItems)
        retList = []  # 创建一个空列表,此列表包含满足最小支持度的集合
        supportData = {}  # 最频繁项集的支持度
        for key in ssCnt:  # 遍历字典中的每个元素,并计算其最小支持度
            support = ssCnt[key] / numItems  # 计算支持度
            if support >= minSupport:  # 如果C1中的支持度满足最小支持度的要求,就将字典中的元素加入retList中
                retList.insert(0, key)  # 在列表的首部插入新的集合
            supportData[key] = support  # 最频繁项集的支持度
        return retList, supportData


    #
    def aprioriGen(Lk, k):   # 创建候选项集Ck,对L1中的元素两两组合,得出候选项集C2
        """
            频繁项集列表LK,项集元素个数K
        """
        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


    # Apriori核心程序
    def apriori(dataSet, minSupport=0.5):
        C1 = createC1(dataSet)
        D = list(map(set, dataSet))
        L1, supportData0 = scanD(D, C1, minSupport)
        L = [L1]
        k = 2
        while(len(L[k - 2]) > 0):
            Ck = aprioriGen(L[k - 2], k)
            Lk, supportDatak = scanD(D, Ck, minSupport)
            supportData0.update(supportDatak)
            L.append(Lk)
            k += 1
        return L, supportData0


    # 关联规则表的生成
    def generateRules(L, supportData, minConf=0.7):
        """
            L:频繁项集
            supportData:包含那些频繁项集支持数据的字典
            minConf:最小的可信度
            返回:一个包含可信度的规则列表bigRuleList
        """
        bigRuleList = []  # 初始化存放所有关联规则的列表
        for i in range(1, len(L)):
            for freqSet in L[i]:  # 最开始的关联规则表中,每条关联规则freqSet的右部H1只有一个元素
                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


    # 计算可信度
    def calcConf(freqSet, H, supportData, brl, minConf=0.7):
        prunedH = []
        for conseq in H:
            conf = supportData[freqSet] / supportData[freqSet - conseq]  # 计算每条关联规则的可信度
            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):
        m = len(H[0])
        if (len(freqSet) > (m + 1)):  # 合并关联规则
            Hmp1 = aprioriGen(H, m + 1)  # 创建新的频繁项集
            Hmp1 = calcConf(freqSet, Hmp1, supportData, brl, minConf)
            if (len(Hmp1) > 1):  # 新的关联规则表中,每条关联规则中的右边Hmp1必须包含两个元素
                rulesFromConseq(freqSet, Hmp1, supportData, brl, minConf)


    # 打印关联规则
    def pntRules(ruleList, itemMeaning):
        for ruleTup in ruleList:
            for item in ruleTup[0]:
                print(itemMeaning[item])
            print("           -------->")
            for item in ruleTup[1]:
                print(itemMeaning[item])
            print("可信度: %f" % ruleTup[2])


    if __name__ == "__main__":
        dataSet = loadDataSet()
        C1 = createC1(dataSet)  # 原始集合C1
        print("原始集合C1:", C1)
        D = list(map(set, dataSet))
        print("数据集D:", D)
        L1, supportData0 = scanD(D, C1, minSupport=0.5)
        print("频繁项集L1:", L1, "\n支持度:", supportData0)
        print("---------------完整测试-----------------")
        dataSet = loadDataSet()
        L, supportData = apriori(dataSet)
        print("频繁项集L:", L, "\n支持度: ", supportData)
        print('--------------关联规则的生成-------------')
        rules = generateRules(L, supportData, minConf=0.5)
        print("关联规则为:", rules)
```
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容