使用Apriori算法进行关联分析

关联分析力图在大规模数据集中寻找关系,这些关系有两种形式:频繁项集、关联规则。

频繁项集:经常出现在一块的物品的集合

关联规则:两种物品之间可能存在的关系

支持度:数据集上包含该项集的记录所占的比例

可信度/置信度:针对物品A-->物品B的关联规则来定义。物品A-->物品B的置信度 = 支持度(A | B)/支持度(A)

Apriori算法原理:如果某个项集是频繁的,则它的所有子集也是频繁的。==》如果某个项集是非频繁的,则它的所有超集也是非频繁的。

寻找频繁项集:

辅助函数:

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 list(map(frozenset, C1))   #frozenset创建不可变集合
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
    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

 完整的Apriori算法

def aprioriGen(Lk, k):   #参数为当前频繁项集列表,需要生成的新的频繁项集的元素个数。因为每次生成的新的项元素个数比当前多1,所以[:k-2]表示当前项除了最后一个元素,即为[:,-1]
    retList = []
    print(Lk)
    print('k:',k)
    lenLk = len(Lk)
    for i in range(lenLk):
        for j in range(i+1, lenLk):
            print('i:',Lk[i])
            print('j:',Lk[j])
            L1 = list(Lk[i])[:k-2]
            L2 = list(Lk[j])[:k-2]
            L1.sort()
            L2.sort()
            if L1==L2:
                retList.append(Lk[i] | Lk[j])  #集合操作符,求并集
                print(retList)
    return retList
def apriori(dataSet, minSupport = 0.5):
    C1 = createC1(dataSet)
    D = list(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)
        supportData.update(supK)
        L.append(Lk)
        k += 1
    return L, supportData

 测试:

if __name__=='__main__':
    dataSet=loadDataSet()
    L,suppData=apriori(dataSet,0.5)
    print(L)
    print(suppData)

 输出:

[[frozenset({1}), frozenset({3}), frozenset({2}), frozenset({5})], [frozenset({3, 5}), frozenset({1, 3}), frozenset({2, 5}), frozenset({2, 3})], [frozenset({2, 3, 5})], []]
{frozenset({5}): 0.75, frozenset({3}): 0.75, frozenset({2, 3, 5}): 0.5, frozenset({1, 2}): 0.25, frozenset({1, 5}): 0.25, frozenset({3, 5}): 0.5, frozenset({4}): 0.25, frozenset({2, 3}): 0.5, frozenset({2, 5}): 0.75, frozenset({1}): 0.5, frozenset({1, 3}): 0.5, frozenset({2}): 0.75}

 

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

如果某条规则不满足最小可信度要求,则该规则的所有子集也不满足最小可信度要求。

分级法:首先从一个频繁项集开始,接着创建一个规则列表,其中规则右部只包含一个元素,然后对这些元素进行测试。接下来合并所有剩余规则来创建一个新的规则列表,其中规则右部包含两个元素。

关联规则生成函数:

def generateRules(L, supportData, minConf=0.7):  #频繁项集列表,支持度列表,最小可信度
    bigRuleList = []    #包含可信度的规则列表
    for i in range(1, len(L)):  #因为无法对只有单个元素的频繁项集构建规则,智能从L[1]开始,L[1]是包含两个元素的频繁项集列表
        for freqSet in L[i]:    #遍历当前L[i]的所有频繁项集
            H1 = [frozenset([item]) for item in freqSet]    #从频繁项集中得到元素列表
            if (i > 1):     #i>1时,频繁项集至少有三个元素
                rulesFromConseq(freqSet, H1, supportData, bigRuleList, minConf)     #对H1进一步合并成含有两个元素的频繁项集列表
            else:   #频繁项集有两个元素,则计算可信度
                calcConf(freqSet, H1, supportData, bigRuleList, minConf)
    return bigRuleList
def calcConf(freqSet, H, supportData, brl, minConf=0.7):    #计算规则的可信度并且找到满足最小可信度要求的规则;函数内部没有重置brl,所以brl可以保存满足条件的规则。
    prunedH = []    #保存满足最小可信度的规则列表
    for conseq in H:    #
        conf = supportData[freqSet]/supportData[freqSet-conseq]     #计算可信度
        print(freqSet - conseq, '====>', conseq, '可信度:', conf)
        if conf >= minConf:
            # print(freqSet-conseq,'====>',conseq,'可信度:',conf)
            brl.append((freqSet-conseq, conseq, conf))  #对列表brl补充,brl即为前面通过检查的bigRuleList
            prunedH.append(conseq)
    print('prunedH:',prunedH)
    return prunedH
def rulesFromConseq(freqSet, H, supportData, brl, minConf=0.7):     #频繁项集,可以出现在规则右面的元素列表H
    m=len(H[0])     #第一次调用时m=1
    if len(freqSet) > m + 1: #如果频繁项集的元素个数大于H的元素长度加1,例如freqSet=[2,3,5],H=[2,3,5]时,3>1+1,此时把H的元素融合成[[2,3],[2,5],[3,5]]
        Hmp1 = aprioriGen(H, m+1)   #Hmpl=[[2,3],[2,5],[3,5]]
        print('freqSet:',freqSet)
        print('Hmp1:',Hmp1)
        Hmp1 = calcConf(freqSet, Hmp1, supportData, brl, minConf)
        if len(Hmp1) > 1:   #如果不止一条规则满足要求,则使用Hmp1迭代调用rulesFromConseq来判断是否可以进一步组合这些规则
            rulesFromConseq(freqSet, Hmp1, supportData, brl, minConf)

 测试:

