Python实现频繁模式挖掘:Apriori与FP-Growth实战及可视化
2026/9/8 6:50:51 网站建设 项目流程

简介:一套Python频繁模式挖掘实践代码包,面向数据挖掘初学者、数据分析师及算法课程项目开发者,解决从交易数据中发现频繁项集与关联规则的落地实现问题。代码包共2个文件,均为Python脚本,体积仅4KB,涵盖基础演示版与完整流程版两种实现,包含数据预处理、频繁项集搜索、支持度与置信度计算、关联规则生成等功能模块,结构紧凑且便于二次修改。目前已有262人学习下载,可当作算法入门与课程设计的参考案例。运行代码可直观看到频繁项集筛选过程及Apriori算法的迭代逻辑,同时输出关联规则的可视化图表,帮助使用者理解不同支持度阈值对结果的影响。代码注释简洁,算法流程清晰,支持直接替换数据集运行并保存规则结果,适用于市场篮子分析、日志行为挖掘等常见场景,是一份兼顾原理与实现、可直接用于课程实验的轻量级参考资料,适合在Jupyter Notebook或PyCharm等环境中直接运行。

1. 先搞清楚:频繁模式挖掘到底在挖什么

做数据分析或者偶尔碰电商数据的同学,一定绕不过“频繁模式挖掘”这个名字。它还有个更接地气的说法叫“购物篮分析”——你去超市买了一袋面包、一盒牛奶、一串香蕉,收银小票上这些商品共同出现,就是一种模式;如果成千上万张小票里“面包+牛奶”反复同时出现,那这就是一个有商业价值的频繁模式。应用上最常见的例子就是啤酒和尿布的故事:把啤酒放在尿布旁边,销量悄悄涨了一截。背后的原理,就是我们今天要跑的这段代码。

频繁模式挖掘的核心目标就两个:一是找出“哪些东西经常一起出现”,二是从这些组合中提炼出“如果买了A,有多大可能买B”之类的关联规则。前者叫频繁项集挖掘,后者叫关联规则挖掘。本文给出的完整代码覆盖了从数据准备、算法实现到结果可视化的全部流程,运行之后会生成频繁项集表、关联规则表和两张结果图片,适合正在学数据挖掘、准备做毕业论文实验,或者工作中需要做商品捆绑推荐的读者直接“抄作业”。

算法选择上,我用了两个经典方案:Apriori 和 FP-Growth。Apriori 逻辑简单,适合理解原理;FP-Growth 不生成候选集,数据量大时性能好很多。完整代码里两个都写了,跑一遍对比,比单纯看理论舒服得多。对了,依赖库我选了mlxtend,它把频繁项集挖掘和关联规则计算封装得很好,不需要自己从零实现 Apriori 的逐层迭代。

2. 完整可运行的代码:从数据集到频繁项集

2.1 环境准备和三行安装命令

写代码之前先把环境准备好。我这里用的 Python 3.10,理论上 3.8 以上都不会有问题。核心依赖就三个:pandasmlxtendmatplotlib,画网络图还需要networkx。安装命令直接复制:

pip install pandas mlxtend matplotlib networkx

如果你用的是 Anaconda,也可以用conda install -c conda-forge mlxtendpandas负责处理数据,mlxtend负责 Apriori、FP-Growth 和关联规则计算,matplotlibnetworkx负责画图。这几个库都是数据挖掘的常客,装好之后基本不用再补别的。

2.2 数据准备:事务数据长什么样

频繁模式挖掘的输入数据有两种常见形态。第一种是“事务列表”,也就是一行一次购买记录,每行是顾客买的商品集合;第二种是“one-hot 编码表”,每一行是一个事务,每一列是一个商品,买了就填 1,没买就填 0。mlxtend里的apriorifpgrowth函数默认要求输入第二种形态,所以我们需要做个转换。

我这里就直接用一个小型超市模拟数据,写死在脚本里,方便你复制运行。

