机器学习实战---使用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算法大约快一个数量级。

posted @ 2020-07-30 08:51  山上有风景  阅读(720)  评论(0编辑  收藏  举报