1. 程式人生 > >《Python資料分析與挖掘實戰》第八章學習-關聯規則Apriori

《Python資料分析與挖掘實戰》第八章學習-關聯規則Apriori

《Python資料分析與挖掘實戰》這本書其實已經在暑假結束的時候就已經基本上過了一遍,但是卻一直沒有堅持著記錄。最近幾天想著將之前的學習內容整理一遍,因此,再做記錄。
全文分為以下三個部分:

  1. Apriori演算法
  2. Apriori的python實現
  3. 總結

Apriori演算法

首先先對Apriori演算法的理論知識進行梳理。由於《Python資料分析與挖掘實戰》主要針對實戰,因此,對理論部分闡述並不多,本文理論知識主要來自於《資料探勘概念與技術》。

頻繁模式、項集和關聯規則

頻繁模式是頻繁地出現在資料集中的模式(如項集、子序列或子結構)。例如,頻繁地同時出現在交易資料集中的商品(如牛奶和麵包)便是一個頻繁項集,而頻繁地出現在購物資料庫中的序列資料(比如先買PC,再買數碼相機,再買記憶體卡)則是頻繁子序列。
項集是項的集合

,包含k個項的項集稱為k項集,如I={i1,i2,,im}就是一個m項集。
關聯規則指的是,項集中每個元素頻繁關聯或同時出現的模式。具體的,設I={i1,i2,,im}是項的集合,則關聯規則是形如A=>B的蘊涵式,其中A和B都是I的子集,且A、B都不為空,A、B的交集為空。
比如,下例表示的就是購買計算機之後又同時購買財務管理軟體的關聯規則。
這裡寫圖片描述

支援度、置信度、頻繁項集和強關聯規則

支援度和置信度是規則興趣度的兩種度量,分別反映所發現規則的有用性和確定性。比如針對上面的舉例規則,假設其支援度為2%,置信度為60%,則前者表示分析的所有事務的2%同時購買計算機和財務管理軟體(每一個事務是一個非空項集,是上述提到I的子集,所有事務的集合是事務集,構成關聯規則發現的事務資料庫。

),後者表示購買計算機的顧客60%也購買了財務管理軟體。
具體用公式表示如下:
這裡寫圖片描述
這裡寫圖片描述
如果項集I的相對支援度(上述公式有時稱為相對支援度,而項集的出現頻度稱為絕對支援度)滿足預定義的最小支援閾值(即I的絕對支援度滿足對應的最小支援度計數閾值),則I是頻繁項集
滿足最小支援度和最小置信度的關聯規則,是強關聯規則

Apriori演算法-頻繁項集挖掘

Apriori演算法是一種發現頻繁項集的基本演算法。其使用一種稱為逐層搜尋的迭代方法,其中k項集用於探索(k+1)項集。首先,通過掃描資料庫,累計每個項的計數,並收集滿足最小支援度的項,找出頻繁1項集的集合。該集合記為L1。然後。使用L1找出頻繁2項集的集合L2,使用L2找出L3,如此下去,直到不能再找到頻繁k項集。
為了壓縮搜尋空間,提出先驗性質:頻繁項集的所有非空子集也一定是頻繁的。(如果一個集合不能通過測試,則他的所有超集也都不能通過相同的測試)

如何使用Lk1找出Lk(k≥2)

分為兩步——連線步和剪枝步

  • 連線步:為找出Lk,通過將Lk1與自身連線產生候選k項集的集合。Apriori演算法假定項集中的項按照字典序排序。設l1l2Lk1中的項集,如果l1l2的前(k-2)個項是相同的,則稱l1l2是可連線的。所以l1l2連線產生的結果項集是{l1[1], l1[2], …, l1[k1], l2[k1]}。
  • 剪枝步:由於存在先驗性質:任何非頻繁的(k-1)項集都不是頻繁k項集的子集。因此,如果一個候選k項集Ck的(k-1)項子集不在Lk1中,則該候選也不可能是頻繁的,從而可以從Ck中刪除,獲得壓縮後的Ck

將《資料探勘概念與技術》中的Apriori例子摘錄如下:
該例基於圖6.2的資料庫,該資料庫一共有9個事務。
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述

由頻繁項集產生關聯規則

可直接由頻繁項集產生強關聯規則:

  • 對於每個頻繁項集l,產生l的所有非空子集。
  • 對於l的每個非空子集s,如果support_count(t)support_count(s)≥min_conf(最小置信度),則輸出規則”s=>(l-s)”。min_conf為最小置信度閾值。

其中置信度公式完整如下:
這裡寫圖片描述
繼續上一個例子,摘錄如下:
這裡寫圖片描述

Apriori的python實現

《Python資料分析與挖掘實戰》中給出了Apriori的實現程式碼。
具體程式碼如下:

import pandas as pd


