Python关联分析:从Apriori到规则筛选,找出真正可落地的强关联规则
2026/9/11 22:52:16 网站建设 项目流程

1. 先回答一个扎心的问题:跑完apriori,你真敢用那些规则吗?

前段时间帮一个做零售数据的朋友看项目,他兴冲冲地跑了一版Python关联分析,把经典的apriori算法套在门店半年的销售流水上,出了几百条频繁项集和关联规则。朋友圈里晒出来的热力图很好看,可当我问他"这些规则里你准备落地哪几条、怎么落地"时,他愣住了。这大概是我见过最普遍的现象——频繁项集和关联规则在Python里跑出来很容易,但"哪些规则真正值得用"这个问题,才是整个项目里最容易被跳过的关键环节。

这不是技术门槛的问题,而是评价体系的问题。很多人对关联分析的认知停留在"啤酒和尿布"的故事上,觉得只要支持度和置信度跑过阈值,就能拿到一条"可落地的策略"。但实际跑过几轮真实数据之后你会发现,高置信度规则里充斥着一堆废话,比如"买牛奶的人也会买面包",这种规则放在业务里既没法指导选品,也没法做捆绑促销,因为它反映的只是两个高频商品恰好经常同时出现,并没有给你任何额外的决策增量。

这篇文章我想从一个完整项目验收的角度聊聊Python关联分析这件事:从频繁项集的挖掘,到关联规则的生成,再到规则价值的评估和筛选。重点放在后面两个环节,也就是"哪些规则真正值得用"。文章里会用一组可以手算的小样本数据贯穿始终,方便你把每一步的数学逻辑对照着代码看明白。同时会给出mlxtend库的完整实操代码,以及我在真实项目里踩过的坑。适合正在做销售数据、用户行为日志、内容推荐标签分析的同学,也适合刚开始接触数据挖掘、想知道apriori到底在算什么的新手。

2. 频繁项集:从"什么经常一起出现"到apriori剪枝原理

2.1 先搞清楚频繁项集到底在算什么

关联分析的输入通常是一组事务数据,每一行是一次交易、一次会话、或者一条日志,每一事务里包含若干个项目。比如超市里一个订单就对应一条事务,订单里的每个商品就是一个项目。关联分析要回答的问题是:哪些项目组合在事务里经常一起出现,而且出现的频率高到不像偶然。

这个"经常一起出现"在数学上就叫支持度。某个项集的支持度,就是包含该项集的事务数除以总事务数。比如表1这组模拟的超市小票数据,共5条事务:

事务ID购买商品
T1牛奶, 面包, 尿布
T2牛奶, 面包
T3牛奶, 尿布, 啤酒
T4面包, 尿布, 啤酒
T5牛奶, 面包, 尿布, 啤酒

计算项集{牛奶, 面包}的支持度,它出现在T1、T2、T5这三条事务里,所以support({牛奶,面包}) = 3/5 = 0.6。如果事先设定一个最小支持度阈值,比如min_support=0.4,那么所有支持度≥0.4的项集就叫频繁项集。在上面的数据里,{牛奶}、{面包}、{尿布}、{啤酒}、{牛奶,面包}、{牛奶,尿布}、{面包,尿布}、{尿布,啤酒}、{牛奶,面包,尿布}、{牛奶,尿布,啤酒}、{面包,尿布,啤酒}这些都是频繁项集。

2.2 apriori的剪枝思想:为什么不用穷举所有组合

如果有几十上百个不重复商品,组合数会爆炸。假设有100个商品,光大小为3的项集就有100×99×98/6,约16万个,更别说更大尺寸的项集。apriori算法最核心的贡献是一条先验原理:如果一个项集是非频繁的,那么它的所有超集也一定是非频繁的。

这个逻辑很好理解。{牛奶,面包}的支持度是0.6,它只会低于等于{牛奶}和{面包}各自的支持度,因为一个项集要同时包含更多项目,能命中它的事务只会更少,不会更多。反过来,如果{啤酒,红酒}的支持度只有0.1,那{啤酒,红酒,奶酪}的支持度无论如何不可能超过0.1,所以在扩展候选项集的时候,这些"注定没戏"的组合可以直接剪掉,不用白费算力。

