MachineLearning-Apriori(关联分析) FP-growth算法

Apriori算法是一种挖掘关联规则的频繁项集算法,FP-growth,又称为FP-增长算法,它比Apriori算法要快,它基于Apriori构建。关联分析在数据挖掘当中有非常经典的成功案例,著名的啤酒尿布就来自于关联算法的贡献。关联分析体现在两个方面:

1.频繁项集(frequency item sets):经常同时出现的一些元素的集合。

2.关联规则(association rules): 意味着两种元素之间存在很强的关系。

交易号码 商品
0 豆奶, 甘蓝
1 甘蓝,尿布,葡萄酒,甜菜
2 甘蓝,尿布,葡萄酒,橙汁
3 甘蓝,豆奶,尿布,葡萄酒
4 甘蓝,豆奶,尿布,橙汁

如上所示的购物清单列表,{尿布,葡萄酒}更为频繁的出现在每条清单中,所以{尿布,葡萄酒}就是一个频繁项集。同时也可以得到一些关联规则例如:尿布—>葡萄酒,意味着买尿布的人同时也有较大的可能性买葡萄酒。那么究竟该如何定义频繁?关联?,这里引入两个重要概念

(一)支持度:一个项集的支持度(support)定义为数据集中包含该项集的记录占总记录的比例。表中可以看出项集 {豆奶} 的支持度为3/5;{豆奶,尿布} 的支持度为2/5

(二)可信度置信度(confidence):是专门为尿布—>葡萄酒这样的关联规则定义的,这条规则的可信度定义为“ 支持度({尿布,葡萄酒})  /  支持度({尿布})”。表中可以发现  {尿布,葡萄酒} 的支持度是3/5,{尿布} 的支持度为4/5, 所以关联规则 “尿布—>葡萄酒”的可信度为3/4=0.75,也就是说对于所有包含 "尿布"的记录中,该关联规则对其中的 75% 记录都适用。


Apriori原理:假设这里有商品0,1,2,3 可以通过以下的方式获得商品的不同组合

MachineLearning-Apriori(关联分析) FP-growth算法_第1张图片

我们的目标是找到经常在一起购买的物品集合。这里使用集合的支持度来度量其出现的频率。一个集合出现的支持度是指有多少比例的交易记录包含该集合。而使用Apriori算法可以降低计算量,减少遍历数据的频次。Apriori原理:一个频繁项集它所有的子集也是频繁的,即如果 {0,1} 是频繁的,那么 {0}, {1} 也一定是频繁的。同时反过来讲,如果一个项集是非频繁的,那么它所有的超集也是非频繁的。这样就省下了部分项集超集的计算。

算法过程描述:
首先会生成所有单个物品的项集列表;
扫描交易记录来查看哪些项集满足最小支持度要求,去掉不满足最小支持度的集合;
对剩下的集合进行组合以生成包含两个元素的项集;
接下来重新扫描交易记录,去掉不满足最小支持度的项集,重复进行直到所有项集都被去掉。

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

def createC1( dataSet ): #C1是大小为1的所有候选项集的集合
    C1 = []
    for transaction in dataSet:
        for item in transaction:
            if [ item ] not in C1:
                C1.append( [ item ] )
    C1.sort()
    return list(map( frozenset, C1 ))

#计算Ck中的项集在数据集合D中的支持度
#返回满足最小支持度的项集的集合,和所有项集支持度信息的字典
def scanD( D, Ck, minSupport ):
    ssCnt = {}
    for tid in D:
        #对于每一条transaction
        for can in Ck:
            if can.issubset( tid ):#对于每一个候选项集can,检查是否是transaction的一部分,即该候选can是否得到transaction的支持
                ssCnt[ can ] = ssCnt.get( can, 0) + 1  #当键不存在时赋值为0
    numItems = float(len(D))
    retList = []
    supportData = {}
    for key in ssCnt:
        #每个项集的支持度
        support = ssCnt[ key ] / numItems        
        #将满足最小支持度的项集,加入retList
        if support >= minSupport:
            retList.insert( 0, key )
        
        #汇总支持度数据
        supportData[ key ] = support
    return retList, supportData#返回所有的频繁项集和支持度信息

if __name__ == '__main__':
    myDat = loadDataSet()
    C1 = createC1( myDat )
    #构建集合表示的数据集D
    D = list(map( set, myDat ))
    L, suppData = scanD( D, C1, 0.5 )  #注意:map类型的D,C1都在迭代一次之后变为空!
    print("频繁项集L:", L)
    print("所有候选项集的支持度信息:", suppData)
频繁项集L: [frozenset({5}), frozenset({2}), frozenset({3}), frozenset({1})]
所有候选项集的支持度信息: {frozenset({1}): 0.5, frozenset({3}): 0.75, frozenset({4}): 0.25, frozenset({2}): 0.75, frozenset({5}): 0.75}

上面我们只是选出了项集中只包含一个元素的频繁项集,下面整合上面的代码,选出包含 2个,3个直至包含所有候选元素的频繁项集。

def aprioriGen( Lk, k ):
    #由初始候选项集的集合L1生成新的候选项集,
    #k表示生成的新项集中包含的元素个数
    retList = []
    lenLk = len( Lk )
    for i in range( lenLk ):
        for j in range( i + 1, lenLk ):
            L1 = list( Lk[ i ] )[ : k - 2 ]#aprioriGen(L1,2)的情况下,L1=L2=[],aprioriGen(L2,3)情况下,第一个元素相等时才合并
            L2 = list( Lk[ j ] )[ : k - 2 ]
            L1.sort();L2.sort()
            if L1 == L2:
                retList.append( Lk[ i ] | Lk[ j ] ) 
    return retList