if __name__=='__main__':
    dataSet=loadDataSet()
    L,suppData=apriori(dataSet,0.5)
    rules=generateRules(L,suppData,0.7)
    print(rules)

 输出:

frozenset({5}) ====> frozenset({3}) 可信度: 0.6666666666666666
frozenset({3}) ====> frozenset({5}) 可信度: 0.6666666666666666
prunedH: []
frozenset({3}) ====> frozenset({1}) 可信度: 0.6666666666666666
frozenset({1}) ====> frozenset({3}) 可信度: 1.0
prunedH: [frozenset({3})]
frozenset({5}) ====> frozenset({2}) 可信度: 1.0
frozenset({2}) ====> frozenset({5}) 可信度: 1.0
prunedH: [frozenset({2}), frozenset({5})]
frozenset({3}) ====> frozenset({2}) 可信度: 0.6666666666666666
frozenset({2}) ====> frozenset({3}) 可信度: 0.6666666666666666
prunedH: []
freqSet: frozenset({2, 3, 5})
Hmp1: [frozenset({2, 3}), frozenset({2, 5}), frozenset({3, 5})]
frozenset({5}) ====> frozenset({2, 3}) 可信度: 0.6666666666666666
frozenset({3}) ====> frozenset({2, 5}) 可信度: 0.6666666666666666
frozenset({2}) ====> frozenset({3, 5}) 可信度: 0.6666666666666666
prunedH: []
[(frozenset({1}), frozenset({3}), 1.0), (frozenset({5}), frozenset({2}), 1.0), (frozenset({2}), frozenset({5}), 1.0)]
View Code

 


 参考

上述代码有逻辑错误:

在函数rulesFromConseq中,如果H有三个元素,例如原文中的H=[2,3,5],freqSet=[2,3,5],程序会将H融合成[[2,3],[2,5],[3,5]],不会校验[2,3]==>[5]、[2,5]==>[3]、[3,5]==>[2]的可信度,所以结果少了这些规则。

修改:

def generateRules(L, supportData, minConf=0.7):  #频繁项集列表,支持度列表,最小可信度
    bigRuleList = []    #包含可信度的规则列表
    for i in range(1, len(L)):  #因为无法对只有单个元素的频繁项集构建规则,智能从L[1]开始,L[1]是包含两个元素的频繁项集列表
        for freqSet in L[i]:    #遍历当前L[i]的所有频繁项集
            H1 = [frozenset([item]) for item in freqSet]    #从频繁项集中得到元素列表
            if (i > 1):     #i>1时,频繁项集至少有三个元素
                H1 = calcConf(freqSet, H1, supportData, bigRuleList, minConf)   #计算可信度,过滤可信度低的候选规则
                rulesFromConseq(freqSet, H1, supportData, bigRuleList, minConf)     #对H1进一步合并成含有两个元素的频繁项集列表
            else:   #频繁项集有两个元素,则计算可信度
                calcConf(freqSet, H1, supportData, bigRuleList, minConf)
    return bigRuleList

测试输出:

[(frozenset({1}), frozenset({3}), 1.0), (frozenset({5}), frozenset({2}), 1.0), (frozenset({2}), frozenset({5}), 1.0), (frozenset({3, 5}), frozenset({2}), 1.0), (frozenset({2, 3}), frozenset({5}), 1.0)]

 if…else…多余,修改为下:

def generateRules(L, supportData, minConf=0.7):  #频繁项集列表,支持度列表,最小可信度
    bigRuleList = []    #包含可信度的规则列表
    for i in range(1, len(L)):  #因为无法对只有单个元素的频繁项集构建规则,只能从L[1]开始,L[1]是包含两个元素的频繁项集列表
        for freqSet in L[i]:    #遍历当前L[i]的所有频繁项集
            H1 = [frozenset([item]) for item in freqSet]    #从频繁项集中得到元素列表
            rulesFromConseq(freqSet, H1, supportData, bigRuleList, minConf)     #对H1进一步合并成含有两个元素的频繁项集列表
    return bigRuleList
def rulesFromConseq(freqSet, H, supportData, brl, minConf=0.7):     #频繁项集,可以出现在规则右面的元素列表H
    m=len(H[0])     #第一次调用时m=1
    if len(freqSet) > m: #如果频繁项集的元素个数大于H的元素长度,例如freqSet=[2,3,5],H=[2,3,5]时,3>1,此时把H的元素融合成[[2,3],[2,5],[3,5]]
        # print('freqSet:',freqSet)
        # print('Hmp1:',Hmp1)
        Hmp1 = calcConf(freqSet, H, supportData, brl, minConf)
        if len(Hmp1) > 1:   #如果不止一条规则满足要求,则使用Hmp1迭代调用rulesFromConseq来判断是否可以进一步组合这些规则
            Hmp1=aprioriGen(Hmp1,m+1)
            rulesFromConseq(freqSet, Hmp1, supportData, brl, minConf)   #递归

 进一步修改rulesFromConseq里的递归函数如下:

def rulesFromConseq(freqSet, H, supportData, brl, minConf=0.7):
    m = len(H[0])
    while (len(freqSet) > m): # 判断长度 > m,这时即可求H的可信度
        H = calcConf(freqSet, H, supportData, brl, minConf)
        if (len(H) > 1): # 判断求完可信度后是否还有可信度大于阈值的项用来生成下一层H
            H = aprioriGen(H, m + 1)
            m += 1
        else: # 不能继续生成下一层候选关联规则,提前退出循环
            break

 

posted @ 2018-11-16 15:26  我的下铺刚田武  阅读(390)  评论(0编辑  收藏  举报