机器学习(三)——Apriori算法进行关联分析

一. 关联分析

关联分析是一种在大规模数据集上寻找有趣关系的任务。这些关系可以有两种形式:频繁项集与关联规则。频繁项集是经常出现在一块的物品的集合;关联规则暗示两种物品之间可能存在很强的关系。如何量化的评价频繁项集和关联规则呢?由此引出了支持度(support)和置信度(confidence)。

                                            

1.1 支持度:

一个项集的支持度(support)被定义为数据集中包含该项集的记录所占的比例。从上图可以得到,{豆奶}的支持度为4/5。而在5条交易记录中有3条包含{豆奶,尿布},因此{豆奶,尿布}的支持度为3/5。支持度是针对项集来说的,因此可以定义一个最小支持度,而只保留满足最小支持度的项集。

1.2 置信度:

置信度(confidence)是针对一条诸如{尿布}→{葡萄酒}的关联规则来定义的,这条规则的可信度被定义为“支持度({尿布,葡萄酒})/支持度({尿布})”。从上图可以看出,由于{尿布,葡萄酒}的支持度为3/5,{尿布}的支持度为4/5,所以“尿布→葡萄酒”的置信度(3/5)/(4/5)=0.75

Confidence(A->B) = support({A,B}) / support({A}) = P(B|A)

二. Apriori原理

前面说过,我们的目标是找到经常在一起购买的物品集合。我们使用集合的支持度来度量其出现的频率。一个集合的支持度是指有多少比例的交易记录包含该集合。如果对一个给定的集合,比如{0,3},计算其支持度,我们遍历每条交易记录并检查记录中包含0和3的记录,如果包含,就增加总计数值。这只是计算单个集合的,若要获得所有的可能集合的支持度就需要多次重复上诉过程。

为了降低所需的计算时间,需要用到Apriori原理。Apriori原理可以帮助我们减少可能感兴趣的项集。

Apriori原理是说如果某个项集是频繁的,那么它的所有子集也是频繁的,这个原理直观上并没有什么帮助,但是如果反过来看就很有用了,也就是说如果一个项集是非频繁集,那么它的所有超集也是非频繁的。如下图:

                                           

已知阴影项集{2,3}是非频繁的。利用Apriori原理,我们就知道项集{0,2,3},{1,2,3}及{0,1,2,3}也是非频繁的。也就是说,一旦计算出{2,3}的支持度,如小于支持度的阈值,就可以判定它是非频繁的,就不需要再计算{0,2,3},{1,2,3}及{0,1,2,3}的支持度。使用该原理就可以避免项集数目的指数增长,节省计算时间。

三. Apriori算法过程

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

Apriori算法是发现频繁项集的一种方法。

3.1 计算频繁项集

算法过程:

  1. 由数据集生成候选项集C1(1表示每个候选项仅有一个数据项);再由C1通过支持度过滤,生成频繁项集L1(1表示每个频繁项仅有一个数据项)。
  2. 将L1的数据项两两拼接成C2。
  3. 从候选项集C2开始,通过支持度过滤生成L2。L2根据Apriori原理拼接成候选项集C3;C3通过支持度过滤生成L3……直到Lk中仅有一个或没有数据项为止。

下面是一个超市的交易记录:

                           

Apriori算法发现频繁项集的过程如下:

Apriori算法的两个输入参数分别是最小支持度和数据集。该算法首先会生成所有单个物品的项集列表,接着扫描交易记录来查看哪些项集满足最小支持度要求,那些不满足最小支持度的集合会被去掉。然后。对剩下的集合进行组合以生成包含两个元素的项集。接下里,再重新扫描交易记录,去掉不满足最小支持度的项集,该过程重复进行直到所有项集都被去掉。

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

#构建第一个候选项集C1,单个物品构成的项集
def createC1(dataSet):
    c1 = []
    for transaction in dataSet:
        for item in transaction:
            if [item] not in c1:
                c1.append([item])
    c1.sort()
    return list(map(frozenset,c1))
