用Python手把手教你复现超市购物篮分析:从Apriori算法到实战规则挖掘
超市购物篮分析是零售行业洞察消费者行为的经典工具。想象一下,当顾客把啤酒和尿布放进同一个购物篮时,背后隐藏着怎样的消费逻辑?本文将用Python带你从零实现Apriori算法,揭示商品之间的关联奥秘。
1. 关联规则分析基础认知
关联规则分析的核心目标是发现数据集中项目之间的"如果...那么..."关系。在超市场景中,典型的关联规则表现为"如果顾客购买A商品,那么有X%的概率会同时购买B商品"。这种分析依赖三个关键指标:
- 支持度(Support):规则中所有商品同时出现的概率
- 置信度(Confidence):购买A商品的顾客中同时购买B商品的比例
- 提升度(Lift):规则中商品组合出现的频率与它们独立出现频率的比值
计算示例(假设总交易数1000):
| 商品组合 | 出现次数 | 支持度 | 置信度计算 |
|---|---|---|---|
| 啤酒 | 200 | 20% | - |
| 尿布 | 150 | 15% | - |
| 啤酒+尿布 | 100 | 10% | 100/200=50% |
提示:实际应用中通常设置最小支持度(如5%)和最小置信度(如30%)阈值来筛选有意义的规则
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Apriori算法实现详解
Apriori算法通过"逐层搜索"发现频繁项集,其核心是向下闭包性:如果一个项集不频繁,它的所有超集也一定不频繁。我们将分步骤实现算法核心函数。
2.1 构建初始候选项集
python复制def createC1(dataset):
"""生成大小为1的候选项集"""
C1 = set()
for transaction in dataset:
for item in transaction:
item_set = frozenset([item]) # 使用不可变集合
C1.add(item_set)
return C1
这个函数遍历每笔交易中的每个商品,创建包含单个商品的候选项集。使用frozenset而非普通集合是为了后续能作为字典键使用。
2.2 扫描数据集计算支持度
python复制def scanD(D, Ck, min_support):
"""筛选满足最小支持度的项集"""
ssCnt = {}
for tid in D:
for candidate in Ck:
if candidate.issubset(tid):
ssCnt[candidate] = ssCnt.get(candidate, 0) + 1
num_items = float(len(D))
retList = []
support_data = {}
for key in ssCnt:
support = ssCnt[key] / num_items
if support >= min_support:
retList.insert(0, key)
support_data[key] = support
return retList, support_data
常见问题排查:
- 数据集D需要先转换为集合列表:
D = [set(trans) for trans in dataset] - 当支持度计数为0时,检查商品名称是否统一(大小写、空格等)
2.3 生成更高阶候选项集
python复制def aprioriGen(Lk, k):
"""根据频繁k-1项集生成候选k项集"""
retList = []
lenLk = len(Lk)
for i in range(lenLk):
for j in range(i+1, lenLk):
# 前k-2项相同时合并两个集合
L1 = list(Lk[i])[:k-2]
L2 = list(Lk[j])[:k-2]
if L1 == L2:
retList.append(Lk[i] | Lk[j])
return retList
合并逻辑示例(k=3时):
- 输入:Lk = [{'A','B'}, {'A','C'}, {'B','C'}]
- 输出:retList = [{'A','B','C'}]
3. 完整Apriori算法实现
将上述函数组合成完整算法流程:
python复制def apriori(dataset, min_support=0.5):
"""完整Apriori算法实现"""
# 初始化
C1 = createC1(dataset)
D = [set(trans) for trans in dataset]
L1, support_data = scanD(D, C1, min_support)
L = [L1] # 存储各阶频繁项集
k = 2
# 迭代生成更高阶项集
while len(L[k-2]) > 0:
Ck = aprioriGen(L[k-2], k)
Lk, supK = scanD(D, Ck, min_support)
support_data.update(supK)
L.append(Lk)
k += 1
return L, support_data
算法执行流程图示:
- 生成C1 → 筛选L1
- 基于L1生成C2 → 筛选L2
- 重复直到无法生成新的频繁项集
4. 关联规则挖掘实战
获得频繁项集后,我们需要从中提取有意义的关联规则:
4.1 计算规则置信度
python复制def calcConf(freqSet, H, supportData, ruleList, min_conf=0.7):
"""计算并筛选满足最小置信度的规则"""
prunedH = []
for conseq in H:
conf = supportData[freqSet] / supportData[freqSet - conseq]
if conf >= min_conf:
ruleList.append((freqSet - conseq, conseq, conf))
prunedH.append(conseq)
return prunedH
4.2 递归生成关联规则
python复制def generateRules(L, supportData, min_conf=0.7):
"""从频繁项集生成关联规则"""
ruleList = []
for i in range(1, len(L)): # 从二阶项集开始
for freqSet in L[i]:
H1 = [frozenset([item]) for item in freqSet]
if i > 1:
ruleFromConseq(freqSet, H1, supportData, ruleList, min_conf)
else:
calcConf(freqSet, H1, supportData, ruleList, min_conf)
return ruleList
4.3 超市数据集实战应用
使用模拟的超市购物数据演示完整流程:
python复制# 示例数据集
dataset = [
['牛奶', '面包', '尿布'],
['可乐', '面包', '尿布', '啤酒'],
['牛奶', '尿布', '啤酒', '鸡蛋'],
['面包', '牛奶', '尿布', '啤酒'],
['面包', '牛奶', '尿布', '可乐']
]
# 执行Apriori算法
L, support_data = apriori(dataset, min_support=0.4)
# 生成关联规则
rules = generateRules(L, support_data, min_conf=0.7)
# 输出结果
for rule in rules:
print(f"规则:{tuple(rule[0])} → {tuple(rule[1])},置信度:{rule[2]:.2f}")
典型输出示例:
code复制规则:('尿布',) → ('啤酒',),置信度:0.75
规则:('啤酒',) → ('尿布',),置信度:1.00
规则:('牛奶',) → ('尿布',),置信度:1.00
5. 性能优化与工程实践
原始Apriori算法存在多次扫描数据库的问题,实际应用中需要考虑以下优化策略:
5.1 数据结构优化
使用垂直数据格式(item-tidset)加速支持度计算:
python复制from collections import defaultdict
def create_vertical_dataset(dataset):
item_tids = defaultdict(set)
for tid, transaction in enumerate(dataset):
for item in transaction:
item_tids[item].add(tid)
return item_tids
5.2 并行计算优化
利用多核CPU并行化候选集生成和支持度计算:
python复制from multiprocessing import Pool
def parallel_scan(args):
"""并行化的支持度计算"""
D, Ck_partition, min_support = args
local_cnt = {}
for tid in D:
for candidate in Ck_partition:
if candidate.issubset(tid):
local_cnt[candidate] = local_cnt.get(candidate, 0) + 1
return local_cnt
5.3 实际应用建议
- 对大规模数据,考虑使用FP-Growth等更高效的算法
- 商品ID化处理可显著提升性能(如用数字代替字符串)
- 动态调整支持度阈值:初期可用较高阈值快速筛选,再逐步降低
在真实项目中,我发现将商品按类别分层(如先分析大类关联,再深入子类)能有效提升分析效率。例如先发现"乳制品→烘焙食品"的关联,再具体分析"酸奶→全麦面包"的关系。