import pandas as pd from mlxtend.preprocessing import TransactionEncoder from mlxtend.frequent_patterns import apriori, fpgrowth, association_rules # 构造事务数据:每一行代表一次购物记录 dataset = [ ["牛奶", "面包", "黄油"], ["牛奶", "面包", "鸡蛋"], ["牛奶", "啤酒", "尿布"], ["面包", "黄油", "饼干"], ["牛奶", "面包", "黄油", "啤酒"], ["面包", "啤酒", "尿布"], ["牛奶", "尿布", "啤酒", "鸡蛋"], ["面包", "牛奶", "黄油", "鸡蛋"], ["啤酒", "尿布"], ["牛奶", "面包", "啤酒", "尿布", "鸡蛋"], ] # 将事务数据转换为 one-hot 编码的 DataFrame te = TransactionEncoder() te_ary = te.fit_transform(dataset) df = pd.DataFrame(te_ary, columns=te.columns_) print("转换后的事务表(前5行):") print(df.head())

这段代码的核心在TransactionEncoder。很多初学者第一次跑apriori时报错,报错信息大概是“Expecting pandas DataFrame with 0/1 values”,原因就是直接传了一个普通二维列表进去。TransactionEncoder会自动扫描所有事务中出现的商品,生成列名,然后填充 0/1,省去手动构造稀疏矩阵的麻烦。

2.3 Apriori 挖掘核心代码

数据准备好之后,挖掘频繁项集只需要一行函数调用。min_support是最小支持度,意思是某个商品组合至少出现在多少比例的事务中;use_colnames=True表示返回商品名而不是列索引。

# 使用 Apriori 挖掘频繁项集 frequent_itemsets_ap = apriori( df, min_support=0.3, # 最小支持度 30% use_colnames=True, max_len=None # 不限制项集内商品数量 ) # 按支持度降序排列,方便观察 frequent_itemsets_ap = frequent_itemsets_ap.sort_values("support", ascending=False) print("\nApriori 频繁项集(支持度 >= 0.3):") print(frequent_itemsets_ap) # 基于频繁项集生成关联规则 rules_ap = association_rules( frequent_itemsets_ap, metric="confidence", # 按置信度评估规则强度 min_threshold=0.5 # 最小置信度 50% ) # 按提升度降序排列 rules_ap = rules_ap.sort_values("lift", ascending=False) print("\nApriori 关联规则(置信度 >= 0.5):") print(rules_ap[["antecedents", "consequents", "support", "confidence", "lift"]])

跑完之后会得到一张频繁项集表,里面会出现类似(牛奶, 面包)这样的组合。以我这份数据为例,牛奶面包同时出现在 7 个事务里,支持度 0.7,说明二者关联极强。关联规则表里(牛奶) -> (面包)这条规则的置信度是 0.875,提升度约 1.25,说明“买牛奶的人买面包的概率”是“随机购买面包概率”的 1.25 倍。

2.4 FP-Growth 对比实现

FP-Growth 的代码跟 Apriori 几乎一模一样,只是函数名换成了fpgrowth。这也是mlxtend设计得比较好的地方,算法可以无缝替换。

# 使用 FP-Growth 挖掘频繁项集 frequent_itemsets_fp = fpgrowth( df, min_support=0.3, use_colnames=True ) frequent_itemsets_fp = frequent_itemsets_fp.sort_values("support", ascending=False) print("\nFP-Growth 频繁项集(支持度 >= 0.3):") print(frequent_itemsets_fp) # 基于 FP-Growth 结果生成关联规则 rules_fp = association_rules( frequent_itemsets_fp, metric="confidence", min_threshold=0.5 ) rules_fp = rules_fp.sort_values("lift", ascending=False) print("\nFP-Growth 关联规则(置信度 >= 0.5):") print(rules_fp[["antecedents", "consequents", "support", "confidence", "lift"]])

我在实际测试中发现,小数据集上 Apriori 和 FP-Growth 的结果完全一致,毕竟它们挖的是同一个频繁项集。区别体现在数据量上:当你有几十万条事务、上千种商品时,Apriori 需要反复扫描数据和生成候选集,速度会肉眼可见地变慢;FP-Growth 把事务压缩到一棵 FP-Tree 里,只需要扫描两遍原始数据,性能优势非常明显。所以如果你跑大规模数据,优先用fpgrowth

3. 结果图片怎么画:支持度-置信度散点图与规则网络图

3.1 散点图:一眼看出规则取舍

