Table of Contents
關聯規則探勘的問題,看似朴素,但實際上卻相當複雜。它不僅涉及到大量的數據處理,還需要考慮到各種不同的度量標準,以便從中提取出有價值的資訊。因為在成千上萬筆交易中,哪些項目「總是一起」出現? 真正困難在於組合爆炸,若有 10000 件商品,潛在組合多到無法枚舉。而 Apriori 的偉大之處是把「可搜尋性」建立在反單調性 (anti-monotonicity) 之上,讓我們能以數學性質剃除海量不必要的候選,保留真有機會成為規則的組合。
問題定義:從交易到規則
資料型態: 一組交易 $T={t_1,\dots,t_n}$,每筆交易 $t$ 是項目集合 $I={i_1,\dots,i_m}$ 的子集。
我們尋找的對象是關聯規則: $X \rightarrow Y$,其中 $X, Y \subseteq I$、$X \cap Y = \varnothing$。
基本度量:
- Support: $\text{supp}(X) = \frac{|{t \in T: X \subseteq t}|}{|T|}$,即 $X$ 在所有交易中的出現比例。
- Confidence: $\text{conf}(X \rightarrow Y) = \frac{\text{supp}(X \cup Y)}{\text{supp}(X)}$,觀察買了 $X$ 之後同時買 $Y$ 的機率。
- Lift: $\text{lift}(X \rightarrow Y) = \frac{\text{supp}(X \cup Y)}{\text{supp}(X)\text{supp}(Y)}$,衡量相對於獨立假設的加成(>1 表正向關聯)。
延伸常用度量 (利於排序與風控):
- Leverage: $\text{lev}(X \rightarrow Y) = \text{supp}(X \cup Y) - \text{supp}(X)\text{supp}(Y)$ (絕對提升量)。
- Conviction: $\text{conv}(X \rightarrow Y)=\frac{1-\text{supp}(Y)}{1-\text{conf}(X \rightarrow Y)}$ (「若 $X$ 發生而 $Y$ 未發生」的驚訝程度)。
數學原理: 反單調性與可搜尋性
頻繁項集 (frequent itemset) 的定義: 若 $\text{supp}(X) \ge \text{min support}$,則 $X$ 為頻繁。
Apriori 的核心斷言:
Apriori Principle (反單調性): 若項集 $X$ 為頻繁,則 $X$ 的所有子集皆頻繁;反之,若 $X$ 的任一子集不頻繁,$X$ 必不頻繁。
這帶來兩個關鍵操作:
- 候選生成 (self-join): 由 $k$-項頻繁集生成 $(k!+!1)$-項候選。
- 剪枝 (prune): 若候選的任一 $k$-子集不頻繁,直接丟棄該候選。
因此,我們僅需在「有可能頻繁」的前提下計數支援度,將組合空間由天文級縮至可管理的規模,時間成本主要來自兩部分:
- 候選數量 (受 $\text{min support}$ 與資料稀疏度影響)
- 支援度計數 (I/O 密集)
空間成本則與候選表、雜湊結構、與交易壓縮格式 (如稀疏編碼) 相關。
資料準備: 從交易表到 one-hot
Apriori 假設輸入為交易 × 項目的 one-hot 矩陣。常見來源型式與轉換:
- 交易-項目明細表 (tid, item): 先 groupby 交易再 pivot。
- 品類/屬性型項: 可考慮展開為虛擬變數 (如 size_M、color_blue)。
- 去噪與最小出現次數: 先對低頻項做過濾,避免稀疏尾項拖慢搜尋。
- 稀疏化: 對超大型資料用稀疏矩陣 (scipy.sparse) 以節省記憶體。
實作流程
重點不只是「跑出規則」,而是以可解釋的步驟建構 pipeline: 資料清理 → 編碼 → 頻繁項集 → 規則 → 篩選 → 詮釋 → 驗證。
import pandas as pd
from mlxtend.frequent_patterns import apriori, association_rules
df = pd.DataFrame({
"ID": [1, 2, 3, 4, 5, 6],
"Onion": [1, 0, 0, 1, 1, 1],
"Potato": [1, 1, 0, 1, 1, 1],
"Burger": [1, 1, 0, 0, 1, 1],
"Milk": [0, 1, 1, 1, 0, 1],
"Beer": [0, 0, 1, 0, 1, 0]
}, dtype=bool)
frequent_itemsets = apriori(df[['Onion', 'Potato', 'Burger', 'Milk', 'Beer']],
min_support=0.5,
use_colnames=True)
rules = association_rules(frequent_itemsets,
metric='lift',
min_threshold=1)
rules = rules[rules["lift"] > 1]
print(rules)
結果觀察建議:
- 請同時觀察 support (置信度的基礎樣本量) 與 lift (對隨機的增益),避免高 conf、低 supp 的脆弱規則誤導。
- leverage 有助挑出「絕對增益」較大的規則;conviction 在 conf 相近時可區分非對稱性。
門檻設定與評估: 如何避免兩個極端?
- in_support 太高: 只剩高度普遍的「必買」規則 (資訊量低)。
- min_support 太低: 候選爆炸、效能崩潰且噪音規則充斥。
實務策略:
- 先以探索性分布決定區間 (觀察單品與雙品出現率的對數分布)。
- 逐步下修 support,同步加入 min_confidence / min_lift 作為「雙閥」過濾。
- 使用 holdout 期 (如以月份切分) 驗證規則是否穩定 (support 漂移不宜過大)。
排序策略 (多目標):
- 商業動機導向 (毛利 × leverage × lift)
- 風控導向 (conviction、向量化後的穩定性分數)
- 行銷導向 (群體覆蓋率、與策略客群的一致性)
效能調校: 把時間花在值得的地方
- 事前去噪: 刪除極低頻項、合併長尾同義項 (如相同 SKU 的不同拼寫)。
- 欄位裁剪: 只保留策略相關品類,減少項目宇宙。
- 交易壓縮: 以 set/bitset 表示交易;或使用稀疏矩陣 (scipy) 以節省 RAM。
- 分批/增量: 以時間窗分塊計數,再合併支持度 (需小心去重)。
- 合理平行化: 在候選計數階段做切分 (注意 I/O 瓶頸)。
常見誤區與反例思考
- 把關聯當因果: ${\text{lift}>1}$ 不代表 $X$ 導致 $Y$;可能是共同原因或季節性;需做時間序列對照與干擾項控制。
- 忽略樣本基礎: 高 confidence、低 support 的規則常不穩定 (對冷門長尾更甚)。
- 只看 lift: 在策略端,leverage 往往更貼近實際增益 (多賣了多少比例)。
- 規則冗餘: $X \cup {a}\rightarrow Y$ 的 conf 若與 $X \rightarrow Y$ 幾乎相同,應做冗餘規則剔除 (closed/maximal itemsets 的思路)。
- 資料漂移: 促銷、換季、上新會改變支持度分布,規則須定期重採與回測。
結語: Apriori 的真正價值
Apriori 的價值,不在「找到最多規則」,而是讓我們以可計算的方式理解「一起發生」的語法,當你能把 support 的穩健、confidence 的可用、lift 的意義、leverage 的商業價值與 conviction 的不對稱風險,編織成一套「可行的決策語言」,Apriori 才算學會,它是資料與行為之間的翻譯器——把共現變成路徑,把路徑變成行動。
What are your thoughts?