用Python实现apriori,最常用的库是mlxtend。它的调用方式非常简洁,后面会给出完整代码。这里想强调的是,理解剪枝原理能帮你规避一个常见的调参误区:不要为了"多挖出一些规则"把min_support压得特别低。在真实数据集上,min_support如果低于0.01,频繁项集数量会呈指数级上涨,很多出现在几十条事务里的三四项集都会被捞出来,但它们对业务来说基本就是噪声。频繁项集的数量级突增,往往不是因为"找到了宝藏",而是因为"阈值放得太松"。

2.3 Python里两套主流实现,怎么选

做频繁项集挖掘时,我一般会根据数据量级在mlxtend和efficient-apriori之间选择。mlxtend的frequent_patterns.apriori功能完整,配合association_rules可以一站式生成规则,适合中小规模数据,而且返回的结果是DataFrame,后面做条件筛选非常方便。efficient-apriori用了一种更紧凑的内部存储策略,在数据量大、项目数多的时候运行更快,也能直接返回规则对象。

如果你发现apriori在大数据量下慢得离谱,可以试试用mlxtend.frequent_patterns.fpgrowth,它是FP-Growth算法的实现,不需要反复扫描数据库生成候选集,速度通常比apriori快一个量级。我的经验是:平时做探索性分析、几千到几万条事务,直接用mlxtend的apriori就够了;跑全量数据或项目数超过几万个,可以换fpgrowth,API几乎一样,迁移成本很低。

3. 置信度、提升度与那些"看着很强其实鸡肋"的规则

3.1 从频繁项集到关联规则:置信度的条件概率错觉

拿到频繁项集之后,下一步是生成关联规则。一条规则写成A→B的形式,表示"购买A的情况下,有多大倾向购买B"。这里唯一的关键指标是置信度,公式是confidence(A→B) = support(A∪B) / support(A)。用上面的例子,{牛奶}→{面包}的置信度 = 0.6 / 0.8 = 0.75。翻译成人话:在所有买了牛奶的人里,75%的人同时买了面包。

到这里很多人就开始下结论了:这条规则置信度0.75,很高,可以做捆绑促销。但实际项目里,这就是最大的认知陷阱。置信度只描述了"条件概率"这一个侧面,它完全没有考虑B本身在全部事务里有多常见。如果面包本身就是热销品,80%的顾客都会买面包,那么"买牛奶的人75%买面包"甚至比"不买牛奶的人反而买面包"的比例更低。换句话说,牛奶和面包其实可能存在某种程度的竞争关系,而不是协同关系。

3.2 提升度帮你打破条件概率错觉

为了修正置信度不看基线的问题,关联分析引入提升度:lift(A→B) = confidence(A→B) / support(B)。这个指标衡量的是"已知A之后,B发生的概率相对B自然发生的概率放大了多少倍"。

拿{牛奶}→{面包}继续算:confidence = 0.75,support(面包) = 4/5 = 0.8,lift = 0.75 / 0.8 = 0.9375。这个数值小于1,说明买牛奶实际上轻微降低了买面包的概率,这条规则虽然置信度很高,但对业务来说不但没有正向价值,还可能是反向的。再看{尿布}→{啤酒}这条规则:confidence = 3/4 = 0.75,support(啤酒) = 3/5 = 0.6,lift = 0.75 / 0.6 = 1.25。提升度大于1,说明尿布和啤酒之间存在正向关联,"买尿布的人更可能买啤酒"这个结论是站得住的。

关于lift的解读,有一条很实用的经验线:lift > 1表示正向关联,lift = 1表示两个事件独立,lift < 1表示负向关联。实际项目里我会要求候选规则lift至少大于1.1,否则即使置信度很高,也说明不了什么增量价值。千万不要只盯着置信度看,那样你筛出来的规则大概率是"高频商品互相捆绑"的伪规律。

3.3 手算一遍,建立三个指标的联动关系

为了让你更直观地感受支持度、置信度、提升度三者之间的关系,我们把手上的小样本数据算成一张规则表。下面只保留置信度≥0.5的几条常见规则:

