1. 程式人生 > >《機器學習實戰》chapter 11 使用apriori演算法進行關聯分析

《機器學習實戰》chapter 11 使用apriori演算法進行關聯分析

使用apriori演算法進行關聯分析apriori原理:1、一個項集是非頻繁的,那麼它的所有超集也是非頻繁的2、一個項集是頻繁的,那麼它的所有子集也是頻繁的一、支援度(support)-使用apriori發現頻繁項集對於資料集(包含M個項集)1、求單個元素組成項集的集合C1(無重複)2、利用minsupport(最小支援度或非頻繁)過濾掉非頻繁的單元素項集,得L13、單個元素兩兩組合成2元素的項集的集合C24、利用minsupport(最小支援度或非頻繁)過濾掉非頻繁的2元素項集,得L25、利用2元素項集兩兩組合得3元素項集C36、利用minsupport(最小支援度或非頻繁)過濾掉非頻繁的3元素項集,得L3
7、重複上述過程求支援度(過濾支援度)低的項集就是這樣一個過程:C1->L1->C2->L2->C3->L3->....->Ck->Lkapriori的應用:一個項集是非頻繁的,那麼它的所有超集也是非頻繁的要獲得項集合並(每種可能集合)的支援度就需要多次重複上述過程,例如
由圖可以看出,對於僅有4個物品的集合,也需要遍歷15次,如果是100種呢?事實上,對於N種物品的資料集共有2^N - 1種項集組合,也就是說我們需要遍歷2^N - 1次。如上圖,C2=[{0, 1}, {0, 2}, {0, 3}, {1, 2}, {1, 3}, {2, 3}],其中 {2, 3}是非頻繁的,由apriori我們知道他的超集也是非頻繁的,所以我們這裡直接過濾掉非頻繁的{2, 3}得到L2=[{0, 1}, {0, 2}, {0, 3}, {1, 2}, {1, 3}]
這時,由L2組合得到的C3就比由C2組合得到的C3少了兩種可能(沒有了{0, 2, 3}, {1, 2, 3}),也不存在C4了。如果放到更大的資料集種,apriori的效果將更加可觀。二、置信度(confidence)-從頻繁項集挖掘關聯規則如果有一個關聯規則{豆奶,萵苣},那麼就可能有一條關聯規則“豆奶-->萵苣”,這意味著如果有人買了豆奶,那麼在統計上他會購買萵苣的概率比較大。但是,這一條反過來並不總是成立。也就是說,即使“豆奶-->萵苣”統計上顯著,那麼“萵苣-->豆奶”也不一定成立。就比方說{啤酒,尿布}這個項集,“啤酒-->尿布”,我們說男人在下班後給孩子買尿布的同時,會順手購買自己愛喝的啤酒。但是去買啤酒的男人,又有多少是剛有孩子的?這個概率是要小很多的。
類似於,上一節的頻繁項集的生成,我們可以為每個頻繁項集產生許多關聯規則。
如果能夠減少規則數目來確保問題的可解性,那麼計算起來就會好很多。apriori的應用:如果某條規則並不能滿足最小可信度要求,那麼該規則的所有子集也不會滿足最小可信度要求。上圖中,假設0, 1, 2-->3不滿足最小可信度要求,那麼就知道任何左部為{0, 1, 2}子集的規則也不會滿足最小可信度要求。演算法過程:對於頻繁項集列表L:[[{1}, {2}, {3}, {5}][{1, 3}, {2, 5}, {2, 3}, {3, 5} ][{2, 3, 5} ]]生成關聯規則_function():遍歷L:計算規則右部只有單個元素的置信度(並過濾掉不滿足最低置信度閾值的規則)如果頻繁項集中的元素個數有3個或更多計算右部有2個、3個...len(freqSet)-1個元素的規則置信度注意:1、遍歷L時從至少有2個元素的項集開始,只有一個元素,就不存在關聯規則了2、對每一個頻繁項集freqSet,求只有單個元素的項集組成的列表H1,用H1作為規則右部3、頻繁項集元素個數有3個或3個以上時,用len(H1[0]) + 1作為遞迴變數,累加右部元素個數4、apriori的應用:先求右部為單個元素的規則置信度,將不滿足最低置信度閾值的0, 1, 2-->3剔除,這樣,左部為{0, 1, 2}的所有子集的規則也直接被剔除不再計算。5、修正書中一個我認為不正確的地方
這個函式中,書中程式碼在頻繁項集個數大於2時,只計算了規則右部為兩個或兩個以上元素的情況,而漏算了規則右部只有一個元素的情況。修正部分如下:
如理解有偏差,歡迎批評指正。

程式碼部分:

# !/usr/bin/env python
# -*- coding: utf-8 -*-

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


def createC1(dataSet):
    """
    構建大小為1的所有候選項集的集合
    :param dataSet:
    :return:
    """
    C1 = []
    for transaction in dataSet:
        for item in transaction:
            if not [item] in C1:
                C1.append([item])
    C1.sort()
    return list(map(frozenset, C1))


