拓冰建站拓冰建站
首页 / 资讯中心 / 正文

基于BreadBasket数据的关联规则实战:从清洗到2月18日单日对比

做数据分析这几年我见过太多人一上来就调包跑模型跑完却完全说不清结果在说什么。今天拿一个非常经典的数据集——BreadBasket 面包店交易数据——把关联分析也就是常说的购物篮分析从数据清洗到规则解读完整走一遍重点落在 2月18日 这个分析窗口上。这篇文章不会只贴代码而是把每一步为什么这么做、踩过哪些坑都讲清楚。想入门关联规则、准备面试、或者真要在零售场景里做商品捆绑分析的都值得花十分钟看完。先说清楚标题里的“2.18”是什么。这不是算法版本号是我在数据集里单独切出来的一个日期窗口。我当时只是想对比一下“单日规则”和“全量规则”有多大差异结果发现拿单日数据跑关联分析时阈值体系和全量数据完全不能混着用。这个坑后面会详细讲。1. 先搞清楚 BreadBasket 数据集到底装了什么1.1 数据从哪来、长什么样BreadBasket 是一个公开的面包店/咖啡店交易数据集最早流传于各类数据挖掘课程和竞赛社区里。它记录了一家店一段时间内每笔交易的明细流水每一行代表“某个交易编号下的某个商品”所以同一笔交易如果买了三样东西就会有三行记录。字段结构非常简洁字段名含义示例Transaction交易编号1Item商品名称CoffeeDate交易日期2017-02-18Time交易时间09:30:00整个数据集大概有两万多条流水覆盖几个月的数据量。商品种类不是特别多常见的有 Coffee、Bread、Cake、Tea、Toast、Pastry、Sandwich、Juice、Soup、Cookies、Muffin 等几十种。数据量不大家用笔记本完全跑得动用来练手非常合适。我这次实战的思路是先把全量数据清洗干净构造出“购物篮”然后跑全量关联规则作为基线再单独切出 2月18日 当天的数据跑一遍单日规则最后对比两组规则差异从中解读业务含义。1.2 用 pandas 做一次数据体检拿到数据第一步不是直接跑模型而是先看清楚数据质量。我习惯先加载数据随手做一轮基础体检。import pandas as pd df pd.read_csv(BreadBasket_DMS.csv) print(df.shape) print(df.head()) print(df.info())这一步看起来简单但信息量很大。你会看到数据量、字段类型、缺失情况基本能判断后续清洗要花多大力气。我跑完df.info()之后发现几个很明显的问题数据集里有空值记录Item字段里混入了NONE和ngrams这样的非法值Transaction编号存在大量重复。前两个问题好理解脏数据嘛过滤掉就行。第三个问题最容易翻车后面专门讲。千万不要跳过体检我见过太多人在不干净的数据上直接跑 Apriori出来的规则全是脏数据制造出来的假象。2. 动手之前关联规则那点事2.1 三个核心指标支持度、置信度、提升度购物篮分析的目标很简单发现“顾客买了 A 之后还倾向于买 B”的规律。在 BreadBasket 里最典型的就是买了咖啡的人是不是大概率也会拿一块蛋糕。要衡量这种规律关联规则引入了三个指标名字听起来吓人其实理解起来非常生活化。支持度Support衡量的是“这个商品组合在多少笔交易里出现过”。比如 100 笔交易里有 30 笔同时包含咖啡和蛋糕那{Coffee, Cake}的支持度就是 0.3。支持度太低说明这个组合太罕见即使规则成立也没有商业价值。公式表达就是Support(A → B) (包含 A 和 B 的交易数) / (总交易数)置信度Confidence衡量的是“买了 A 的交易里有多大比例也买了 B”。如果 40 笔买咖啡的交易里 30 笔买了蛋糕那Coffee → Cake的置信度就是 0.75。置信度反映的是规则的可靠性越高说明这个连带关系越强。Confidence(A → B) (包含 A 和 B 的交易数) / (包含 A 的交易数)提升度Lift是我最看重的指标。它衡量的是“买了 A 之后买 B 的概率比没买 A 的人买 B 的概率高多少”。如果提升度大于 1说明 A 和 B 存在正相关等于 1说明两者独立小于 1说明反而互相排斥。Lift(A → B) Confidence(A → B) / Support(B)举个例子假设全店有 20% 的交易包含蛋糕而买了咖啡的交易里有 60% 都买了蛋糕那提升度就是 0.6 / 0.2 3。这个 3 意味着“买咖啡的人买蛋糕的可能性是随机顾客的 3 倍”这才是真正有价值的信号。2.2 算法选型为什么用 Apriori购物篮分析最常见的算法是 Apriori它的核心思想是先找频繁项集再生成规则。所谓“频繁项集”就是支持度超过阈值的商品组合。Apriori 有个很聪明的剪枝策略如果一个商品组合不频繁那它的任何超集也一定不频繁。也就是说{Coffee, 鱼子酱}如果没人买那{Coffee, 鱼子酱, 面包}肯定也不会有人买。这样就能大幅缩小搜索空间。有人会问为什么不直接上 FP-GrowthFP-Growth 确实更快它只需要扫描两遍数据库不需要像 Apriori 那样反复生成候选项集。但 BreadBasket 这个数据量实在太小了几万条流水而已Apriori 跑起来毫无压力。而且 Apriori 更好理解、更容易调试作为学习案例非常合适。阈值怎么设是这里最值得说的地方。我一开始按全量数据跑min_support设成 0.01事务数大概几千笔相当于要求这个商品组合至少出现几十次低于这个次数统计意义就不大跑出来的规则也没法信。后来切到 2月18日 单日数据时事务数直接缩了一个数量级同样 0.01 的支持度可能只有一两笔交易规则立刻变得不稳定。所以单日分析时我会把支持度阈值往上提至少保证每个频繁项集有足够的样本支撑。3. 数据清洗与购物篮构造3.1 准备环境这次实战用到的库不多pandas 做数据处理mlxtend 跑 Apriorimatplotlib 和 seaborn 做可视化。pip install pandas mlxtend matplotlib seabornmlxtend 是一个机器学习扩展库里面封装了apriori和association_rules两个函数省去了手写算法的麻烦。我建议初学者先用封装好的库理解整个流程之后再尝试自己实现一遍算法理解会更深刻。3.2 清洗脏数据回到数据本身第一件事就是清掉Item字段里的非法值。df df.dropna() df df[df[Item] ! NONE] df df[df[Item] ! ngrams]这里的NONE一般是系统生成的无意义记录ngrams则是数据采集时混进去的噪声。这两类值必须在构造购物篮之前清干净否则它们在 Apriori 里会形成虚假的商品项干扰关联规则的输出。清洗完之后可以顺手看一眼商品频次确认数据是否符合直觉。print(df[Item].value_counts().head(10))正常情况下Coffee 一定排在第一位接下来是 Bread、Tea、Cake 这些常见品类。如果排序结果很奇怪比如某种冷门商品排在最前面就要回头检查是不是清洗步骤出了问题。3.3 生成唯一交易编号这个坑我必须单独拿出来说因为真的很容易踩。BreadBasket 数据集里的Transaction字段并不是全局唯一的它可能在每天都会重新计数。也就是说可能第一天有交易编号 1第二天也有交易编号 1。如果直接把这个字段当作购物篮的分组依据就会把不同日期、不同顾客的购买记录混成一个虚假的购物篮后面所有分析全部白做。解决办法是组合Transaction和Date两个字段生成一个全局唯一的交易标识。df[BasketID] df[Transaction].astype(str) _ df[Date].astype(str)跑关联分析之前一定要先用这个组合字段确认一下唯一性没有这一步后面算出来的支持度、置信度全都不作数。3.4 切出 2月18日 的数据并构造购物篮构造购物篮的本质是把“长表”转换成“宽表”。原始数据一行是一个商品现在要变成一行是一笔交易每一列是一个商品交易里包含哪个商品就填 1没有就填 0。这个 0/1 矩阵就是 Apriori 的输入。df_feb18 df[df[Date] 2017-02-18] def create_basket(df_sub): basket df_sub.groupby([BasketID, Item])[Item].count().unstack().fillna(0) def encode(x): return (x 0).astype(int) basket basket.apply(encode) return basket basket_all create_basket(df) basket_feb18 create_basket(df_feb18)这里用groupby按交易和商品分组然后unstack把商品变成列。因为同一个商品在同一笔交易里只算一次所以无论买了几份都统一编码成 1。print(basket_all.shape) print(basket_feb18.shape)看到两个矩阵的形状差异就能直观感受到单日数据量有多小。这也是为什么后面跑单日关联规则时必须单独调整支持度阈值。4. 跑 Apriori频繁项集与关联规则4.1 先找频繁项集购物篮矩阵构造好之后就可以直接调用 mlxtend 的apriori函数了。from mlxtend.frequent_patterns import apriori, association_rules frequent_itemsets apriori( basket_all, min_support0.01, use_colnamesTrue ) print(frequent_itemsets.sort_values(support, ascendingFalse).head(10))use_colnamesTrue的意思是让输出里的itemsets列直接显示商品名称而不是编号看起来更直观。这里min_support0.01的选择是有讲究的。如果这个数据集有几千笔有效交易那 0.01 的支持度意味着这个组合至少出现了几十次算下来是有统计基础的。如果你发现频繁项集数量太少或太多就上下调整这个阈值一般从 0.01 开始试比较合理。跑完之后看输出最高频的项集一定和商品频次排名一致比如{Coffee}单独的支持度很高。然后是几个高频组合比如{Coffee, Bread}、{Coffee, Cake}这些都是后面生成规则的原料。4.2 生成关联规则并按提升度排序有了频繁项集接下来用association_rules生成规则。rules association_rules( frequent_itemsets, metriclift, min_threshold1 ) rules rules.sort_values(lift, ascendingFalse) print(rules.head(10))这里metriclift、min_threshold1表示只保留提升度大于 1 的规则也就是只留下存在正相关的组合。提升度大于 1 是底线小于等于 1 的规则没有业务价值可以直接丢弃。如果你的 mlxtend 版本比较新有可能会提示需要传入num_itemsets参数遇到这种情况补上就行。rules association_rules( frequent_itemsets, metriclift, min_threshold1, num_itemsetslen(basket_all) )4.3 单日窗口和全量数据对比全量规则跑完之后用同样的流程对 2月18日 的数据单独跑一遍。注意单日事务数少支持度阈值要相应提高我这次直接设成了 0.03否则组合样本量太小规则没有参考价值。对比两组规则之后能看到非常有意思的现象对比维度全量数据2月18日单日事务数量几千笔几十到几百笔支持度阈值0.010.03需上调规则稳定性较稳定波动大易受个别顾客影响典型规则Coffee → Cake受当天天气、活动影响明显全量规则代表的是“店里的长期规律”适合做常规陈列和套餐设计。单日规则代表的是“当天发生了什么特殊事情”比如某一天天气冷热咖啡和汤类的关联可能特别强某一天是节日甜点和礼品类组合的置信度会明显上升。这个对比视角是我这次实战最大的收获。单日数据不适合单独看“准不准”而更适合用来找“差异点”。一旦发现全量规则和单日规则的排序差异很大就去倒查当天的业务动作、天气、促销活动往往能对得上号。5. 结果怎么用从规则到经营动作5.1 读懂几条典型规则跑完关联规则后千万不能只看表格里的数字要回到真实场景里去解释。以 BreadBasket 为例常见的高提升度规则大致这样Coffee → Cake这是店铺里最经典的搭配。买咖啡的人买蛋糕的意愿远高于随机顾客提升度通常很高。Toast → Coffee早餐场景的典型组合。吐司和咖啡几乎是一对固定搭配。Bread → Juice这说明有一部分顾客在买面包时倾向于搭配饮品而且可能不是咖啡类而是果汁类。解读规则的时候我会从顾客动线的角度去推演。顾客进门买咖啡等待出杯的几十秒里眼睛很容易扫到收银台旁边的蛋糕柜顺手就带一块——这个行为链完全解释得通。5.2 从规则到具体经营动作关联规则分析最大的价值是落地到业务动作不然就是白跑。根据跑出来的规则至少可以做三件事第一重新设计套餐。既然Coffee → Cake的关联强度很高就可以直接设计“咖啡 蛋糕”套餐价用套餐价引导顾客做出更符合店铺利益的购买决策。套餐价格不是简单打折而是要把毛利更高的商品和引流商品绑在一起。第二改造商品陈列。把高频关联的商品放在同一视线范围、同一动线区域。比如在咖啡出杯处放小蛋糕架把吐司和果酱放在同一个货架上减少顾客寻找成本。第三优化推荐系统。如果店里上线点单系统可以在顾客选完咖啡后弹窗推荐蛋糕推荐逻辑直接采用关联规则的结果。这种推荐不需要复杂模型一张规则表就能跑得很好。5.3 再进一步分时段、分场景的拓展做完单日和全量对比后我还习惯按时间维度继续拆。比如把数据分成上午、下午、晚上三个时段分别跑关联规则。上午时段早餐组合会非常强比如Coffee → Toast、Sandwich → Coffee下午时段甜点组合会上升晚上则可能出现Soup → Bread这类偏正餐的搭配。这个拓展不需要改代码只要把create_basket里的过滤条件从日期改成时间段就行。如果你发现不同时段的规则差异很明显就能据此安排员工排班、备货比例和推荐策略这才是数据分析该有的姿态。6. 踩坑记录与常见问题速查6.1 数据集本身的坑BreadBasket 这个数据集最经典的两个坑一个是NONE和ngrams脏数据一个是Transaction编号重复。前者直接污染商品项后者直接污染购物篮结构。如果不处理这两个问题就算代码跑通出来的规则也是错的。另外日期格式也值得留意。不同来源的数据集日期格式可能不一样有的是2017-02-18有的可能是18-02-2017读取之后一定要先用pd.to_datetime统一格式再去做筛选否则很容易因为字符串格式不同导致切不出来数据。6.2 算法参数与实现上的坑Apriori 跑出来的规则数量非常受阈值影响。我见过有人把min_support设成 0.001结果频繁项集爆炸式增长规则上百万条根本看不过来。支持度阈值不要一味求低要结合业务判断商品组合出现次数太少即便统计上相关也不值得投入运营资源。还有一点要提醒拿到规则表之后不要只看置信度排序也不要只看提升度排序。置信度高有时候只是因为商品本身热卖比如咖啡和蛋糕都很热销随机共现的概率本来就高。提升度高才说明组合确实有特殊关系。但如果提升度极度偏高比如超过 10往往是小样本造成的假象优先级反而要往后放。另外关联规则和灰色关联分析是两个完全不同的东西。灰色关联分析解决的是“多个因素序列之间的关联程度”问题常用于系统分析和评价和购物篮里的频繁项集挖掘不是一回事。搜资料的时候别搞混。6.3 业务解读的坑关联规则只能说明“经常一起出现”不能直接证明因果。Coffee → Cake可能只是因为咖啡本来就是引流品顾客买咖啡多顺手买蛋糕的概率自然就高。不代表只要把咖啡价格降低蛋糕销量就会跟着涨。想要验证因果关系得再做 AB 测试或者专门的因果推断分析。解读单日规则时也要格外小心。单日数据量小几个团体顾客的购买行为就可能大幅拉高某个组合的置信度。所以单日规则只能作为线索用于生成假设验证还得靠更长周期的数据。最后再分享一个我个人的实操习惯每次跑完关联规则先把 Top 20 规则原样打印出来用业务常识快速过一遍。凡是连自己这关都过不了、解释不通的规则直接放一边。真正有价值的是那些既在数据上显著、又在业务上说得通、还能马上落地成动作的规则。带着这个标准去看结果你才不会在几千条规则里迷失方向。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门