分享

机器学习理论与实战(十一)关联规则分析Apriori

 dinghj 2013-11-26

          《机器学习实战》的最后的两个算法对我来说有点陌生,但学过后感觉蛮好玩,了解了一般的商品数据关联分析和搜索引擎智能提示的工作原理。先来看看关联分析(association analysis)吧,它又称关联规则学习(association rule learning),它的主要工作就是快速找到经常在一起的频繁项,比如著名的“啤酒”和“尿布”。试想一下,给我们一堆交易数据,每次的交易数据中有不同的商品,要我们从中发掘哪些商品经常被一起购买?当然穷举法也可以解决,但是计算量很大,这节的算法Apriori就是解决此类问题的快速算法。Apriori在拉丁语中表示“from before”(来自以前)的意思,在这里就是来自于子集的频繁信息咯,不懂,别着急。下面给出几个简单的交易数据,如(表一)所示:

交易号

商品

0

豆奶

生菜

 

 

1

生菜

尿布

葡萄酒

甜菜

2

豆奶

尿布

葡萄酒

橙汁

3

生菜

豆奶

尿布

葡萄酒

4

生菜

豆奶

尿布

橙汁

                                            (表一)    商品交易数据

     我们的目标就是找到经常在一起出现的频繁子集,集合哦。我们用大括号“{}”来表示集合。为了表示某个子集是否是频繁子集,我们需要用一些量化方法,光计数也不行,因为不同量的交易数据出现的次数差别很大,一般用支持度(support)和置信度(confident)这两个指标来量化频繁子集、关联规则。这两个指标的计算都很简单:

支持度=(包含该子集的交易数目)/总交易数目

比如{豆奶}的支持度为4/5,有四条交易数据都有豆奶,而{豆奶,尿布}的支持度为3/5。

置信度只是针对像{尿布}->{葡萄酒}的 关联规则 来定义的:

尿布到葡萄酒的置信度=支持度({尿布,葡萄酒})/支持度(尿布)

比如在(表一)中,{尿布,葡萄酒}的支持度为3/5,而尿布的支持度为4/5,那么尿布->葡萄酒的可信度为3/4=0.75。

尽管有了量化指标,但是要让我们在大规模的数据中计算所有的组合的支持度和关联规则的支持度工作量也很大,如(图一)所示:


(图一) 商品{0,1,2,3}所有可能的组合

      (图一)中只有四种商品,而其子集则有2^4-1=15个(空子集除外),每计算一个子集的两个指标都需要遍历一下数据,要遍历15次,如果有100种商品,则有1.26*10^30个子集,这个计算量很大,所幸的是研究人员发现了一种Apriori原理:Apriori原理是说如果某个子集不是频繁的,那么包含它的所有超集也不是频繁的,这样一下就砍掉了一大半,如(图二)所示:


(图二)  Apriori原理

        (图二)中{2,3}不是频繁的,那么所有包含它的超子集都不是频繁的,有了这个原则,就使得我们计算频繁子集的变成可行的。有了Apriori原理作为指导,我们还需要一些trick 来实现代码,这些trickiju构成了Apriori算法:

先生成所有单个商品构成子集列表

然后计算每个子集的支持度

剔除不满足最小支持度的子集

接下来对满足要求的子集进行两两组合(但组合一定是某个交易的数据的子集)

然后再计算组合的支持度

再剔除,

依次按上述循环直到剔除完毕。

返回频繁子集

上述算法的伪代码如(图三)所示:

(图三)

下面直接给出代码,没有公式,就根据上面的伪代码,读者都可以尝试自己写代码:

from numpy import *

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

def createC1(dataSet):
    C1 = []
    for transaction in dataSet:
        for item in transaction:
            if not [item] in C1:
                C1.append([item])
                
    C1.sort()
    return map(frozenset, C1)#use frozen set so we
                            #can use it as a key in a dict    

def scanD(D, Ck, minSupport):
    ssCnt = {}
    for tid in D:
        for can in Ck:
            if can.issubset(tid):
                if not ssCnt.has_key(can): 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

def aprioriGen(Lk, k): #creates Ck
    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: #if first k-2 elements are equal
                retList.append(Lk[i] | Lk[j]) #set union
    return retList

def apriori(dataSet, minSupport = 0.5):
    C1 = createC1(dataSet)
    D = map(set, dataSet)
    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)#scan DB to get Lk
        supportData.update(supK)
        L.append(Lk)
        k += 1
    return L, supportData

另外多说一点关于关联规则的挖掘,有了频繁子集,就是挖掘关联规则,和频繁子集一样的道理,只不过改计算置信度(confident),计算方法也同上,如(图四)所示:

(图四) 关联规则挖掘

      (图四)和(图二)中的否定关系是反过来的,关联规则挖掘的原则是:如果某个关联规则不满足最小置信度,那么它的所有子集都要被舍去,都是不满足最小置信度的,这点并不是绝对的,读者可以根据自己的需求个性化修改。代码如下:

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 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 "confidence: %f" % ruleTup[2]
        print       #print a blank line

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多