zoukankan      html  css  js  c++  java
  • 机器学习实战---使用Apriori算法进行关联分析

    一:参考资料

    (一)机器学习实战或者见https://blog.csdn.net/qq_36523839/article/details/82191677

    (二)Apriori算法是什么?适用于什么情境?(更好的理解关联规则)

    (三)python中set和frozenset方法和区别

    二:实现Apriori算法中的辅助函数

    (一)加载数据

    #1.加载数据
    def loadDataSet():
        return [[1,3,4],[2,3,5],[1,2,3,5],[2,5]]    #模拟了多个(4)订单,每个订单中购买了不同的商品

    (二)创建C1集合,是对各个订单数据统一统计,返回唯一的不重复的商品号

    #2.创建C1集合,是对各个订单数据统一统计,返回唯一的不重复的商品号
    def createC1(dataSet):
        C1 = []     #先设置为列表,表示可以改变  #注意:我们在C1列表中存放的还是列表,不是直接的数据
        for dts in dataSet:
            for dt in dts:
                if not [dt] in C1:  #注意:我们在C1列表中存放的还是列表,不是直接的数据
                    C1.append([dt])
    
        C1.sort()   #进行排序操作
        return list(map(frozenset,C1))    #返回不可变集合(以为不可变集合可以作为字典键值使用),使用list将map转list

    (三)根据数据集、和我们在createC1函数中获取的候选项集列表Ck,以及我们设置的支持度。返回满足最小支持度要求的集合

    #3.根据数据集、和我们在createC1函数中获取的候选项集列表Ck,以及我们设置的支持度。返回满足最小支持度要求的集合
    def scanD(D,Ck,minSupport): #D是数据集,由loadDataSet获取
        ssCnt = {}
    
        #实现统计出现各个商品在多少个订单中出现
        for tid in D:   #遍历所有订单
            for can in Ck:  #遍历所有的商品号,是不可变集合类型
                if can.issubset(tid):   #如果是订单的子集,表示出现在一个订单中,可以+1计数
                    if not can in ssCnt:    #py3没有has_key方法
                    # if not ssCnt.has_key(can):  #注意:这就是我们要的不可变集合使用位置,是要作为字典的键
                        ssCnt[can] = 1
                    else:
                        ssCnt[can] += 1
    
        #计算各个商品的支持度,返回满足最小支持度的商品号
        numItems = len(D)   #计算所有订单数
        retList = [] #返回所有满足最小支持度的商品号
        supportData = {}    #返回所有商品的支持度
    
        for key in ssCnt.keys():
            support = ssCnt[key] / numItems
            if support >= minSupport:
                retList.append(key)
            supportData[key] = support
    
        return retList,supportData

    三:Apriori算法实现,获取候选频繁项集

    (一)实现将多个列表合并(辅助Apriori算法):具体就是将多个n维列表合并维多个n+1维的列表

    #二:Apriori算法实现
    #1.实现将多个列表合并:具体就是将多个n维列表合并维多个n+1维的列表
    #重点:我们要将多个n维列表合并维多个n+1维的列表,那么我们必须保证要合并的两个列表前n-1个数据是相同的,合并后才会变为n+1维
    def aprioriGen(Lk,K):   #实现合并,创造新的CK。注意:这里的K是只我们合并后列表的长度,Lk的实际长度是K-1
        retList = []
        lenLK = len(Lk)
        for i in range(lenLK): #进行遍历合并
            for j in range(i+1,lenLK):
                L1 = list(Lk[i])[:K-2] #Lk的实际长度是K-1,所以我们只需要设置Lk的K-1-1即可
                L2 = list(Lk[j])[:K-2]
                L1.sort()   #之所以我们在这里进行排序,因为在createC1初始数据中,已经排序完成,后面新加入的数据也是自动排序的
                L2.sort()   #注释之后无影响
                if L1==L2:
                    retList.append(Lk[i] | Lk[j])   #注意对于frozenset类型,我们可以使用|进行并集操作,并且并集后的数据是排序后的
    
        return retList  #返回合并后维n+1维的列表数据
    

    (二)实现Apriori算法

    #2.实现Apriori算法
    def apriori(dataSet,minSupport=0.5):
        #先获取初始数据
        C1 = createC1(dataSet)
        #获取数据
        D = list(map(set,dataSet))  #注意:我们需要使用list将数据从map转换为list
        #进行一次裁剪
        L1,supportData = scanD(D,C1,minSupport)
    
        #开始进行循环处理
        L = [L1]    #将每一个维度的列表都放入列表中
        K = 2
        while len(L[K-2]) > 0: #从LK[0]即列表长度为1开始
            Ck = aprioriGen(L[K-2],K)   #进行合并操作
            Lk,supportNewData = scanD(D,Ck,minSupport)  #进行新的裁剪
            #更新新的维度数据Lk的支持度
            supportData.update(supportNewData)  #对于没有的n+1维长度的列表,进行添加
            L.append(Lk)    #添加操作
            K += 1
        #注意:对于上面循环中,我们扩展到最后可能会出现,由于scanD方法中对于支持度较低的数据进行过滤,导致后面出现返回的Lk为空集,但是却更新了一次支持度(对最后面的n维列表更新)
        #所以必然L最后为[],导致len(LK[K-2])==0,退出循环
        return L,supportData

    (三)测试上述代码

    #测试
    dataset = loadDataSet()
    L,supportData = apriori(dataset,0.7)

    L,supportData = apriori(dataset,0.5)
    [[frozenset({1}), frozenset({3}), frozenset({2}), frozenset({5})], [frozenset({1, 3}), frozenset({2, 3}), frozenset({3, 5}), frozenset({2, 5})], [frozenset({2, 3, 5})], []]

    四:基于上面的候选频繁项集,实现关联规则获取

    (一)关联规则主函数实现

    #三:从上面获取的频繁项集中挖掘出关联规则https://www.zhihu.com/question/19912364/answer/963934469?utm_source=wechat_session
    #注意:frozenset不可变集合是可以作为dict重点的key处理 a =frozenset({2, 5}) b = {a:1}
    #注意:frozenset可以进行多种运算,比如|,-...
    
    #1.关联规则生成函数
    def generateRules(L,supportData,minConf=0.7):   #L是我们上面apriori(dataset,0.7)求解的频繁项集,supportData是所有数据集的支持度信息,minConf是置信度
        bigRuleList = []    #存放关联规则 注意:对于列表,字典等类型,通过函数可以进行修改
    
        for i in range(1,len(L)):   #我们只对列表有两个及以上的元素的集合进行关联处理(单个没有关联)比如i=1时:[frozenset({2, 5}),frozenset({3, 4})]
            for freqset in L[i]:    #遍历集合中元素所有都是含有i+1个元素的列表的数据信息    frozenset({2, 5})
                H1 = [frozenset([item]) for item in freqset]    #注意:对应frozenset初始化,必须使用[]或者{}   [frozenset({2}),frozenset({5})]
                if i > 1:   #对于i>1,即集合中每个元素列表长度大于2,我们的规则可以有多种方法[a,b,c]  a=>bc ac=>b ...,所以我们将其封装为一个函数
                    rulesFromConseq(freqset,H1,supportData,bigRuleList,minConf)
                else:   #对于i=1时,表示有2两个元素在集合列表中,我们只包含一个映射[a,b]   a=>b b=>a   单个元素之间的映射(通过calConf可以实现这一种映射)
                    calConf(freqset,H1,supportData,bigRuleList,minConf) #计算置信度
                #注意:calConf可以看做rulesFromConseq的辅助函数
        return bigRuleList

    (二)实现置信度求解函数

    #2.辅助函数,计算置信度
    def calConf(freqset,H1,supportData,bigRuleList,minConf):
        prunedH = []    #保存置信度
        for conseq in H1:
            #置信度confidence=P(B|A)=P(AB)/P(A)),指的是发生事件A的基础上发生事件B的概率(相当与条件概率)。
            conf = supportData[freqset] / supportData[freqset-conseq]   #计算置信度看机器学习实战
            if conf >= minConf: #大于最小置信度,打印并保存
                print(freqset-conseq,"---->",conseq,conf)   #打印前提条件,对于结果,置信度
                bigRuleList.append((freqset-conseq,conseq,conf))    #保存规则
                prunedH.append(conseq)  #保存本次置信度检查中,满足最小置信度要求的规则,但是保存不全,后面也没有使用,应该不需要
    
        return prunedH

    (三)从初始的候选项集中,生成更多的数据集,进行关联规则查看,借助上面的函数计算置信度,也可以将两个函数合二为一

    #3.从初始的候选项集中,生成更多的数据集,进行关联规则查看,借助上面的函数计算置信度,也可以将两个函数合二为一
    def rulesFromConseq(freqset,H1,supportData,bigRuleList,minConf=0.7):    #注意:在递归过程中,freqset不会变化,H1会随着合并增加
        m = len(H1[0])  #查看当前输入的列表中,每个不可变集合元素个数
        #查看频繁项是否可以大到可以移除大小为m的子集
        if len(freqset) >= (m+1):    #如果我们传入的原始freqset长度>m+1则可以正常递归,假设同上假设fregset为frozenset({2, 5}),H1为[frozenset({2}), frozenset({5})]
            #重点注意:len(freqset) > (m+1)中,如果len(freqset)=3,m=2呢???
            #因此对比书本代码进行修改,使得可以出现a,b=>c的情况
            Hmp1 = calConf(freqset,H1,supportData,bigRuleList,minConf)    #根据我们合并的新的集合,进行置信度检测
            Hmp1 = aprioriGen(Hmp1,m+1)   #进行合并操作
            if len(Hmp1) > 1:   #继续递归,直到我们的函数不满足len(freqset) > (m+1)时退出
                rulesFromConseq(freqset,Hmp1,supportData,bigRuleList,minConf)
    

    (四)进行测试

    L,supportData = apriori(dataset)
    rules = generateRules(L,supportData,0.5)

    (五)全部代码

    import numpy as np
    
    #一:实现Apriori算法中的辅助函数
    #1.加载数据
    def loadDataSet():
        return [[1,3,4],[2,3,5],[1,2,3,5],[2,5]]    #模拟了多个(4)订单,每个订单中购买了不同的商品
    
    #2.创建C1集合,是对各个订单数据统一统计,返回唯一的不重复的商品号
    def createC1(dataSet):
        C1 = []     #先设置为列表,表示可以改变  #注意:我们在C1列表中存放的还是列表,不是直接的数据
        for dts in dataSet:
            for dt in dts:
                if not [dt] in C1:  #注意:我们在C1列表中存放的还是列表,不是直接的数据
                    C1.append([dt])
    
        C1.sort()   #进行排序操作
        return list(map(frozenset,C1))    #返回不可变集合(以为不可变集合可以作为字典键值使用),使用list将map转list
    
    #3.根据数据集、和我们在createC1函数中获取的候选项集列表Ck,以及我们设置的支持度。返回满足最小支持度要求的集合
    def scanD(D,Ck,minSupport): #D是数据集,由loadDataSet获取
        ssCnt = {}
    
        #实现统计出现各个商品在多少个订单中出现
        for tid in D:   #遍历所有订单
            for can in Ck:  #遍历所有的商品号,是不可变集合类型
                if can.issubset(tid):   #如果是订单的子集,表示出现在一个订单中,可以+1计数
                    if not can in ssCnt:    #py3没有has_key方法
                    # if not ssCnt.has_key(can):  #注意:这就是我们要的不可变集合使用位置,是要作为字典的键
                        ssCnt[can] = 1
                    else:
                        ssCnt[can] += 1
    
        #计算各个商品的支持度,返回满足最小支持度的商品号
        numItems = len(D)   #计算所有订单数
        retList = [] #返回所有满足最小支持度的商品号
        supportData = {}    #返回所有商品的支持度
    
        for key in ssCnt.keys():
            support = ssCnt[key] / numItems
            if support >= minSupport:
                retList.append(key)
            supportData[key] = support
    
        return retList,supportData
    
    #二:Apriori算法实现
    #1.实现将多个列表合并:具体就是将多个n维列表合并维多个n+1维的列表
    #重点:我们要将多个n维列表合并维多个n+1维的列表,那么我们必须保证要合并的两个列表前n-1个数据是相同的,合并后才会变为n+1维
    def aprioriGen(Lk,K):   #实现合并,创造新的CK。注意:这里的K是只我们合并后列表的长度,Lk的实际长度是K-1
        retList = []
        lenLK = len(Lk)
        for i in range(lenLK): #进行遍历合并
            for j in range(i+1,lenLK):
                L1 = list(Lk[i])[:K-2] #Lk的实际长度是K-1,所以我们只需要设置Lk的K-1-1即可
                L2 = list(Lk[j])[:K-2]
                L1.sort()   #之所以我们在这里进行排序,因为在createC1初始数据中,已经排序完成,后面新加入的数据也是自动排序的
                L2.sort()   #注释之后无影响
                if L1==L2:
                    retList.append(Lk[i] | Lk[j])   #注意对于frozenset类型,我们可以使用|进行并集操作,并且并集后的数据是排序后的
    
        return retList  #返回合并后维n+1维的列表数据
    
    #2.实现Apriori算法
    def apriori(dataSet,minSupport=0.5):
        #先获取初始数据
        C1 = createC1(dataSet)
        #获取数据
        D = list(map(set,dataSet))  #注意:我们需要使用list将数据从map转换为list
        #进行一次裁剪
        L1,supportData = scanD(D,C1,minSupport)
    
        #开始进行循环处理
        L = [L1]    #将每一个维度的列表都放入列表中
        K = 2
        while len(L[K-2]) > 0: #从LK[0]即列表长度为1开始
            Ck = aprioriGen(L[K-2],K)   #进行合并操作
            Lk,supportNewData = scanD(D,Ck,minSupport)  #进行新的裁剪
            #更新新的维度数据Lk的支持度
            supportData.update(supportNewData)  #对于没有的n+1维长度的列表,进行添加
            L.append(Lk)    #添加操作
            K += 1
        #注意:对于上面循环中,我们扩展到最后可能会出现,由于scanD方法中对于支持度较低的数据进行过滤,导致后面出现返回的Lk为空集,但是却更新了一次支持度(对最后面的n维列表更新)
        #所以必然L最后为[],导致len(LK[K-2])==0,退出循环
        return L,supportData
    
    #测试
    dataset = loadDataSet()
    # L,supportData = apriori(dataset,0.5)
    # print(L)    #[[frozenset({1}), frozenset({3}), frozenset({2}), frozenset({5})], [frozenset({1, 3}), frozenset({2, 3}), frozenset({3, 5}), frozenset({2, 5})], [frozenset({2, 3, 5})], []]
    
    #三:从上面获取的频繁项集中挖掘出关联规则https://www.zhihu.com/question/19912364/answer/963934469?utm_source=wechat_session
    #注意:frozenset不可变集合是可以作为dict重点的key处理 a =frozenset({2, 5}) b = {a:1}
    #注意:frozenset可以进行多种运算,比如|,-...
    
    #1.关联规则生成函数
    def generateRules(L,supportData,minConf=0.7):   #L是我们上面apriori(dataset,0.7)求解的频繁项集,supportData是所有数据集的支持度信息,minConf是置信度
        bigRuleList = []    #存放关联规则 注意:对于列表,字典等类型,通过函数可以进行修改
    
        for i in range(1,len(L)):   #我们只对列表有两个及以上的元素的集合进行关联处理(单个没有关联)比如i=1时:[frozenset({2, 5}),frozenset({3, 4})]
            for freqset in L[i]:    #遍历集合中元素所有都是含有i+1个元素的列表的数据信息    frozenset({2, 5})
                H1 = [frozenset([item]) for item in freqset]    #注意:对应frozenset初始化,必须使用[]或者{}   [frozenset({2}),frozenset({5})]
                if i > 1:   #对于i>1,即集合中每个元素列表长度大于2,我们的规则可以有多种方法[a,b,c]  a=>bc ac=>b ...,所以我们将其封装为一个函数
                    rulesFromConseq(freqset,H1,supportData,bigRuleList,minConf)
                else:   #对于i=1时,表示有2两个元素在集合列表中,我们只包含一个映射[a,b]   a=>b b=>a   单个元素之间的映射(通过calConf可以实现这一种映射)
                    calConf(freqset,H1,supportData,bigRuleList,minConf) #计算置信度
                #注意:calConf可以看做rulesFromConseq的辅助函数
        return bigRuleList
    
    #2.辅助函数,计算置信度
    def calConf(freqset,H1,supportData,bigRuleList,minConf):
        prunedH = []    #保存置信度
        for conseq in H1:
            #置信度confidence=P(B|A)=P(AB)/P(A)),指的是发生事件A的基础上发生事件B的概率(相当与条件概率)。
            conf = supportData[freqset] / supportData[freqset-conseq]   #计算置信度看机器学习实战
            if conf >= minConf: #大于最小置信度,打印并保存
                print(freqset-conseq,"---->",conseq,conf)   #打印前提条件,对于结果,置信度
                bigRuleList.append((freqset-conseq,conseq,conf))    #保存规则
                prunedH.append(conseq)  #保存本次置信度检查中,满足最小置信度要求的规则,但是保存不全,后面也没有使用,应该不需要
    
        return prunedH
    
    #3.从初始的候选项集中,生成更多的数据集,进行关联规则查看,借助上面的函数计算置信度,也可以将两个函数合二为一
    def rulesFromConseq(freqset,H1,supportData,bigRuleList,minConf=0.7):    #注意:在递归过程中,freqset不会变化,H1会随着合并增加
        m = len(H1[0])  #查看当前输入的列表中,每个不可变集合元素个数
        #查看频繁项是否可以大到可以移除大小为m的子集
        if len(freqset) >= (m+1):    #如果我们传入的原始freqset长度>m+1则可以正常递归,假设同上假设fregset为frozenset({2, 5}),H1为[frozenset({2}), frozenset({5})]
            #重点注意:len(freqset) > (m+1)中,如果len(freqset)=3,m=2呢???
            #因此对比书本代码进行修改,使得可以出现a,b=>c的情况
            Hmp1 = calConf(freqset,H1,supportData,bigRuleList,minConf)    #根据我们合并的新的集合,进行置信度检测
            Hmp1 = aprioriGen(Hmp1,m+1)   #进行合并操作
            if len(Hmp1) > 1:   #继续递归,直到我们的函数不满足len(freqset) > (m+1)时退出
                rulesFromConseq(freqset,Hmp1,supportData,bigRuleList,minConf)
    
    L,supportData = apriori(dataset)
    rules = generateRules(L,supportData,0.5)
    View Code

    五:总结

    Apriori算法简单,易于实现。但是它也有自己的缺点,数据集很大的时会出现下面两个问题。

    1. 需要多次扫描数据集

    例如在Apriori算法循环中:即每次增加新的频繁项集的时候,Apriori算法都会重新扫描整个数据集

    Lk,supportNewData = scanD(D,Ck,minSupport)  #进行新的裁剪

    或者后面求解的支持度(针对每个数据都存在支持度),也存在多次扫描

    2. 可能会产生庞大的候选集

    当数据极大,支持度设置太小时

    3.FP-growth算法引出

    针对Apriori算法的性能瓶颈问题,2000年Jiawei Han等人提出了基于FP树生成频繁项集的FP-growth算法。该算法只进行2次数据库扫描且它不使用侯选集,直接压缩数据库成一个频繁模式树,最后通过这棵树生成关联规则。

    研究表明它比Apriori算法大约快一个数量级。

  • 相关阅读:
    什么是回归测试?
    单元测试、集成测试、系统测试的侧重点是什么?
    一个测试工程师应具备那些素质?
    你所了解的的软件测试类型都有哪些,简单介绍一下。
    Python
    爬虫-urllib3模块的使用
    爬图片(附,源码)
    MySQL存储引擎:MyISAM和InnoDB的区别
    员工管理系统
    MySQL锁(二)表锁:为什么给小表加字段会导致整个库挂掉?
  • 原文地址:https://www.cnblogs.com/ssyfj/p/13401774.html
Copyright © 2011-2022 走看看