规则支持度置信度提升度
牛奶→面包0.600.750.94
牛奶→尿布0.600.750.94
尿布→啤酒0.600.751.25
面包→牛奶0.600.750.94
啤酒→尿布0.601.001.25
尿布→牛奶0.600.750.94
牛奶→面包,尿布0.400.501.04

你注意看,规则"牛奶→面包"和"尿布→啤酒"置信度完全相同,都是0.75,但提升度一个小于1一个大于1。如果只看置信度,这两条规则会被当成同一档次的发现,可一旦代入提升度,价值立刻分出了高下。这就是后文筛选规则时,需要用多指标而不是单一指标的原因。

4. 真正值得用的规则:引入leverage、conviction与组合筛选策略

4.1 提升度什么时候会失灵

提升度虽然比置信度靠谱,但它在两类场景下依然会骗人。第一类是低频项上的高lift。如果一个项集的支持度特别低,例如0.005,但它的lift可能高达3甚至5,因为分母support(B)很小,随便一点共现都会被放大。这类规则在成千上万条规则里最容易吸引眼球,但落地时你会发现它覆盖的样本太少,根本撑不起一个促销活动的选品逻辑。第二类是中低频项但带强烈季节性的数据,比如年底的牛奶和灯笼,看起来提升度很高,但放到全年视角下就只是一个季节性巧合。

所以我不建议把某一项指标作为唯一筛选依据。比较成熟的做法是组合指标一起看,这也是当前主流的学术和应用共识。

4.2 三个互补指标的计算逻辑:leverage、conviction、zhangs_metric

在mlxtend生成规则时,association_rules函数会顺带计算出好几个评估指标,其中三个最大用途是:

**Leverage(杠杆率)**的公式是leverage(A→B) = support(A∪B) - support(A) × support(B)。它衡量的是实际共现概率与"假设完全独立时共现概率"的差值。差值为正,表示正相关;为0,表示独立;为负,表示负相关。和lift相比,它不受support(B)太小的影响,是一个在频率尺度上直观的差值指标。

**Conviction(确信度)**的公式是conviction(A→B) = (1 - support(B)) / (1 - confidence(A→B))。它想表达的是"如果没有A,B出现会困难多少倍"。这个指标官方解释有点绕,你可以这样理解:它衡量的是在置信度不够理想的情况下,B被A"带出来"的额外保证程度。conviction越大,说明A对B的带动作用越强,且不像lift那样对低频项敏感。

**Zhang's metric(张氏度量)**是一个综合了support、confidence和lift的规范化指标,取值范围在-1到1之间,越接近1说明正向关联越强,越接近-1说明负向关联越强。它在处理强负相关时比lift和conviction都要稳定。

4.3 一个可以直接抄作业的四象限筛选法

根据我在项目里的经验,建议把规则按"业务扩张潜力"和"统计可信度"拆成两个维度来筛选。统计可信度看支持度、置信度和p值类信息,业务扩张潜力看lift、leverage和商品本身的利润结构。落到操作层面,我会定这样一组默认阈值:

指标筛选区间原因
support≥ 0.02 且 ≤ 0.6太小没有覆盖力,太大说明是高频商品自然共现
confidence≥ 0.5低于0.5说明规则本身没有明显方向性
lift≥ 1.2至少要带来20%以上的概率增益
leverage≥ 0.01确保不是低频项上的虚高
conviction≥ 1.2保证规则有"拉动"意义,不是简单伴生

再把满足这些条件的规则按业务场景归类:一类是"高lift、高leverage、高confidience"的强正向规则,适合做捆绑促销、组合推荐;一类是"高support、lift接近1"的中性规则,适合做备选项,但不要花太多运营资源;还有一类是"lift明显大于1但support非常低"的长尾规则,这种适合做个性化推荐里的补充,不适合做全量活动。

4.4 警惕"规则数量爆炸",加一个规则长度约束

还有一个很实际的问题:频繁项集一旦挖到4项、5项,生成的规则里会有大量"前件太长"的规则。比如{A,B,C}→{D},这种规则在数据里可能只有几笔支持,置信度再高也不具备业务解释性。我通常在筛选时直接加一条过滤条件:规则两侧的项目数分别不超过2个,总长度不超过3个。几乎每次这么做以后,候选规则表都会大幅收缩,剩下的规则才真正敢拿去给人看。