#自定義連線函式,用於實現L_{k-1}到C_k的連線
def connect_string(x, ms):
    x = list(map(lambda i:sorted(i.split(ms)), x))
    l = len(x[0])
    r = []
    for i in range(len(x)):
        for j in range(i,len(x)):
            if x[i][:l-1] == x[j][:l-1] and x[i][l-1] != x[j][l-1]:
                r.append(x[i][:l-1]+sorted([x[j][l-1],x[i][l-1]]))
    return r

#尋找關聯規則的函式
def find_rule(d, support, confidence, ms = u'--'):
    result = pd.DataFrame(index=['support', 'confidence']) #定義輸出結果

    support_series = 1.0*d.sum()/len(d) #支援度序列
    column = list(support_series[support_series > support].index) #初步根據支援度篩選
    k = 0

    while len(column) > 1:
        k = k+1
        print(u'\n正在進行第%s次搜尋...' %k)
        column = connect_string(column, ms)
        print(u'數目:%s...' %len(column))
        sf = lambda i: d[i].prod(axis=1, numeric_only = True) #新一批支援度的計算函式

       #建立連線資料,這一步耗時、耗記憶體最嚴重。當資料集較大時,可以考慮並行運算優化。
        d_2 = pd.DataFrame(list(map(sf,column)), index = [ms.join(i) for i in column]).T

        support_series_2 = 1.0*d_2[[ms.join(i) for i in column]].sum()/len(d) #計算連線後的支援度
        column = list(support_series_2[support_series_2 > support].index) #新一輪支援度篩選
        support_series = support_series.append(support_series_2)
        column2 = []
        #遍歷可能的推理,如{A,B,C}究竟是A+B-->C還是B+C-->A還是C+A-->B?
        for i in column:
            i = i.split(ms)
            for j in range(len(i)):
                column2.append(i[:j]+i[j+1:]+i[j:j+1])

        cofidence_series = pd.Series(index=[ms.join(i) for i in column2]) #定義置信度序列

        for i in column2: #計算置信度序列
            cofidence_series[ms.join(i)] = support_series[ms.join(sorted(i))]/support_series[ms.join(i[:len(i)-1])]

        for i in cofidence_series[cofidence_series > confidence].index: #置信度篩選
            result[i] = 0.0
            result[i]['confidence'] = cofidence_series[i]
            result[i]['support'] = support_series[ms.join(sorted(i.split(ms)))]

    result = result.T.sort_values(['confidence','support'], ascending = False) #結果整理,輸出
    print(u'\n結果為:')
    print(result)

    return result

通過分析上述程式碼,可以發現,上述程式碼並沒有利用先驗性質進行剪枝步。
利用上述函式,對資料進行處理。
程式碼如下:

inputfile = 'D:/ProgramData/PythonDataAnalysiscode/chapter8/demo/data/apriori.txt' #輸入事務集檔案
data = pd.read_csv(inputfile, header=None, dtype = object)

start = time.clock() #計時開始
print(u'\n轉換原始資料至0-1矩陣...')
ct = lambda x : pd.Series(1, index = x[pd.notnull(x)]) #轉換0-1矩陣的過渡函式
b = list(map(ct, data.as_matrix())) #用map方式執行
data = pd.DataFrame(b).fillna(0) #實現矩陣轉換,空值用0填充
end = time.clock() #計時結束
print(u'\n轉換完畢,用時:%0.2f秒' %(end-start))
del b #刪除中間變數b,節省記憶體

support = 0.06 #最小支援度
confidence = 0.75 #最小置信度
ms = '---' #連線符,預設'--',用來區分不同元素,如A--B。需要保證原始表格中不含有該字元

start = time.clock() #計時開始
print(u'\n開始搜尋關聯規則...')
find_rule(data, support, confidence, ms)
end = time.clock() #計時結束
print(u'\n搜尋完成,用時:%0.2f秒' %(end-start))

輸出結果如下:
這裡寫圖片描述
由結果也可以看出,本書中對於Apriori演算法的實現尚有不足,不僅沒有利用到剪枝步,最後的結果中也沒有明確究竟是怎樣的規則。
最後博主查詢其他資料,參考了Apriori(Python實現)這篇文章中的Python程式碼。
輸出結果(部分)如下:
這裡寫圖片描述
這裡寫圖片描述
利用這位作者的程式碼對書中的資料集進行實戰,結果跟書中最後實現是一樣的,但是過程更加清晰,且最後的規則展現也更加明確。

總結

本文主要結合了《資料探勘概念與技術》的理論內容和《Python資料分析與挖掘實戰》的實戰內容,最後又參考了其他人的python實現程式碼。
對關聯規則理論部分有了較好的理解,但是對程式碼實現部分,還需要再反覆揣摩理解一下。
至此,對第八章關聯規則部分整理完畢。