机器学习之Apriori算法

251 阅读5分钟
参与拿奖:本文已参与「新人创作礼」活动,一起开启掘金创作之路

1.相关概念

关联分析(关联规则学习):从大规模数据中寻找物品间的联系
频繁项集:经常出现在一块的物品的集合
关联规则:暗示两种物品之间存在很强的关系
Apriori算法
优点:易于编码实现
缺点:在大数据集上运行较慢
适用数据类型:数值型或者标称型

2.分析案例

2.1 数据分析

from numpy import *
import pandas as pd

dataId=[i for i in range(5)]
dataValue=['豆奶、莴苣','莴苣、尿布、葡萄酒、甜菜','豆奶、尿布、葡萄酒、橙汁','莴苣、豆奶、尿布、葡萄酒','莴苣、豆奶、尿布、橙汁']
data=pd.DataFrame()
data['交易号码']=dataId
data['商品']=dataValue

image.png 上述数据中,葡萄酒、豆奶、尿布就是一组频繁项集,可以观察出买了尿布的人很可能会买葡萄酒。进一步分析可知:

频繁项集中频繁的度量单位:支持度和可信度
一个频繁项集的支持度被定义为:数据集中包含该项集记录所占比例,是针对项集的
例如上述数据中豆奶的支持度为4/5
可信度(置信度):针对一条关联规则来的,这条关联规则的可信度被定义为两个支持度的比值(类似于条件概率)
例如关联规则尿布->葡萄酒的置信度为:支持度(尿布、葡萄酒)/支持度(尿布)
例如关联规则尿布->葡萄酒=3/4,故得出结论所有包含“尿布”的记录中75%都适用

2.2 简单案例

求解项集伪代码如下:
对于数据集中的每条交易记录train:
    对于每个候选集项can:
        检查can是否属于train的子集
        如果是子集:
            增加can的计数器
    对于每个候选集项:
        如果其支持度不小于最小度量,则保留该项集
返回所有项集列表          
def loadDataSet():
    return [[1,2,3],[1,2,3,4],[2,3],[2,3,4],[2,5]]

# 创建一个不变集合(存放所有商品类别)
def createC1(dataSet):
    c1=[]
    for trainsaction in dataSet:
        for item in trainsaction:
            if not [item] in c1:
                c1.append([item])
    c1.sort()
    # 返回一个不变集合
    # print(c1)
    return list(map(frozenset,c1))

# 输入参数分别为原始数据集、商品种类集合、最小支持度
def scanD(D,ck,minSupport):
    ssCnt={}
    for tid in D:
        for can in ck:
            if can.issubset(tid):
                if can not in ssCnt:
                    ssCnt[can]=1
                else:
                    ssCnt[can]+=1
                # 上述if-else等价于:
                # ssCnt[can]=ssCnt.get(cant,0)+1
    numItem=float(len(D))
    retList=[]
    supportData={}
    for key in ssCnt:
        # 计算所有项集的支持度
        support=ssCnt[key]/numItem
        # 根据支持度划分项集子集
        if support>= minSupport:
            retList.insert(0,key)
        supportData[key]=support
    return retList,supportData
                
   
def test():
    data=loadDataSet()
    ck=createC1(data)
    print('商品种类为:',ck)
    retList,supportData=scanD(data,ck,0.5)
    print('单商品频繁项集为',retList)
    print('单商品支持度为:',supportData)
    print('从上述数据可以根据设置最小支持度过滤掉部分数据,减少后续计算程度!')
test()

2.3 Apriori算法

Apriori算法伪代码如下:
当集合项中的个数大于0时:
    构建一个k个项组成的候选项集的列表
    检查数据以确认每个项集都是频繁的
    保留频繁集项并构建k+1项组成的候选集的列表
def loadDataSet():
    return [[1,2,3],[1,2,3,4],[1,2,3,4],[2,5],[1,2,3,4]]

# 该函数负责将Lk中的元素分解为不重复的子集所组成的列表
def aprioriGen(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()
            # print(L1,L2)
            # 如果两个列表相等就合并
            if L1==L2:
                retList.append(Lk[i]|Lk[j])
    return retList


def apriori(dataSet,minSupport=0.5):
    C1=createC1(dataSet)
    D=list(map(set,dataSet))
    # print("将列表元素转换为集合:",D)
    L1,supportData=scanD(D,C1,minSupport)
    L=[L1]
    k=2
    while(len(L[k-2])>0):
        ck=aprioriGen(L[k-2],k)
        Lk,supK=scanD(D,ck,minSupport)
        supportData.update(supK)
        if Lk!=None:
            L.append(Lk)
        k+=1
    return L,supportData

def test():
    dataSet=loadDataSet()
    L,supportData=apriori(dataSet,minSupport=0.5)
    print('频繁项集为:',L)
    print('频繁项集机:支持度为 ',supportData)
    
test()

image.png

2.3.1 一点发现

在计算可信度时可以注意到:如果一个项集不满足最低可信度,其子集也一定不满足
从频繁项集中挖掘关联规则:可以先从一个频繁项集开始,接着创建一个关联规则列表,右边只包含一个元素
然后对这些元素进行测试,接下来合并所有剩余规则创建一个新的规则列表,列表右边包含两个元素(该方法为分级法)

2.3.2 生成新的关联规则

def generateRules(L, supportData, minConf=0.7):  #supportData is a dict coming from scanD
    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         

def calcConf(freqSet, H, supportData, brl, minConf=0.7):
    prunedH = [] #create new list to return
    for conseq in H:
        conf = supportData[freqSet]/supportData[freqSet-conseq] #calc confidence
        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):
    m = len(H[0])
    if (len(freqSet) > (m + 1)): #try further merging
        Hmp1 = aprioriGen(H, m+1)#create Hm+1 new candidates
        Hmp1 = calcConf(freqSet, Hmp1, supportData, brl, minConf)
        if (len(Hmp1) > 1):    #need at least two sets to merge
            rulesFromConseq(freqSet, Hmp1, supportData, brl, minConf)

def test():
    data=loadDataSet()
    print('在当前数据集上生成一个置信度大于0.7,且其项集支持度大于0.5的关联规则:')
    L,supportData=apriori(data,minSupport=0.5)
    rules=generateRules(L,supportData,minConf=0.7)
    print('关联规则为:',rules)

test()

image.png

3.小结

关联分析是用于发现大数据集中元素间有趣关系的一个工具集。
可以适用频繁项集来对这种关系进行量化,但直接计算组合会耗费大量时间,可以采用apriori方法。
可以适用支持度和可信度来生成一条关联规则。

参考资料

[1] 机器学习实战

[2] 书籍源码

[3] jupyter版本

[4] 本节代码