生成结果之后,如果只是打印一堆表格数字,说服力不够,图表才是给人看的。我一般会画两张图,第一张是“支持度-置信度-提升度”三维散点图,横轴是支持度,纵轴是置信度,点的颜色深浅或大小表示提升度。

import matplotlib.pyplot as plt # 配置中文字体,否则会出现方块乱码 plt.rcParams["font.sans-serif"] = ["SimHei"] plt.rcParams["axes.unicode_minus"] = False plt.figure(figsize=(10, 6)) scatter = plt.scatter( rules_ap["support"], rules_ap["confidence"], c=rules_ap["lift"], cmap="viridis", s=rules_ap["lift"] * 80, alpha=0.6, edgecolors="w", linewidth=0.5 ) plt.colorbar(scatter, label="提升度 (lift)") plt.xlabel("支持度 (support)") plt.ylabel("置信度 (confidence)") plt.title("关联规则分布:支持度-置信度-提升度") plt.grid(True, linestyle="--", alpha=0.4) plt.tight_layout() plt.savefig("关联规则散点图.png", dpi=150) plt.show()

这张图的价值在于帮助筛选规则。看右上角的点,支持度高、置信度也高,说明这些规则既常见又可靠,优先考虑;角落里支持度很低但置信度很高的点,可能只是小样本上的巧合,需要谨慎。我自己的经验是:实际业务里通常牺牲一点置信度,也要守住支持度,否则规则应用场景太小,没有商业意义。

3.2 网络图:谁和谁总是一起出现

第二张图我习惯画规则网络图,把商品之间的关联关系用图的方式呈现出来。节点是商品,连线是规则,线越粗表示提升度越高。

import networkx as nx # 取前10条强规则,避免网络太密看不清 top_rules = rules_ap.head(10).copy() top_rules["antecedents"] = top_rules["antecedents"].apply(lambda x: list(x)[0]) top_rules["consequents"] = top_rules["consequents"].apply(lambda x: list(x)[0]) G = nx.DiGraph() for _, row in top_rules.iterrows(): G.add_edge(row["antecedents"], row["consequents"], weight=row["lift"]) plt.figure(figsize=(10, 6)) pos = nx.spring_layout(G, k=1.2, seed=42) weights = [G[u][v]["weight"] for u, v in G.edges()] nx.draw_networkx_nodes(G, pos, node_color="#5B9BD5", node_size=1800) nx.draw_networkx_labels(G, pos, font_size=12, font_color="white", font_weight="bold") nx.draw_networkx_edges( G, pos, width=[w * 4 for w in weights], edge_color="#FF7F50", alpha=0.7, arrowstyle="->", arrowsize=20 ) plt.title("频繁模式关联规则网络图(Top 10)") plt.axis("off") plt.tight_layout() plt.savefig("关联规则网络图.png", dpi=150) plt.show()

运行之后生成的图里,你能清晰地看到“牛奶”这个节点周围连了“面包”“黄油”“鸡蛋”等多条边,说明它是数据中的核心商品。做捆绑推荐时,这类中心节点就是最佳切入点——把它放在货架中间或者推荐位首位,带动效果最明显。

3.3 图片保存与中文乱码问题

上面代码里我都加了plt.savefig,把图片保存成 PNG 文件,方便直接贴到报告或论文里。有两个容易踩的坑:

第一,中文乱码。matplotlib默认字体不支持中文,如果注释或标题里有中文,画出来就是一堆方块。解决办法是设plt.rcParams["font.sans-serif"] = ["SimHei"],Windows 一般没问题;如果是 Mac,把SimHei换成Arial Unicode MS;Linux 系统需要先安装中文字体,比如fonts-wqy-zenhei

第二,图片清晰度。保存时把dpi调到至少 150,期刊论文建议 300。屏幕上看着还行,贴到 Word 里放大就模糊,多半是dpi没设置。

4. 参数解读与调优:support、confidence、lift 怎么配合

4.1 三个指标各自管什么

频繁模式挖掘的结果里,最常看的三个指标是支持度、置信度和提升度,很多人一开始分不清,我在这里用一个例子说透。