#使用map函数,将c1中所有的元素都执行一次frozenset函数。python3的map函数的返回值已经不再是list,而是iterators, 所以想要使用,只用将iterator 转换成list即可。
#frozenset是冻结的集合,它是不可变的,好处是它可以作为字典的key,也可以作为其它集合的元素。这里必须使用frozenset而不是set类型,因为之后要将这些集合作为字典键值使用,set不能实现这一点。 
#主要得到项集列表Lk
def scanD(D,ck,minSupport):
    ssCnt = {}
    for tid in D:
        for can in ck:
            if can.issubset(tid):
                if can not in ssCnt.keys():
                    ssCnt[can]=1
                else:
                    ssCnt[can]+=1
    numItems = float(len(D))
    retList = []
    supportData = {}
    for key in ssCnt:
        support = ssCnt[key]/numItems
        if support>=minSupport:
            retList.insert(0,key)
        supportData[key]=support
    return retList,supportData  #返回的retList是符合最小支持度的项集列表,supportData是每个项集的支持度值,该值在下一步
                                #求关联关系中的置信度时要用到

测试以下:

d = loadDataSet()
c1 = createC1(d)
r,s = scanD(d,c1,0.5)

r    #符合最小支持度的项集
>>>[frozenset({5}), frozenset({2}), frozenset({3}), frozenset({1})]

s  #所有项集的支持度,用于关联关系中的置信度的计算
>>>{frozenset({1}): 0.5,
 frozenset({3}): 0.75,
 frozenset({4}): 0.25,
 frozenset({2}): 0.75,
 frozenset({5}): 0.75}

组织完整的Apriori算法的伪代码如下:

当项集中项的个数大于0时

        构建一个k个项组成的候选项集的列表

        检查数据以确认每个项集都是频繁的

        保留频繁项集并构建k+1项组成的候选项集的列表

# 创建Ck
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]  #前 k-2 个项相同时,将两个集合合并
            L2 = list(Lk[j])[:k-2]
            L1.sort()
            L2.sort()
            if L1 == L2:
                retList.append(Lk[i]|Lk[j])
    return retList  #返回的频繁项集的列表

def apriori(dataSet,minSupport=0.5):
    c1 = createC1(dataSet)
    d = dataSet
    L1,supportData = scanD(d,c1,minSupport)
    L =[L1]
    k = 2
    while len(L[k-2])>0:   #L[K-2]是L列表中的第k-2项,判断集合中项的个数是否大于0
        ck = aprioriGen(L[k-2],k)
        Lk,supk=scanD(d,ck,minSupport)
        supportData.update(supk)
        L.append(Lk)
        k+=1
    return L,supportData

返回的L是所有频繁项集组成的列表

supportData是一个字典,包含项集的支持度,现在暂时不考虑这些值,下一节计算关联规则中的置信度要用到。

3.2 从频繁项集中挖掘关联规则

我们的目标是从频繁项集中挖掘到隐藏的关联规则。要找关联规则,从频繁项集开始,根据置信度算出频繁项集中出现的哪些商品必然可以推到出当中的另外一些商品,即找出频繁项集中各商品之间的关联规则。

                          

每个频繁项集可以产生多个关联规则。如果某条规则并不满足最小可信度要求,那么该规则的所有子集也不会满足最小可信度要求。apriori算法,可以首先从一个频繁项集开始,接着创建一个规则列表,其中规则右部只包含一个元素,然后对这些规则进行测试,合并所有剩余规则来创建一个新的规则列表。

def generateRules(L,supportData,minConf=0.7):
    bigRuleList = []   #包含置信度的规则列表
    for i in range(1,len(L)):  #从频繁项集包含两个项集的开始遍历
        for freqSet in L[i]:
            H1 = [frozenset([item]) for item in freqSet]
            if i > 1:
                rulesFromConseq(freqSet,H1,supportData,bigRuleList,minConf)   #频繁项集元素个数超过 2 ,对它进一步进行合并
            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]
        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)):
        Hmp1 = aprioriGen(H,m+1)
        Hmp1 = calcConf(freqSet,Hmp1,supportData,brl,minConf)
        if len(Hmp1)>1:
            rulesFromConseq(freqSet,Hmp1,supportData,brl,minConf)

参考:http://www.cnblogs.com/bigmonkey

猜你喜欢

转载自blog.csdn.net/guoyang768/article/details/84921467