#L1=[frozenset({1}), frozenset({3}), frozenset({2}), frozenset({5})]
#L2=[frozenset({1, 3}),frozenset({1, 2}),frozenset({1, 5}),frozenset({2, 3}),frozenset({3, 5}),frozenset({2, 5})]
#aprioriGen(L2,3)

def apriori( dataSet, minSupport = 0.5 ):
    C1 = createC1( dataSet )
    D = list(map( set, dataSet ))
    L1, suppData = scanD( D, C1, minSupport )#构建初始频繁项集,所有项集只有一个元素
    L = [ L1 ]
    k = 2  #最初的L1中每个项集只包含一个元素,新生成的项集应该含有2个元素,所以 k=2
    while (len( L[ k - 2 ]) > 0):
        Ck = aprioriGen( L[ k - 2 ], k ) #产生新的候选项集
        Lk, supK = scanD( D, Ck, minSupport ) #返回频繁项集和支持度信息
        suppData.update( supK )
        L.append( Lk )
        k += 1#新生成的项集中的元素个数应不断增加
    return L, suppData   #返回所有满足条件的频繁项集的列表,所有候选项集的支持度信息

if __name__ == '__main__':
    myDat = loadDataSet()    
    L, suppData = apriori( myDat, 0.5 )
    print ("频繁项集L:", L)
    print ("所有候选项集的支持度信息:", suppData)
频繁项集L: [[frozenset({5}), frozenset({2}), frozenset({3}), frozenset({1})], [frozenset({2, 3}), frozenset({3, 5}), frozenset({2, 5}), frozenset({1, 3})], [frozenset({2, 3, 5})], []]
所有候选项集的支持度信息: {frozenset({1}): 0.5, frozenset({3}): 0.75, frozenset({4}): 0.25, frozenset({2}): 0.75, frozenset({5}): 0.75, frozenset({1, 3}): 0.5, frozenset({2, 5}): 0.75, frozenset({3, 5}): 0.5, frozenset({2, 3}): 0.5, frozenset({1, 5}): 0.25, frozenset({1, 2}): 0.25, frozenset({2, 3, 5}): 0.5}

注意:aprioriGen(L2,3)由二元素的两两合并为三元素的,只有当L2中元素的第一个成员相同才合并。aprioriGen(L3,4)由三元素的两两合并为四元素的,只有当L3中元素的第一二个成员相同才合并。[ : k-2]的作用就在于此!

挖掘关联规则:从上面的购物清单发现,购买豆奶的同时购买葡萄酒的概率往往较高,即{豆奶}—>{葡萄酒},但反过来不一定成立。那么可以产生多少条关联规则呢?首先基于该频繁项集生成一个可能的规则列表,然后测试每条规则的可信度,如可信度不满足最小值要求,则去掉该规则。类似于前面讨论的频繁项集生成,如果某条规则并不满足最小可信度要求,那么该规则的所有子集也不会满足最小可信度要求。如图:{0,1,2}—>{3}是一条低可信度的关联规则,则所有包含3为结论的规则都是低可信度的。

MachineLearning-Apriori(关联分析) FP-growth算法_第2张图片

#挖掘关联规则,根据频繁项集和最小可信度生成规则
'''
[[frozenset({5}), frozenset({2}), frozenset({3}), frozenset({1})], 
[frozenset({2, 3}), frozenset({3, 5}), frozenset({2, 5}), frozenset({1, 3})], 
[frozenset({2, 3, 5})], []]
'''
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:    #如果频繁项集中的元素个数大于2,需要进一步合并
                rulesFromConseq(freqSet, H1, supportData, bigRuleList, minConf)
            else:
                calcConf(freqSet, H1, supportData, bigRuleList, minConf)
    return bigRuleList

def calcConf(freqSet, H, supportData, brl, minConf=0.7):
    '''
    计算规则的可信度,返回满足最小可信度的规则    
    H(frozenset):频繁项集中所有的元素
    brl(tuple):满足可信度条件的关联规则
    '''
    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):
    '''
    这里包含Apriori的一个核心思想:
    {0,1,2}—>{3}是一条低可信度的关联规则,则所有包含3为结论的规则都是低可信度的。用来减少计算量
    H(frozenset):频繁项集中的所有元素,即可以出现在规则右部的元素
    brl(tuple):生成的规则
    '''
    m=len(H[0])
    if len(freqSet) > m + 1:
        Hmp1 = aprioriGen( H, m + 1 )#生成候选项集,H={2}{3}{5}
        Hmp1 = calcConf(freqSet, Hmp1, supportData, brl, minConf)
        #calcConf可以得到后件,即通过关联规则能够推出的结果,这个地方体现了Apriori算法的一个核心思想
        if len( Hmp1 ) > 1:  #Hmp1是一个列表,里面包含{2,5},{2,3}
            rulesFromConseq(freqSet, Hmp1, supportData, brl, minConf)

if __name__ == '__main__':
    myDat = loadDataSet()    
    L, suppData = apriori(myDat, 0.5)   
    rules = generateRules(L, suppData, minConf=0.7)
    print ('rules:\n', rules)

输出关联规则结果:

frozenset({5}) --> frozenset({2}) conf: 1.0
frozenset({2}) --> frozenset({5}) conf: 1.0
frozenset({1}) --> frozenset({3}) conf: 1.0
rules:
 [(frozenset({5}), frozenset({2}), 1.0), (frozenset({2}), frozenset({5}), 1.0), (frozenset({1}), frozenset({3}), 1.0)]


参考内容:https://www.manning.com/books/machine-learning-in-action

                https://www.cnblogs.com/90zeng/p/apriori.html

你可能感兴趣的:(Machine,Learning)