以规则牛奶 -> 面包为例。支持度是“同时买了牛奶和面包”的事务占总事务的比例,它衡量规则的普遍性;置信度是“买了牛奶的人里面,又买了面包”的比例,它衡量规则的可靠性;提升度是“买牛奶时买面包的概率”除以“没买牛奶时买面包的概率”,它衡量规则是否有实际价值。

三个指标的取舍逻辑是这样的:提升度等于 1 说明牛奶和面包相互独立,这条规则没有意义;大于 1 才说明有正相关。实际项目里,支持度设太低了会挖出一堆冷门组合,设太高了会漏掉有价值的细节规则。我一般会把支持度从 0.5 开始往下试,每降 0.05 看一眼结果数量,找到一个结果条数在几十条左右的阈值,这样既不会爆炸也不会有太多遗漏。

4.2 实际调参经验

针对我这份模拟数据,min_support=0.3min_threshold=0.5是比较合适的组合。如果你换成自己的数据,可以参考下面几条经验:

  • 数据量小(几千条事务以内),支持度从 0.2 到 0.5 之间试探;数据量大(百万级),支持度往往要降到 0.01 以下,不然什么都挖不出来。
  • 关联规则的metric参数可以根据业务选。做推荐系统我更看重lift,太低提升度的规则宁可不要;做风控或异常检测,反而关注置信度,确保规则触发时足够可靠。
  • max_len参数可以控制项集大小。如果只想看两两组合,直接设max_len=2;如果关心组合购买,比如“牛奶+面包+黄油”是不是经常一起买,就保持默认。

5. 常见问题与排查技巧实录

写这一节是因为我亲手踩过不少坑。初学者最容易在半路卡住,我把典型的几个问题和解决方案整理成了一张速查表。

5.1 报错合集速查表

报错信息原因解决方案
ModuleNotFoundError: No module named 'mlxtend'没有安装 mlxtend执行pip install mlxtend
Expecting pandas DataFrame with 0/1 values直接传了列表或没有 0/1 编码TransactionEncoder转换数据
ValueError: The ``support`` column is missing频繁项集结果没有 support 列检查apriorifpgrowth是否成功运行
中文标题乱码matplotlib 默认字体不支持中文设置plt.rcParams["font.sans-serif"]
图片横坐标标签太密集,看不清商品种类太多导致刻度重叠旋转标签plt.xticks(rotation=45, ha="right"),或者只显示部分标签

5.2 画图横坐标太密集怎么办

这个问题在热搜词里也出现了,说明很多人卡在可视化上。当你用df.columns作为 x 轴标签时,如果商品种类超过 20 个,标签就会挤成一团。最简单的处理办法是加一行代码:

plt.xticks(rotation=45, ha="right")

把标签旋转 45 度,基本能缓解。如果还是太挤,则用plt.xticks(ticks=range(0, len(columns), 5))每隔 5 个显示一个标签。这两招对任何“横坐标太密集”的场景都适用。

5.3 结果图片里的核心信息怎么解读

很多人跑完代码,图也出来了,但不知道从图里读什么。基于我这份数据生成的两张图,你至少应该关注三点:图右上角是否有成片的点,有说明规则整体质量不错;网络图里度最高的节点是哪个,它就是核心商品;有没有某条规则的提升度特别高但支持度特别低,这种规则需要人工判断是小概率事件还是真正的隐蔽关联。

我在实际项目里遇到过一种情况:某条规则提升度高达 8,所有人都激动得不行,结果把原始数据翻出来一看,只是某两个月导入了两批关联订单数据,属于系统性偏差,并不是真实的购买习惯。所以挖出规则之后,一定要回顾原始数据验证,不要被统计数字带走。

最后再分享一个小技巧:mlxtend的规则结果里,antecedentsconsequents列是frozenset类型,打印出来像frozenset({'牛奶'}),不太好看。导出 CSV 之前可以做一个转换:rules["antecedents"] = rules["antecedents"].apply(lambda x: list(x)[0]),把集合转成普通字符串,后续处理数据、做 Excel 透视都会方便很多。这套代码跑出来的数据和图片,足够支撑一篇完整的频繁模式挖掘实验报告;想继续深入的朋友,还可以把算法换成 ECLAT,或者直接上pyspark的 FPGrowth 处理海量数据。

本文还有配套的精品资源,点击获取

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

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

立即咨询