def scanD(D, Ck, minSupport):
    """
    Ck中的元素經過最小支援度過濾得到Lk,和每個元素的支援度
    :param D: 資料集
    :param Ck:
    :param minSupport: 最小支援度(過濾)
    :return:
    過程:
    對資料集中的每條交易記錄tran
    對每個候選項集can:
        檢查一下can是否是tran的子集:
            如果是:則增加can的計數值
            如果不是:把can的值設為1
    對每個候選項集:
        如果其支援度不低於最小值,則保留該項集
    返回所有頻繁項集列表
    """
    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


def aprioriGen(Lk, k):
    """
    合併頻繁項集為k個元素
    :param Lk: 頻繁項集列表
    :param k: 項集元素個數
    :return:
    """
    retList = []
    lenLk = len(Lk)
    for i in range(lenLk):
        for j in range(i+1, lenLk):
            # 每一個項集同其後面的項集做比較
            # 如果前k-2個元素相同(取不到k-2),則合併為k個元素的項集,新增到retList中
            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])
    return retList


def Apriori(dataSet, minSupport=0.5):
    """

    :param dataSet: 資料集
    :param minSupport: 最小支援度
    :return:
    過程:
    C1->L1->C2->L2->C3->L3-> ... ->Ck->Lk
    """
    # 單個物品項組成的集合
    C1 = createC1(dataSet)
    # 集合表示的資料集D
    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


def calcConf(freqSet, H, supportData, bigRuleList, minConf=0.7):
    """
    從頻繁項集freqSet,計算關聯規則(freqSet - H) --> H的置信度
    並過濾置信度低於最小置信度閾值的規則
    :param freqSet: 頻繁項集
    :param H: 出現在規則右部
    :param supportData: 支援度字典
    :param bigRuleList: 關聯規則列表
    :param minConf: 最小置信度閾值
    :return: 滿足最小可信度要求的規則列表
    """
    # 初始化滿足最小可信度要求的規則列表
    prunedH = []
    for conseq in H:
        conf = supportData[freqSet] / supportData[freqSet - conseq]
        if conf >= minConf:
            print(freqSet-conseq, "-->", conseq, "conf : ", conf)
            bigRuleList.append((freqSet - conseq, conseq, conf))
            # 與bigRuleList對應的置信度值列表
            prunedH.append(conseq)
    return prunedH


def rulesFromConseq(freqSet, H, supportData, bigRuleList, minConf=0.7):
    """
    由於freqSet中的元素個數大於等於3,計算規則的右部為多個元素的情形
    :param freqSet: 頻繁項集
    :param H: 出現在規則右部的元素列表
    :param supportData: 支援度列表
    :param bigRuleList: 關聯規則列表
    :param minConf: 最小置信度閾值
    :return:
    """
    m = len(H[0])
    # m + 1,其實示一個遞迴變數,遞迴地增加右部元素個數
    if len(freqSet) > (m + 1):
        # 合併元素,任意組合成m+1個元素的項集
        Hmp1 = aprioriGen(H, m + 1)
        # 過濾關聯規則(freqSet - Hmp1) --> Hmp1的置信度小於最小置信度閾值的規則
        # 規則右部有m + 1個元素
        Hmp1 = calcConf(freqSet, Hmp1, supportData, bigRuleList, minConf)
        # 遞迴累加右部元素個數
        if len(Hmp1) > 1:
            rulesFromConseq(freqSet, Hmp1, supportData, bigRuleList, minConf)
    else:
        # 當右部元素個數和頻繁項集freqSet的長度一樣時,不再劃分,返回
        return


def generateRules(L, supportData, minConf=0.7):
    """
    基於給定的頻繁項集L和支援度supportData,計算滿足最小支援度閾值的規則
    :param L: 頻繁項集列表
    :param supportData: 包含那些頻繁項集支援資料的字典
    :param minConf: 最小置信度閾值
    :return: 包含可信度的規則列表
    """
    # 初始化規則存放列表
    bigRuleList = []
    # 遍歷頻繁項集
    for i in range(1, len(L)):
        #
        for freqSet in L[i]:
            # 只包含單個元素集合的列表H1
            H1 = [frozenset([item]) for item in freqSet]
            # 過濾關聯規則(freqSet - H1) --> H1的置信度小於最小置信度閾值的規則
            # H1是右部為單個元素的情形
            H1 = calcConf(freqSet, H1, supportData, bigRuleList, minConf)
            if i > 1:
                # 如果頻繁項集中元素個數大於等於3,就需要考慮右部為多個元素的情形
                rulesFromConseq(freqSet, H1, supportData, bigRuleList, minConf)
    return bigRuleList


dataSet = loadData()
L, supportData = Apriori(dataSet)
rules = generateRules(L, supportData, minConf=0.7)
print(rules)