關聯規則挖掘可以讓我們從數據集中發現項與項(item 與 item)之間的關系,它在我們的生活中有很多應用場景,“購物籃分析”就是一個常見的場景。
在今天的內容中,希望你能帶着問題,和我一起來搞懂以下幾個知識點:
- 搞懂關聯規則中的幾個重要概念:支持度、置信度、提升度;
- Apriori 算法的工作原理;
- 在實際工作中,我們該如何進行關聯規則挖掘。
搞懂關聯規則中的幾個概念
我舉一個超市購物的例子,下面是幾名客戶購買的商品列表:
什么是支持度呢?
支持度是個百分比,它指的是某個商品組合出現的次數與總次數之間的比例。支持度越高,代表這個組合出現的頻率越大。
在這個例子中,我們能看到“牛奶”出現了 4 次,那么這 5筆訂單中“牛奶”的支持度就是 4/5=0.8。
同樣“牛奶 + 面包”出現了 3 次,那么這 5 筆訂單中"牛奶 + 面包”的支持度就是 3/5=0.6。
什么是置信度呢?
它指的就是當你購買了商品 A,會有多大的概率購買商品 B,在上面這個例子中:
置信度(牛奶→啤酒)=2/4=0.5,代表如果你購買了牛奶,有多大的概率會購買啤酒?
置信度(啤酒→牛奶)=2/3=0.67,代表如果你購買了啤酒,有多大的概率會購買牛奶?
我們能看到,在 4 次購買了牛奶的情況下,有 2 次購買了啤酒,所以置信度 (牛奶→啤酒)=0.5,而在 3 次購買啤酒的情況下,有 2 次購買了牛奶,所以置信度(啤酒→牛奶)=0.67。
所以說置信度是個條件概念,就是說在 A 發生的情況下,B 發生的概率是多少。
什么是提升度呢?
我們在做商品推薦的時候,重點考慮的是提升度,因為提升度代表的是“商品 A 的出現,對商品 B 的出現概率提升的”程度。
Apriori 的工作原理
首先我們把上面案例中的商品用 ID 來代表,牛奶、面包、尿布、可樂、啤酒、雞蛋的商品 ID 分別設置為 1-6,上面的數據表可以變為:
Apriori 算法其實就是查找頻繁項集 (frequent itemset) 的過程,所以首先我們需要定義什么是頻繁項集。
頻繁項集就是支持度大於等於最小支持度 (Min Support) 閾值的項集,所以小於最小值支持度的項目就是非頻繁項集,而大於等於最小支持度的項集就是頻繁項集。
項集這個概念,英文叫做 itemset,它可以是單個的商品,也可以是商品的組合。我們再來看下這個例子,假設我隨機指定最小支持度是 50%,也就是 0.5。
我們來看下 Apriori 算法是如何運算的。
首先,我們先計算單個商品的支持度,也就是得到 K=1 項的支持度:
因為最小支持度是 0.5,所以你能看到商品 4、6 是不符合最小支持度的,不屬於頻繁項集,於是經過篩選商品的頻繁項集就變成:
在這個基礎上,我們將商品兩兩組合,得到 k=2 項的支持度:
我們再篩掉小於最小值支持度的商品組合,可以得到:
以此類推,K = K+1,直至頻繁項集為0
總結下 Apriori 算法的遞歸流程:
- K=1,計算 K 項集的支持度;
- 篩選掉小於最小支持度的項集;
- 如果項集為空,則對應 K-1 項集的結果為最終結果。
- 否則 K=K+1,重復 1-3 步。
Apriori 的改進算法:FP-Growth 算法
我們剛完成了 Apriori 算法的模擬,你能看到 Apriori 在計算的過程中有以下幾個缺點:
- 可能產生大量的候選集。因為采用排列組合的方式,把可能的項集都組合出來了;
- 每次計算都需要重新掃描數據集,來計算每個項集的支持度。
所以 Apriori 算法會浪費很多計算空間和計算時間,為此人們提出了 FP-Growth 算法,它的特點是:
- 創建了一棵 FP 樹來存儲頻繁項集。在創建前對不滿足最小支持度的項進行刪除,減少了存儲空間。我稍后會講解如何構造一棵 FP 樹;
- 整個生成過程只遍歷數據集 2 次,大大減少了計算量。
FP-Growth 的原理
1. 創建項頭表(item header table)
創建項頭表的作用是為 FP 構建及頻繁項集挖掘提供索引。
這一步的流程是先掃描一遍數據集,對於滿足最小支持度的單個項(K=1 項集)按照支持度從高到低進行排序,這個過程中刪除了不滿足最小支持度的項。
項頭表包括了項目、支持度,以及該項在 FP 樹中的鏈表,初始的時候鏈表為空。
2. 構造 FP 樹
FP 樹的根節點記為 NULL 節點。
整個流程是需要再次掃描數據集,對於每一條數據,按照支持度從高到低的順序進行創建節點(也就是第一步中項頭表中的排序結果),節點如果存在就將計數 count+1,如果不存在就進行創建。同時在創建的過程中,需要更新項頭表的鏈表。
3. 通過 FP 樹挖掘頻繁項集
到這里,我們就得到了一個存儲頻繁項集的 FP 樹,以及一個項頭表。我們可以通過項頭表來挖掘出每個頻繁項集。
具體的操作會用到一個概念,叫“條件模式基”,它指的是以要挖掘的節點為葉子節點,自底向上求出 FP 子樹,然后將 FP 子樹的祖先節點設置為葉子節點之和。
我以“啤酒”的節點為例,從 FP 樹中可以得到一棵 FP 子樹,將祖先節點的支持度記為葉子節點之和,得到:
你能看出來,相比於原來的 FP 樹,尿布和牛奶的頻繁項集數減少了。這是因為我們求得的是以“啤酒”為節點的 FP 子樹,也就是說,在頻繁項集中一定要含有“啤酒”這個項。你可以再看下原始的數據,其中訂單 1{牛奶、面包、尿布}和訂單 5{牛奶、面包、尿布、可樂}並不存在“啤酒”這個項,所以針對訂單 1,尿布→牛奶→面包這個項集就會從 FP 樹中去掉,針對訂單 5.也包括了(尿布→牛奶→面包)這個項集也會從 FP 樹中去掉,所以你能看到以“啤酒”為節點的 FP 子樹,尿布、牛奶、面包項集上的計數比原來少了 2。
條件模式基不包括“啤酒”節點,而且祖先節點如果小於最小支持度就會被剪枝,所以“啤酒”的條件模式基為空。
同理,我們可以求得“面包”的條件模式基為:
所以可以求得面包的頻繁項集為{尿布,面包},{尿布,牛奶,面包}。同樣,我們還可以求得牛奶,尿布的頻繁項集,這里就不再計算展示。
總結:
如何使用 Apriori 工具包
Apriori 雖然是十大算法之一,不過在 sklearn工具包中並沒有它,也沒有 FP-Growth 算法。這里教你個方法,來選擇 Python 中可以使用的工具包,你可以通過https://pypi.org/ 搜索工具包。
itemsets, rules = apriori(data, min_support, min_confidence)
其中 data 是我們要提供的數據集,它是一個 list 數組類型。min_support 參數為最小支持度,在 efficient-apriori 工具包中用 0 到 1 的數值代表百分比,比如 0.5 代表最小支持度為 50%。min_confidence 是最小置信度,數值也代表百分比,比如1 代表 100%。
接下來我們用這個工具包,跑一下上面講到的超市購物的例子。下面是客戶購買的商品列表:
具體實現的代碼如下:
from efficient_apriori import apriori # 設置數據集 data = [('牛奶','面包','尿布'), ('可樂','面包', '尿布', '啤酒'), ('牛奶','尿布', '啤酒', '雞蛋'), ('面包', '牛奶', '尿布', '啤酒'), ('面包', '牛奶', '尿布', '可樂')] # 挖掘頻繁項集和頻繁規則 itemsets, rules = apriori(data, min_support=0.5, min_confidence=1) print(itemsets) print(rules) # 運行結果: {1: {('啤酒',): 3, ('尿布',): 5, ('牛奶',): 4, ('面包',): 4}, 2: {('啤酒', '尿布'): 3, ('尿布', '牛奶'): 4, ('尿布', '面包'): 4, ('牛奶', '面包'): 3}, 3: {('尿布', '牛奶', '面包'): 3}} [{啤酒} -> {尿布}, {牛奶} -> {尿布}, {面包} -> {尿布}, {牛奶, 面包} -> {尿布}]
你能從代碼中看出來,data 是個 List 數組類型,其中每個值都可以是一個集合。實際上你也可以把 data 數組中的每個值設置為 List 數組類型,比如:
data = [['牛奶','面包','尿布'], ['可樂','面包', '尿布', '啤酒'], ['牛奶','尿布', '啤酒', '雞蛋'], ['面包', '牛奶', '尿布', '啤酒'], ['面包', '牛奶', '尿布', '可樂']]
兩者的運行結果是一樣的,efficient-apriori 工具包把每一條數據集里的項式都放到了一個集合中進行運算,並沒有考慮它們之間的先后順序。因為實際情況下,同一個購物籃中的物品也不需要考慮購買的先后順序。而其他的 Apriori 算法可能會因為考慮了先后順序,出現計算頻繁項集結果不對的情況。所以這里采用的是 efficient-apriori 這個工具包。
挖掘導演是如何選擇演員的
# -*- coding: utf-8 -*- from efficient_apriori import apriori import csv director = u'寧浩' file_name = './'+director+'.csv' lists = csv.reader(open(file_name, 'r', encoding='utf-8-sig')) # 數據加載 data = [] for names in lists: name_new = [] for name in names: # 去掉演員數據中的空格 name_new.append(name.strip()) data.append(name_new[1:]) # 挖掘頻繁項集和關聯規則 itemsets, rules = apriori(data, min_support=0.5, min_confidence=1) print(itemsets) print(rules)
# 運行結果: {1: {('徐崢',): 5, ('黃渤',): 6}, 2: {('徐崢', '黃渤'): 5}} [{徐崢} -> {黃渤}]
Apriori 算法的核心就是理解頻繁項集和關聯規則。在算法運算的過程中,還要重點掌握對支持度、置信度和提升度的理解。在工具使用上,你可以使用 efficient-apriori 這個工具包,它會把每一條數據中的項(item)放到一個集合(籃子)里來處理,不考慮項(item)之間的先后順序。
在實際運用中你還需要靈活處理,比如導演如何選擇演員這個案例,雖然工具的使用會很方便,但重要的還是數據挖掘前的准備過程,也就是獲取某個導演的電影數據集。