ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Apriori关联规则实战:购物篮分析从数据清洗到规则筛选

Apriori关联规则实战:购物篮分析从数据清洗到规则筛选 简介这份资源面向数据挖掘初学者与商业分析入门者围绕Apriori关联规则的市场购物篮分析展开帮助读者从零完成一次完整的关联规则挖掘实践。压缩包共4个文件约20.36MB包含csv与xlsx两种格式的原始交易数据集、ipynb代码笔记以及html格式的代码导出覆盖数据读取、频繁项集发现、规则生成与结果评估等环节。资源以真实购物篮数据为案例读者可据此动手实现支持度与置信度阈值设定、强关联规则筛选并理解数据预处理、参数调整与规则解释的完整流程。目前已有270人学习下载适合希望把关联规则理论落到代码与数据上的学习者也可作为课程作业或零售场景分析的参考案例。1. 购物篮里的隐藏货架Apriori 关联规则到底在挖什么超市收银台每天吐出几千条流水真正值钱的不是“今天卖了多少”而是“谁和谁总是一起被买走”。数据挖掘实战里基于 Apriori 关联规则的市场购物篮分析就是干这件事的把订单流水拆成一个个“项集”从高频共现里找出像“啤酒 尿布”这种反直觉但能直接改货架、改促销、改推荐的规则。它适合谁手里有交易明细、想用 Python 做数据分析与数据挖掘实战的人尤其是做零售、电商、餐饮、SaaS 套餐组合的从业者。难点不在算法公式而在数据怎么整理、参数怎么设、结果怎么筛。这篇就按“数据集 代码”的实战路径把 Apriori 从黑匣子拆成能抄作业的步骤顺带把翻车点讲透。2. 先搞懂 Apriori 的支撑度、置信度和提升度别把三个指标混着用2.1 三个指标到底在回答什么问题Apriori 的核心不是“算得快”而是“算得准”。它靠三个指标把海量组合筛成可解释的规则。支持度Support一个项集在所有订单里出现的比例。比如“牛奶 面包”在 1000 单里出现 80 次支持度就是 8%。它回答的是这个组合够不够普遍。置信度Confidence买了 A 的人里有多少也买了 B。公式是 Support(A∪B) / Support(A)。它回答的是这条规则有多可信。提升度Lift置信度除以 B 本身的支持度。Lift 1 说明 A 对 B 有正向拉动Lift 1 说明两者基本独立Lift 1 说明 A 出现时 B 反而被抑制。它回答的是这个关联是不是“真有用”而不是因为 B 本来就卖得好。很多人只盯置信度结果挖出一堆“买了牙膏的人 90% 也买牙刷”但牙刷本来就 90% 的人会买提升度接近 1这种规则没有增量价值。实战里我一般先卡支持度保覆盖再卡置信度保可信最后用提升度排序看增量。2.2 Apriori 的“逐层剪枝”为什么能跑得动暴力枚举所有组合是 2^n 级别商品一多就炸。Apriori 利用一个性质如果一个项集是频繁的那它的所有子集也必须是频繁的。反过来如果一个项集不频繁它的超集也一定不频繁直接剪掉。流程分两步生成频繁项集从 1 项集开始扫描数据保留满足最小支持度的再用频繁 k 项集两两组合生成 k1 候选扫描剪枝直到没有新的频繁项集。生成规则对每个频繁项集拆成“前件 → 后件”保留满足最小置信度的规则再算提升度。这个“逐层”思路决定了参数不能乱设最小支持度太高频繁项集太少规则出不来太低候选爆炸内存和耗时直接翻车。2.3 用 Python 跑通最小 Apriori 示例先装库。常见做法是用mlxtend它封装了 Apriori 和关联规则生成接口稳定适合实战。pip install mlxtend pandas下面是一段最小可复现代码手工造 5 条订单跑通全流程。import pandas as pd from mlxtend.preprocessing import TransactionEncoder from mlxtend.frequent_patterns import apriori, association_rules # 原始交易数据每行是一个订单里的商品列表 transactions [ [牛奶, 面包, 尿布], [啤酒, 尿布], [牛奶, 啤酒, 尿布, 鸡蛋], [牛奶, 面包, 啤酒, 尿布], [面包, 牛奶, 尿布] ] # 1. 独热编码把交易列表转成 0/1 矩阵 te TransactionEncoder() te_array te.fit(transactions).transform(transactions) df pd.DataFrame(te_array, columnste.columns_) # 2. 挖掘频繁项集最小支持度 0.4即至少出现在 40% 的订单里 frequent_itemsets apriori(df, min_support0.4, use_colnamesTrue) # 3. 生成关联规则最小置信度 0.7 rules association_rules(frequent_itemsets, metricconfidence, min_threshold0.7) # 4. 按提升度排序看增量价值 rules rules.sort_values(bylift, ascendingFalse) print(rules[[antecedents, consequents, support, confidence, lift]])逻辑说明TransactionEncoder把“订单里有哪些商品”转成布尔矩阵这是 Apriori 的标准输入。apriori的min_support控制频繁项集门槛association_rules的min_threshold配合metric控制规则门槛。最后按lift排序是因为支持度和置信度高的规则未必有增量。参数说明min_support从 0.01 到 0.1 是零售场景常见起点订单量大可以更低min_threshold用置信度时一般 0.5 到 0.8看业务容忍度lift没有硬门槛但低于 1 的规则基本可以丢。提示mlxtend的apriori要求输入是布尔或 0/1 的 DataFrame列名就是商品名。如果直接喂原始订单列表会报类型错误。3. 把原始订单洗成 Apriori 能吃的格式从 CSV 到布尔矩阵的完整链路3.1 真实数据集长什么样先做字段盘点实战里的购物篮数据通常是一张明细表常见字段有订单号、商品名、商品数量、下单时间、会员 ID、门店 ID。Apriori 只关心“一个订单里出现了哪些商品”所以第一步是聚合按订单号把商品名收成一个列表。先看数据长什么样import pandas as pd # 读取交易明细假设文件是 utf-8 编码 raw pd.read_csv(transactions.csv, encodingutf-8) print(raw.head()) print(raw.columns.tolist()) print(raw.shape)逻辑说明先确认列名和规模避免后面写错字段。如果列名是中文或带空格统一重命名减少后续出错。# 统一列名方便后续引用 raw raw.rename(columns{ 订单编号: order_id, 商品名称: item, 数量: qty }) # 只保留有效订单和商品 raw raw.dropna(subset[order_id, item]) raw raw[raw[qty] 0]参数说明dropna去掉订单号或商品名为空的脏数据qty 0排除退货、赠品等负数量或零数量记录。这一步不做后面会出现“空订单”和“负项集”。3.2 按订单聚合商品列表处理重复项同一个订单里同一商品可能出现多次Apriori 的布尔矩阵只关心“有没有”所以要去重。# 按订单聚合商品去重后转成列表 basket raw.groupby(order_id)[item].apply(lambda x: list(set(x))).reset_index() basket.columns [order_id, items] # 过滤掉只有 1 个商品的订单单商品无法产生关联 basket basket[basket[items].apply(len) 2] print(basket.head()) print(有效订单数:, len(basket))逻辑说明set(x)去重避免同一商品在同一订单里重复计数过滤单商品订单是因为关联规则至少需要两个商品。参数上len 2是硬门槛没有可调空间。3.3 编码成布尔矩阵并落盘方便复用每次跑 Apriori 都重新编码很浪费时间尤其是数据量大时。我一般把编码后的矩阵存成 parquet 或 pickle。from mlxtend.preprocessing import TransactionEncoder import pandas as pd te TransactionEncoder() te_array te.fit(basket[items]).transform(basket[items]) df pd.DataFrame(te_array, columnste.columns_) # 落盘下次直接读 df.to_parquet(basket_encoded.parquet, indexFalse) print(矩阵形状:, df.shape) print(商品数:, len(te.columns_))逻辑说明te.columns_保存了所有商品名顺序和矩阵列一致。存成 parquet 比 CSV 快且保留布尔类型。参数上indexFalse避免多出一列索引。注意如果商品名里有特殊字符或极长文本先做标准化去空格、统一大小写否则同一个商品会被拆成多个列规则直接失真。3.4 大数据量下的分块与稀疏处理订单超过几十万时布尔矩阵会非常宽商品数可能上万内存容易爆。常见做法有两种只保留高频商品先统计商品出现次数过滤掉出现次数低于某个阈值的商品再编码。用稀疏矩阵mlxtend的apriori对稠密 DataFrame 更友好如果商品数过万建议先用scipy.sparse存再按需转成稠密子集。# 只保留出现次数 50 的商品降低矩阵宽度 item_counts raw[item].value_counts() valid_items item_counts[item_counts 50].index raw_filtered raw[raw[item].isin(valid_items)] # 重新聚合 basket_filtered raw_filtered.groupby(order_id)[item].apply(lambda x: list(set(x))).reset_index() basket_filtered basket_filtered[basket_filtered[item].apply(len) 2]参数说明50是经验值订单量 10 万级可以用 20 到 100 之间订单量小就降到 5 到 10。过滤太狠会丢掉长尾组合过滤太松内存吃紧需要根据机器配置试。4. 参数怎么调、规则怎么筛把 Apriori 输出变成能落地的货架建议4.1 最小支持度的选择从业务覆盖率倒推最小支持度不是拍脑袋定的。我一般先问业务一个组合至少覆盖多少订单才值得关注如果日订单 5000希望一个规则至少覆盖 50 单那支持度就是 50/5000 0.01。如果希望覆盖 200 单就是 0.04。# 按业务覆盖率倒推最小支持度 total_orders len(basket) min_orders 50 # 业务上希望一个组合至少覆盖的订单数 min_support min_orders / total_orders print(建议最小支持度:, round(min_support, 4)) frequent_itemsets apriori(df, min_supportmin_support, use_colnamesTrue) print(频繁项集数量:, len(frequent_itemsets))逻辑说明先算业务门槛再跑 Apriori。如果频繁项集数量为 0 或个位数说明支持度太高如果几万条说明太低需要往上调。4.2 置信度和提升度的组合筛选置信度控制“买了 A 又买 B”的比例提升度控制“比随机买 B 强多少”。实战里我一般这样筛置信度 0.5低于这个值规则太弱业务不敢用。提升度 1.2低于 1.2 的规则增量不明显容易是“本来就卖得好”的伪关联。支持度 业务门槛保证规则有足够覆盖。rules association_rules(frequent_itemsets, metricconfidence, min_threshold0.5) # 组合筛选 rules_filtered rules[ (rules[confidence] 0.5) (rules[lift] 1.2) (rules[support] min_support) ].sort_values(bylift, ascendingFalse) print(rules_filtered[[antecedents, consequents, support, confidence, lift]].head(20))逻辑说明association_rules先用置信度粗筛再用 DataFrame 条件做精筛。antecedents是前件consequents是后件业务上通常把前件当“触发条件”后件当“推荐目标”。参数说明lift 1.2是经验阈值不同行业不一样快消品可以放到 1.1高客单价商品可以提到 1.5。confidence 0.5也是经验值规则太少就降到 0.3 试。4.3 规则去重与业务可解释性处理Apriori 会输出大量对称规则比如“牛奶 → 面包”和“面包 → 牛奶”可能同时出现。业务上只需要保留一个方向通常选置信度更高或提升度更高的那条。# 构造规则字符串去重时保留提升度最高的 rules_filtered[rule] rules_filtered.apply( lambda row: tuple(sorted(list(row[antecedents]) list(row[consequents]))), axis1 ) # 按规则集合去重保留 lift 最大的 rules_dedup rules_filtered.sort_values(lift, ascendingFalse).drop_duplicates(subsetrule) print(去重后规则数:, len(rules_dedup))逻辑说明把前件和后件合并成一个排序后的元组作为规则唯一标识再按提升度降序去重。这样保留的是最强的那条方向。提示如果业务需要指定方向比如“买了 A 才推荐 B”就不要去重直接按前件过滤。4.4 把规则导出成货架建议表最终输出要能让运营直接看。我一般导出成 CSV包含前件、后件、支持度、置信度、提升度再加一列“建议动作”。# 导出规则表 output rules_dedup[[antecedents, consequents, support, confidence, lift]].copy() output[antecedents] output[antecedents].apply(lambda x: , .join(list(x))) output[consequents] output[consequents].apply(lambda x: , .join(list(x))) output[建议动作] output.apply( lambda row: f将 {row[antecedents]} 与 {row[consequents]} 相邻陈列或组合促销, axis1 ) output.to_csv(basket_rules.csv, indexFalse, encodingutf-8-sig) print(output.head())逻辑说明antecedents和consequents是 frozenset 类型导出前转成字符串。utf-8-sig保证 Excel 打开不乱码。参数上建议动作是模板文案实际可以按业务替换成“捆绑销售”“满减凑单”“首页推荐”等。5. 避坑与排查Apriori 购物篮分析最常见的 5 个翻车现场5.1 现象频繁项集为 0 或只有个位数原因最小支持度设得太高或者数据过滤太狠把大量订单砍掉了。常见于订单量小但商品数多的场景。解决先算商品出现频率分布看 Top 商品的占比。如果最高频商品支持度只有 0.05那min_support设 0.1 肯定出不来。把min_support降到 0.01 到 0.03 再试同时检查basket过滤条件是不是把单商品订单砍太多。5.2 现象规则数量爆炸几万条没法看原因最小支持度和置信度都设太低候选组合太多。或者商品没有做标准化同一个商品被拆成多个列导致虚假组合。解决先提高min_support到 0.02 以上再提高confidence到 0.6。同时检查商品名是否统一比如“可乐”和“可口可乐”要合并。可以用raw[item].nunique()看商品数如果超过 5000先做高频过滤。5.3 现象提升度很高但业务不认原因样本偏差。比如某个组合只出现在少数订单里支持度极低但提升度很高。这种规则统计上显著业务上不可用。解决加支持度下限。我一般要求规则支持度至少覆盖 30 到 50 个订单低于这个数不看。同时看置信度的绝对值如果置信度低于 0.3即使提升度高也先放一边。5.4 现象运行时间过长或内存溢出原因布尔矩阵太宽或者apriori在低支持度下生成大量候选。常见于商品数上万、订单数十万的场景。解决先做商品高频过滤只保留出现次数 50 的商品再用min_support从 0.05 开始试逐步降低。如果还是慢考虑用 FP-Growth 替代 Apriorimlxtend也提供了fpgrowth速度更快结果等价。from mlxtend.frequent_patterns import fpgrowth # FP-Growth 替代 Apriori参数含义一致 frequent_itemsets_fp fpgrowth(df, min_support0.02, use_colnamesTrue) print(FP-Growth 频繁项集数量:, len(frequent_itemsets_fp))逻辑说明fpgrowth接口和apriori一致但内部用 FP 树不需要反复扫描数据适合大数据量。参数上min_support可以设得比 Apriori 更低因为速度更快。5.5 现象规则方向反了业务用不上原因Apriori 生成的是无向关联antecedents和consequents可以互换。业务上通常需要“买了 A 推荐 B”但输出里可能是“买了 B 推荐 A”。解决按业务需求过滤前件。比如只保留前件包含指定商品的规则或者按置信度排序选置信度更高的方向作为推荐方向。# 只保留前件包含“尿布”的规则 rules_diaper rules_filtered[ rules_filtered[antecedents].apply(lambda x: 尿布 in x) ] print(rules_diaper[[antecedents, consequents, confidence, lift]])逻辑说明antecedents是 frozenset用in判断是否包含目标商品。参数上可以把“尿布”替换成业务关心的触发商品。6. 进阶技巧用规则分层和可视化把 Apriori 结果讲成业务故事规则跑出来只是第一步真正难的是让运营、采购、店长愿意用。我一般做两件事分层和可视化。分层是按提升度和支持度把规则分成四象限象限支持度提升度业务含义建议动作高支持高提升高 1.5核心组合覆盖广且增量强相邻陈列、组合促销高支持低提升高1.0~1.2习惯性组合增量有限保持现状不做额外动作低支持高提升低 1.5小众强关联适合精准推荐线上推荐、会员定向低支持低提升低 1.0噪声或抑制关系丢弃或单独分析可视化用散点图横轴支持度纵轴提升度点的大小表示置信度。这样一眼能看出哪些规则值得跟进。import matplotlib.pyplot as plt plt.figure(figsize(10, 6)) plt.scatter(rules_filtered[support], rules_filtered[lift], srules_filtered[confidence] * 200, alpha0.5) plt.xlabel(Support) plt.ylabel(Lift) plt.title(Basket Rules: Support vs Lift) plt.axhline(y1.2, colorr, linestyle--, labelLift 1.2) plt.legend() plt.show()逻辑说明s参数用置信度放大点的大小alpha控制透明度避免重叠。axhline画出提升度阈值线方便快速筛选。参数上s的倍数可以按数据量调整点太多就调小。还有一个实战技巧把规则按商品品类聚合。比如“尿布 → 啤酒”可能只是个案但如果“婴儿用品 → 啤酒”整体提升度都高那就是品类级的关联业务价值更大。做法是把商品映射到品类再跑一次 Apriori或者对规则做品类归并。# 假设有商品到品类的映射表 category_map { 尿布: 婴儿用品, 啤酒: 酒饮, 牛奶: 乳制品, 面包: 烘焙, 鸡蛋: 生鲜 } # 把规则前件和后件转成品类 def to_category(itemset): return set(category_map.get(item, 其他) for item in itemset) rules_filtered[antecedent_cat] rules_filtered[antecedents].apply(to_category) rules_filtered[consequent_cat] rules_filtered[consequents].apply(to_category) # 按品类组合聚合看平均提升度 cat_rules rules_filtered.groupby([antecedent_cat, consequent_cat]).agg( avg_lift(lift, mean), rule_count(lift, count) ).reset_index().sort_values(avg_lift, ascendingFalse) print(cat_rules.head(10))逻辑说明to_category把商品级项集映射成品类级项集groupby聚合后看品类间的平均提升度。参数上category_map需要业务提供没有映射表的商品归到“其他”。这样输出的品类级规则更适合采购和招商用。最后说个我自己的习惯每次跑完 Apriori先不看规则先看频繁项集的分布。如果 1 项集里 Top 商品占比超过 30%说明数据头部效应太强规则容易被大单品主导这时候要么提高支持度要么对商品做加权。这个习惯帮我省了很多次“规则看起来很美但业务不认”的返工。希望帮到你。本文还有配套的精品资源点击获取
返回列表