关联规则:购物篮分析与 Apriori
·
摘要:经典的"啤酒与尿布"故事——沃尔玛发现超市里买啤酒的顾客也经常买尿布,于是把这两个商品放在一起,销量大增。这个故事的背后就是关联规则挖掘(Association Rule Mining)。它是无监督学习的一个分支,目标是从事务数据中发现"如果 A 发生了,那么 B 也很有可能发生"这类规律。这篇文章讲清楚关联规则的核心指标、Apriori 算法原理、以及如何用它做电商推荐。
一、什么是关联规则?
购物篮分析
每个顾客的购物篮里有一组商品。关联规则要回答的问题是:
"买 A 的顾客中,有多大比例也买了 B?"
已知一万个购物篮:
- 买啤酒的: 2000 个
- 买尿布的: 2500 个
- 同时买啤酒和尿布的: 1500 个
规则:{啤酒} → {尿布}
支持度:1500/10000 = 15%(两者同时出现的频率)
置信度:1500/2000 = 75%(买了啤酒的顾客中有 75% 也买了尿布)
关联规则的应用场景
| 场景 | 交易 | 规则示例 |
|---|---|---|
| 电商 | 购物车 | {手机} → {手机壳} |
| 金融 | 交易流水 | {转账} → {登录网银} |
| 医疗 | 就诊记录 | {发烧} → {感冒} |
| 推荐系统 | 用户行为 | {浏览A} → {浏览B} |
| 安全审计 | 日志 | {登录失败3次} → {暴力破解} |
二、三个核心指标
支持度(Support)
Support(A → B) = P(A ∩ B) = 同时包含 A 和 B 的交易数 / 总交易数
支持度衡量规则的"普遍性"——一条规则覆盖了多少交易。
支持度太低 → 规则可能只是巧合
通常设最小支持度阈值,如 0.01(1%)
置信度(Confidence)
Confidence(A → B) = P(B | A) = Support(A ∩ B) / Support(A)
= 同时买 A 和 B 的交易数 / 买 A 的交易数
置信度衡量规则的"可靠性"——买了 A 的顾客有多大比例也买了 B。
高置信度 → 规则强
但高置信度也可能有误导(见下文)
提升度(Lift)
Lift(A → B) = Confidence(A → B) / Support(B)
= P(B|A) / P(B)
提升度衡量 A 的出现对 B 的出现是"促进"还是"抑制":
Lift > 1:A 促进 B(正相关)→ 真正的强规则
Lift = 1:A 和 B 独立(无关)
Lift < 1:A 抑制 B(负相关)
为什么需要提升度?
例子:A={买牛奶}, B={买面包}
P(牛奶) = 0.6, P(面包) = 0.7, P(牛奶∩面包) = 0.5
Confidence(牛奶→面包) = 0.5/0.6 = 0.83 ← 很高
Lift(牛奶→面包) = 0.83/0.7 = 1.19 ← 只是略大于1
实际上面包本身就很流行(70% 的人都买),
买牛奶的人中有 83% 买面包——只比平均水平高一点点。
→ 高置信度可能只是因为商品本身畅销,而不是有真正的关联
→ 提升度排除"畅销品偏差",衡量真正的关联强度
三个指标的关系
| 指标 | 衡量 | 越大说明 | 典型阈值 |
|---|---|---|---|
| 支持度 | 规则的普遍性 | 越多交易符合 | 0.01~0.1 |
| 置信度 | 规则的可靠性 | A→B 的概率越高 | 0.5~0.8 |
| 提升度 | 规则的显著性 | A 和 B 关联越强 | >1.5 |
一条好规则:高支持度 + 高置信度 + 高提升度
例:
规则:{尿布} → {啤酒}
支持度 5%, 置信度 60%, 提升度 2.5
解读:
5% 的顾客同时买了尿布和啤酒(有一定普遍性)
买尿布的顾客中 60% 也买了啤酒(很可靠)
买尿布的顾客买啤酒的概率是普通顾客的 2.5 倍(强关联)
三、Apriori 算法
核心思想
Apriori 算法的名字来自拉丁语 "a priori"("来自先前的")。其核心原则是:
如果一个项集是频繁的(支持度 ≥ min_support),
那么它的所有子集也必须是频繁的。
反过来:如果一个项集不频繁,
那么它的所有超集也一定不频繁——可以直接剪枝!
例子:min_support = 0.3
如果 {啤酒, 尿布} 的支持度 = 0.05 < 0.3
→ 不频繁
→ {啤酒, 尿布, 牛奶}、{啤酒, 尿布, 纸巾} 等所有超集
都不需要再计算——它们一定也不频繁!
→ 这种剪枝大幅减少了搜索空间
算法步骤
Apriori 算法(三步走):
第一步:找出所有频繁项集(支持度 ≥ min_support)
1-项集:{啤酒}{尿布}{牛奶}{面包}...
2-项集:{啤酒, 尿布}{啤酒, 牛奶}{尿布, 面包}...
3-项集:{啤酒, 尿布, 面包}...
直到无法生成更大的频繁项集
第二步:从频繁项集生成所有可能的规则
频繁项集 {啤酒, 尿布, 面包} 可以生成:
{啤酒}→{尿布, 面包}
{啤酒, 尿布}→{面包}
{面包}→{啤酒, 尿布}
...
第三步:筛选出置信度 ≥ min_confidence 的规则
# 伪代码:Apriori
def apriori(transactions, min_support):
"""Apriori 算法"""
# 1. 生成 1-项集
frequent_items = {1: find_frequent_1_itemsets(transactions, min_support)}
# 2. 迭代生成更大的项集
k = 2
while frequent_items[k-1]:
# 用 (k-1)-项集生成候选 k-项集
candidates = generate_candidates(frequent_items[k-1])
# 计算候选集的支持度,筛选
frequent_items[k] = find_frequent_itemsets(
candidates, transactions, min_support
)
k += 1
return frequent_items
sklearn 没有直接实现 Apriori
需要注意:sklearn 内置算法中没有 Apriori。需要用 mlxtend 库:
# pip install mlxtend
from mlxtend.frequent_patterns import apriori, association_rules
import pandas as pd
四、完整实战:电商购物篮分析
import pandas as pd
from mlxtend.frequent_patterns import apriori, association_rules
# ===== 1. 模拟电商交易数据 =====
# 每条记录是一个购物篮
transactions = [
['牛奶', '面包', '鸡蛋'],
['面包', '尿布', '啤酒', '鸡蛋'],
['牛奶', '尿布', '啤酒', '可乐'],
['面包', '牛奶', '尿布', '啤酒'],
['面包', '牛奶', '尿布', '可乐'],
['面包', '鸡蛋'],
['牛奶', '尿布', '啤酒'],
['牛奶', '面包', '尿布', '啤酒'],
['牛奶', '面包', '鸡蛋', '可乐'],
['面包', '尿布', '啤酒'],
]
# ===== 2. 转换成 One-Hot 编码 =====
# Apriori 需要 DataFrame 格式,每列是一个商品,每行是一笔交易
items = sorted(set(item for t in transactions for item in t))
onehot = []
for t in transactions:
onehot.append([1 if item in t else 0 for item in items])
df = pd.DataFrame(onehot, columns=items)
print("购物篮数据(One-Hot 编码):")
print(df.head())
"""
可乐 啤酒 尿布 面包 牛奶 鸡蛋
0 0 0 0 1 1 1
1 0 1 1 1 0 1
2 1 1 1 0 1 0
3 0 1 1 1 1 0
4 1 0 1 1 1 0
"""
# ===== 3. Apriori 找出频繁项集 =====
frequent_itemsets = apriori(
df,
min_support=0.2, # 最小支持度 20%
use_colnames=True, # 使用商品名而非索引
max_len=None # 不限项集大小
)
print(f"\n频繁项集(支持度 ≥ {0.2}):")
print(frequent_itemsets.sort_values('support', ascending=False))
"""
support itemsets
0 0.8 (面包)
1 0.6 (牛奶)
2 0.6 (尿布)
3 0.5 (鸡蛋)
4 0.5 (啤酒)
5 0.6 (面包, 牛奶)
6 0.5 (面包, 尿布)
7 0.4 (面包, 鸡蛋)
8 0.5 (牛奶, 尿布)
9 0.4 (牛奶, 啤酒)
10 0.4 (尿布, 啤酒)
11 0.4 (面包, 牛奶, 尿布)
12 0.3 (面包, 牛奶, 啤酒)
13 0.4 (牛奶, 尿布, 啤酒)
14 0.3 (面包, 牛奶, 尿布, 啤酒)
"""
# ===== 4. 生成关联规则 =====
rules = association_rules(
frequent_itemsets,
metric='lift', # 按提升度排序
min_threshold=1.0 # 只保留提升度 > 1 的规则
)
# 按提升度降序排列
rules = rules.sort_values('lift', ascending=False)
print("\n关联规则(按提升度排序):")
print(rules[['antecedents', 'consequents',
'support', 'confidence', 'lift']].round(3).to_string(index=False))
"""
antecedents consequents support confidence lift
1 (尿布, 啤酒) (牛奶) 0.400 1.000 1.667
0 (牛奶, 啤酒) (尿布) 0.400 1.000 1.667
8 (尿布, 啤酒) (面包, 牛奶) 0.300 0.750 1.250
5 (牛奶) (尿布) 0.500 0.833 1.389
13 (啤酒) (尿布) 0.400 0.800 1.333
3 (尿布) (面包, 牛奶) 0.400 0.667 1.111
...
最强的规则:
{尿布, 啤酒} → {牛奶} 提升度 = 1.667 ✅
{牛奶, 啤酒} → {尿布} 提升度 = 1.667 ✅
→ 这三样商品被一起购买的倾向非常强
"""
五、关联规则解读
如何看懂规则
规则:{尿布, 啤酒} → {牛奶}
支持度:0.40 → 40% 的订单同时包含这三样
置信度:1.00 → 买尿布和啤酒的顾客 100% 也买了牛奶
提升度:1.667 → 是随机购买概率的 1.667 倍
结论:
很可能是"年轻爸爸的周末采购组合"——促销时可以打包推荐
规则筛选建议
| 业务目标 | 筛选标准 | 原因 |
|---|---|---|
| 商品摆放 | 提升度 > 1.5, 置信度 > 0.5 | 真正的强关联,值得放在一起 |
| 交叉销售 | 提升度 > 1.2, 支持度 > 0.05 | 不要太严格,多发现机会 |
| 捆绑套餐 | 提升度 > 2.0, 置信度 > 0.6 | 强关联才有捆绑价值 |
| 个性化推荐 | 提升度 > 1.0 | 任何比随机好的关联都可用 |
几个需要避免的误区
误区 1:高置信度 ≠ 强关联
→ 畅销品本身置信度就高,需要用提升度排除
误区 2:关联 ≠ 因果
→ {啤酒}→{尿布} 不一定是因为因果关系
→ 可能只是"周五晚上年轻爸爸采购"这个隐藏因素
误区 3:稀有但有价值的规则容易被忽略
→ 高价值但低频的组合(如奢侈品关联)
→ 需要降低最小支持度,但会增加计算量
六、FP-Growth:Apriori 的改进
Apriori 的瓶颈
Apriori 需要反复扫描数据库来检查候选集的支持度。当数据量大时,效率很低。
Apriori 的复杂度:
每次生成新的候选集 → 扫描一次数据库
如果最大项集长度为 5 → 扫描 5 次
如果有 10 万笔交易、1 万种商品 → 非常慢
FP-Growth
FP-Growth(Frequent Pattern Growth)通过构建FP-Tree来避免反复扫描:
from mlxtend.frequent_patterns import fpgrowth
# FP-Growth 比 Apriori 快得多(尤其大数据集)
frequent_itemsets_fp = fpgrowth(
df,
min_support=0.2,
use_colnames=True
)
# 速度对比:
# 小数据(<1000 交易):Apriori ≈ FP-Growth
# 中数据(1000-1万):FP-Growth 快 2-5 倍
# 大数据(>1万):Apriori 几乎不可用,FP-Growth 仍然可行
| 对比 | Apriori | FP-Growth |
|---|---|---|
| 扫描数据库次数 | 多次(=最大项集长度) | 2 次 |
| 内存占用 | 小 | 大(需要构建树) |
| 小数据集 | 可用 | 可用 |
| 大数据集 | ❌ 慢 | ✅ 快 |
| 实现复杂度 | 简单 | 复杂 |
七、关联规则在推荐系统中的应用
基于关联规则的推荐流程
def recommend_by_rules(rules, purchased_items, top_n=5):
"""
基于关联规则做推荐
参数:
rules: 训练好的关联规则
purchased_items: 用户已购商品列表
top_n: 推荐数量
"""
recommendations = {}
for _, rule in rules.iterrows():
antecedents = set(rule['antecedents'])
consequents = set(rule['consequents'])
# 如果用户买了规则的前件
if antecedents.issubset(purchased_items):
# 规则推荐的后件中,用户还没买的
new_items = consequents - purchased_items
for item in new_items:
if item not in recommendations:
recommendations[item] = []
recommendations[item].append(rule['lift'])
# 按提升度均值排序
scored = [(item, np.mean(scores)) for item, scores in recommendations.items()]
scored.sort(key=lambda x: x[1], reverse=True)
return [item for item, _ in scored[:top_n]]
# 使用示例
user_items = {'牛奶', '面包'}
recs = recommend_by_rules(rules, user_items)
print(f"已购: {user_items} → 推荐: {recs}")
关联规则 vs 协同过滤
| 对比 | 关联规则 | 协同过滤 |
|---|---|---|
| 算法类型 | 无监督 | 无监督 |
| 数据要求 | 购物篮数据 | 用户-物品评分矩阵 |
| 可解释性 | ✅ 极强("买A的人也买B") | ⚠️ 较弱 |
| 冷启动 | ✅ 可以处理(基于商品而非用户) | ❌ 新用户无历史 |
| 覆盖率 | 低(只推荐强关联商品) | 高(任何商品都能推荐) |
| 实时性 | 不支持(需要周期性重新计算) | 支持实时增量更新 |
八、关联规则的局限
| 局限 | 说明 | 缓解方法 |
|---|---|---|
| 数据稀疏性 | 大部分商品被购买的频率很低 | 降低最小支持度,或聚合商品类别 |
| 规则数量爆炸 | 10 种商品 → 可能生成数千条规则 | 用提升度/置信度严格筛选 |
| 时效性 | 关联关系会随时间变化 | 滑动窗口,只使用近期数据 |
| 没有考虑价格 | 关联规则不考虑商品价格和利润 | 结合利润加权(高利润商品优先推荐) |
| 只看"买了什么" | 不知道用户为什么买 | 结合用户画像、购买意图分析 |
九、总结
| 概念 | 一句话理解 |
|---|---|
| 关联规则 | "买 A 的顾客中,有多大比例也买了 B" |
| 支持度 | 这条规则覆盖了多少数据——普遍性 |
| 置信度 | 这条规则有多可靠——买了 A 后买 B 的概率 |
| 提升度 | 这对商品是不是真的有关联——排除畅销品偏差 |
| Apriori | 通过剪枝高效搜索频繁项集的算法 |
| FP-Growth | 用树结构替代多次扫描的 Apriori 改进版 |
核心三句话:
- 关联规则是最容易理解的 ML 算法之一——"买 A 的人也买 B",业务人员一眼就能看懂
- 提升度比置信度更重要——高置信度可能只是因为商品本身畅销,提升度才能告诉你真正的强关联
- Apriori 适合小数据,FP-Growth 适合大数据——数据量大时 Apriori 的性能会急剧下降
更多推荐




所有评论(0)