5. 完整实操:mlxtend从原始订单到规则落地的全套流程

5.1 环境准备与数据结构化:把订单表转成one-hot事务矩阵

先说环境。除了基础的pandas、numpy之外,需要安装mlxtend。如果你的Python环境里还没有这个包,命令行执行一下即可:

pip install mlxtend

关联分析输入的数据结构比较讲究。mlxtend原生的apriori接口需要的是one-hot编码的事务矩阵:每一行是一条事务,每一列是一个项目,单元格用True或False标记该事务是否包含该项目。假设你的原始数据是两张表,订单表和订单明细表,最省事的转换逻辑是先把明细按订单聚合为list,再用TransactionEncoder转成矩阵。

import pandas as pd from mlxtend.preprocessing import TransactionEncoder # 模拟原始订单明细 orders = [ ['牛奶', '面包', '尿布'], ['牛奶', '面包'], ['牛奶', '尿布', '啤酒'], ['面包', '尿布', '啤酒'], ['牛奶', '面包', '尿布', '啤酒'], ] te = TransactionEncoder() te_ary = te.fit_transform(orders) df = pd.DataFrame(te_ary, columns=te.columns_) print(df)

输出结果长这样:

牛奶面包尿布啤酒
TrueTrueTrueFalse
TrueTrueFalseFalse
TrueFalseTrueTrue
FalseTrueTrueTrue
TrueTrueTrueTrue

这是所有后续步骤的数据基础。我在做真实项目时,最常处理的坑订单表里重复购买的商品没去重,导致同一张订单里同一个商品出现两行。建议在聚合前先对订单明细执行drop_duplicates(),否则one-hot矩阵里同一商品在一条事务里只会保留一次,而重复计数会影响支持度计算。

5.2 频繁项集挖掘:用apriori还是fpgrowth

数据转好后,挖掘频繁项集就一行代码:

from mlxtend.frequent_patterns import apriori, fpgrowth # 用apriori挖掘频繁项集,min_support=0.4 frequent_itemsets = apriori(df, min_support=0.4, use_colnames=True) # 如果数据量大,可以换成fpgrowth,函数签名几乎一样 # frequent_itemsets = fpgrowth(df, min_support=0.4, use_colnames=True) print(frequent_itemsets)

use_colnames=True的意思是项集列直接显示商品名而不是列索引,调试时非常直观。apriori和fpgrowth的返回值都是DataFrame,包含itemsetssupport两列。注意,min_support的设置要结合数据规模。我的习惯是先用0.05试跑,看返回的频繁项集行数,如果超过几千行就上调;如果只有十几行,就下调到0.02。频繁项集行数控制在100到1000行之间比较适合继续做规则分析,太少了说明没有挖掘空间,太多了后续规则筛选会非常耗内存。

5.3 生成关联规则,并写一个自动筛选函数

生成规则用association_rules,它在mlxtend.frequent_patterns里:

from mlxtend.frequent_patterns import association_rules rules = association_rules(frequent_itemsets, metric='lift', min_threshold=1.0) print(rules.columns)

这里metricmin_threshold是生成阶段的一个预筛。metric='lift', min_threshold=1.0表示只保留lift≥1.0的规则,基础框架生成后再用前面的多指标组合精确过滤。

rules这个DataFrame里每一行是一条规则,列包含antecedentsconsequentsantecedent supportconsequent supportsupportconfidenceliftleverageconvictionzhangs_metric。它已经帮我们把评估指标全部算好了,下面的工作就是看怎么选。

我通常会把筛选逻辑封装成一个函数,方便不同数据集复用:

def select_actionable_rules(rules, min_support=0.02, min_confidence=0.5, min_lift=1.2, min_leverage=0.01, min_conviction=1.2, max_items=3): filtered = rules[ (rules['support'] >= min_support) & (rules['confidence'] >= min_confidence) & (rules['lift'] >= min_lift) & (rules['leverage'] >= min_leverage) & (rules['conviction'] >= min_conviction) & (rules['antecedents'].apply(len) + rules['consequents'].apply(len) <= max_items) ] return filtered.sort_values('lift', ascending=False) actionable_rules = select_actionable_rules(rules)

这个函数实现了上面说的多指标联合筛选,加上规则总长度不超过3个项目的约束。几个阈值可以根据项目数据量调整:样本总量只有几百的事务,support阈值可以放宽一点,但lift和conviction这种比值类指标不要轻易放宽,因为它们是你判断"规则是否有增量价值"的核心防线。

筛选之后,最好把规则导出成Excel或CSV做进一步人工排序。我一般会额外计算一列提升度×置信度作为综合评分列,再按业务毛利排序,把高分且高毛利的规则优先交给运营同事验证。

actionable_rules = actionable_rules.copy() actionable_rules['score'] = actionable_rules['lift'] * actionable_rules['confidence'] actionable_rules.to_csv('candidate_rules.csv', index=False)

6. 那些文档里不会写的东西:我踩过的坑和几条原则

6.1 时间顺序是个隐形的坑:关联规则不等同于"先买A再买B"

很多人在做用户行为时序数据时,会把用户在一个月内的全部点击、收藏、购买记录做成一条"事务",然后跑关联分析,得出"访问了A页面的人会访问B页面"。但仔细想想,关联规则公式里压根没有时间顺序,support和confidence都只统计"是否在同一事务里出现过"。如果A和B的先后顺序对你的业务结论有影响,就必须在数据准备阶段把同一用户的行为按时间窗口切分,比如"2小时内"算一个会话,而不是按自然月聚合。否则你得到的规则很可能把"先看A后看B"和"先看B后看A"混为一谈,业务上根本没法落地。

6.2 高lift的低频规则:看起来很美,落地就赔钱

这是我在促销选品项目里踩过最深的坑。那次的规则表里出现了一条lift高达4.2的规则:某款低度酒→一款进口零食。当时运营同事很兴奋,准备做一个组合上新活动。我一查支持度才发现,这条规则只覆盖了全量订单的0.3%,也就是一周才几单。真要围绕它做捆绑推荐,曝光量小不说,还可能因为样本太少产生误判。后来我养成了一个习惯:在给运营的可执行规则清单里,永远分两栏,一栏是"高覆盖主推组合",一栏是"高增益长尾组合",两类规则用不同的运营策略。高覆盖组合做全站推荐,高增益组合只在相关商品详情页做个性化推荐。

6.3 相关不等于因果,这是关联分析永远解释不了的事

关于"哪些规则真正值得用",我自己的体会是:关联分析能给你的是值得做实验的候选清单,而不是可以直接执行的商业结论。比如冰淇淋销量和溺水人数在所有统计上都是强相关,但没有人会认为卖冰淇淋能提高安全性。你筛出来一条"买纸尿裤的人更倾向买啤酒"的规则,它背后的原因可能是"年轻爸爸群体下班后顺便买酒",这个洞察可以用来指导广告定向,但它本身不是因果关系。所以在交付结果时,我通常会在报告最后加一段话:这些规则需要A/B测试验证,或者至少抽样电话回访验证,再决定是否全量上线。

6.4 最后的实操建议:规则是用来迭代的,不是一次定死的

关联分析不是一锤子买卖。数据会更新,用户偏好会漂移,今天算出来的一条高lift规则,三个月后可能因为季节变化、供应链调整、竞品上新而完全失效。我建议把频繁项集和关联规则的脚本做成定期任务,至少每个月重跑一次,并且把历史版本的规则存档做对比。当某条曾经很稳定的规则出现lift明显下降时,往往预示用户消费行为在发生变化,这信息本身比规则还有价值。

回到开头那个朋友的项目,最后我帮他把筛选逻辑落地成了一套固定流程:min_support=0.02、confidence=0.5、lift=1.2为底线,再结合leverage和conviction做二次过滤,规则数量从最初的几百条收敛到二十几条。交给运营的候选清单里,每一行都标注了支持的样本量、可能的业务含义和对应的人群建议。这次他总算没有再看着几百条规则发愁了。关联分析这个工具就像一把筛子,筛出来的东西能不能吃,不能光看筛子转得快不快,还得看筛网孔径合不合适,更要看你到底想筛出什么。把"哪些规则真正值得用"想清楚,你这一整套Python关联分析的功夫才算真正落地。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询