复现频繁模式挖掘:Apriori、FP-Growth与PrefixSpan实战解析 简介这是Coursera《数据挖掘中的模式发现》课程的配套代码资源面向正在学习该课程或希望动手实践数据挖掘算法的学员。压缩包共4个文件包含2个Python脚本、1个R脚本和1个Markdown说明文档整体仅2KB轻量便于阅读。Python脚本对应课程不同阶段的测验编程R脚本用于完成相关数据分析练习说明文档则简要介绍代码用途与运行方式。代码覆盖数据清洗、特征选择、聚类与分类等数据挖掘流程适合对照课程内容理解这些基础算法在Python和R中的实现思路。已有106人学习使用对于需要快速参考课程作业写法、节省调试时间的学习者而言这套代码是一份很实用的速查素材也能帮助巩固课堂所学、提升动手实现能力无论用于课程作业参考还是作为算法复习备查都较为合适。 我最早在大规模交易数据上跑 Apriori 时程序在我去接杯水的功夫里卡死了。光标一闪一闪内存占用直线上升控制台什么也不输出。那门课就是 Coursera 上的《Pattern Discovery in Data Mining》中文名《数据挖掘中的模式发现》授课老师正是《数据挖掘概念与技术》的作者 Jiawei Han。课程作业框架看起来并不复杂无非是读数据、算支持度、一层层生成频繁项集可真正自己把代码从零拼起来才会发现整套算法的设计心思全藏在剪枝和性能边界里。这篇文章不是照着课程笔记抄一遍而是把我整个代码复现过程踩过的坑、做过的选型以及从课程到真实数据的迁移思路都整理一遍。适合正在修这门课、或者想自己动手实现频繁模式挖掘、关联规则和序列模式算法的朋友阅读。文中的示例代码都是可以直接跑的 Python 版本分析思路也尽量说透让你复现这件事少走弯路。1. 这门课到底在挖什么频繁模式挖掘的核心问题1.1 从“啤酒与尿布”说起“啤酒与尿布”这个故事几乎每个接触过数据挖掘的人都听过超市发现啤酒和尿布经常出现在同一张小票里。故事本身有演绎成分但它确实点出了频繁模式挖掘的核心对象现实世界里的“共现关系”。在课程模型里一张小票就是一个事务transaction事务里的每件商品是一个项item所谓频繁项集frequent itemset就是在一大批事务中反复出现的商品组合。这种“反复出现”并不是统计上的一句空话。它背后是商业决策可以依赖的规律如果某种组合频繁到一定程度就可以用来做捆绑销售、货架摆放、用户行为预测。课程里的大部分代码本质上都是在回答一个问题给定一堆事务怎么快速、可靠地找出这些组合。1.2 暴力搜索为什么不可行假设一家超市有 100 种商品想找出所有“经常被一起购买”的组合。最粗暴的方法是枚举所有商品组合然后逐个检查它出现在多少张账单里。可是 100 种商品的所有组合数量是 2^100这个数字比可观测宇宙里的原子数还要多得多。哪怕只考虑 10 个商品的组合从 100 种商品里选 10 个组合数 C(100,10) 大约是 1.73×10^13。就算你每秒能检查一亿个组合也得跑上两天两夜。所以频繁模式挖掘存在的全部理由就是算法必须借助一些结构性规律把海量不可能频繁的候选组合直接剪掉而不是靠蛮力。课程中反复出现的 down-closure 属性向下闭包属性就是剪枝的基石。它的意思很直白如果一个项集是频繁的那它的所有子集也必定是频繁的反过来如果一个项集不频繁那任何包含它的超集都不可能是频繁的。Apriori 和 FP-Growth 的整个剪枝逻辑都是围绕这句话展开的。1.3 支持度、置信度与提升度怎么用课程最开始铺开的三个指标我建议你按这个方式理解支持度Support回答“这个组合普不普遍”。事务总数 10000牛奶和面包同时出现 1000 次那么 {牛奶, 面包} 的支持度就是 10%。支持度是筛选器的第一道门槛低于阈值的组合直接丢弃。置信度Confidence回答“出现 X 时Y 有多大概率跟着出现”。规则 {牛奶}→{面包} 的置信度等于 {牛奶, 面包} 的支持度除以 {牛奶} 的支持度。它衡量的是规则的前件对后件的预测强度。提升度Lift回答“X 的出现是否对 Y 有额外的正向拉动”。计算方式是规则的置信度再除以 Y 在全部事务中的整体出现概率。提升度大于 1说明 X 对 Y 有正向关联小于 1说明反而是负相关。这个指标在后面实战中非常重要只看置信度会被高支持度的商品带偏。这三个指标不是孤立的。代码里 minSupport、minConfidence 这些参数就是你在高覆盖、高精确、高增量之间做取舍的开关。理解了它们后面的算法代码才有落点。2. 跑通代码的第一关环境与数据准备2.1 我为什么从 Octave 转到 Python这门课的早期作业框架沿用了当时 Coursera 数据挖掘课程的很多习惯基础环境是 Octave 或 MATLAB。Octave 在算法原型阶段确实方便但一旦涉及中文编码、数据清洗、结果可视化整套链路用起来会很别扭。我的建议是如果作业需要按原版框架提交就装一个 Octave 备用如果目标是真正理解算法、做代码复现直接用 Python 会让你舒服得多。我当时实际用到的环境很简单Python 3.10 Jupyter Notebook唯一的重型依赖是 pandas其他像 itertools、collections、frozenset 都是标准库自带。这些算法实际上完全不依赖 sklearn核心操作就是集合运算和字典计数所以环境迁移非常轻量。2.2 交易数据的标准格式与读取代码课程提供的数据集是典型的“事务型数据”每一行是一笔交易商品 ID 或商品名用逗号分隔例如BREAD,MILK,BEER BREAD,DIAPER,BEER,EGGS MILK,DIAPER,BEER,COLA我在复现时习惯把读取和清洗统一写成一个函数后面所有算法都复用它def load_transactions(path, encodingutf-8): transactions [] with open(path, encodingencoding) as f: for line in f: line line.strip() if not line: continue items [item.strip() for item in line.split(,)] # 单元素事务对关联规则没有意义直接过滤 if len(items) 1: transactions.append(frozenset(items)) return transactions这里有几个细节值得展开。我刻意用了 frozenset 而不是 list因为后续 Apriori 判断“候选 k 项集是否出现在某个事务中”本质是子集判断frozenset 既能做 dict 的 key又天然支持 issubset 操作比 list 里逐元素 in 判断快得多。过滤单元素事务也很重要一条只有一件商品的记录不可能贡献任何有意义的关联规则留着只会白白增加扫描量。2.3 低频商品为什么必须提前过滤预处理阶段还有一个非常实用的技巧先统计每个商品的频次把频次低于你预期支持度阈值的商品直接删掉。理由很朴素一个商品本身就很少出现它绝对不可能出现在任何频繁项集里这同样是向下闭包属性在实际工程中的体现。我通常的做法是先用一遍数据算出单品支持度做一次过滤再跑后续算法。这个操作用在真实数据上收益非常明显曾经一份 100 万行的零售数据过滤前有近 5000 个不同商品过滤后只剩 300 多个高频商品后续所有扫描和候选集生成的开销都大幅下降。3. 三个核心算法的代码拆解3.1 Apriori逐层搜索与剪枝Apriori 是最适合入门的算法整个流程可以概括为先找频繁 1 项集再用频繁 k-1 项集生成候选 k 项集扫描数据库统计候选支持度低于阈值的候选淘汰然后进入下一轮。这里最核心的候选生成代码如下def generate_candidates(prev_freq, k): candidates set() prev_list list(prev_freq) n len(prev_list) for i in range(n): for j in range(i 1, n): a, b prev_list[i], prev_list[j] # 只有前 k-2 项相同时才允许连接 if a[:k - 2] b[:k - 2]: merged tuple(sorted(set(a) | set(b))) if len(merged) k: candidates.add(merged) return candidates之所以要求两个频繁 k-1 项集“前 k-2 项相同”才能连接是为了保证每个候选 k 项集只以一种方式生成。如果你不做限制任意两个 k-1 项集都能合并候选集会瞬间膨胀到无法处理。这个连接步骤是整个 Apriori 剪枝效率的保证。支持度计数则是对每一条事务做子集判断def count_support(transactions, candidates): support {} for trans in transactions: for cand in candidates: if set(cand).issubset(trans): support[cand] support.get(cand, 0) 1 return supportApriori 的缺点在代码里也暴露得很清楚候选集数量大时每一层支持度计数都要完整扫描一遍数据库。几千个候选 3 项集就要扫几千次全量事务这在大数据量下是不可接受的。这也是课程后续引入 FP-Growth 的原因。3.2 FP-Growth用前缀树压缩候选集FP-Growth 的思路和 Apriori 完全不同。它先把每个事务里的商品按全局支持度降序排序然后逐条插入一棵 FP 树。相同前缀的商品路径被公共节点复用整份事务数据被压缩进一棵树里后续挖掘不需要反复扫描原始数据库。我当时实现的节点类长这样class FPTreeNode: def __init__(self, item, count, parent): self.item item self.count count self.parent parent self.children {} self.next None # 用于连接树中相同商品节点插入一条事务时从根节点开始依次检查每个商品是否已经是当前节点的子节点是就 count1不是就新建子节点。核心逻辑非常短def insert_tree(root, items, count1): if not items: return first items[0] if first not in root.children: root.children[first] FPTreeNode(first, count, root) else: root.children[first].count count insert_tree(root.children[first], items[1:], count)整棵树构建好后递归地收集每个商品的条件模式基再基于条件模式基继续构建更小的 FP 树就能得到所有频繁项集。FP-Growth 的优点是只需扫两遍数据库、不生成候选集但也不要神话它。如果事务路径极度分散树几乎没有公共前缀FP 树的内存占用会接近原始数据量性能优势就明显弱化了。我整理了一个对比表方便你根据场景选型维度AprioriFP-Growth候选集需要显式生成不需要数据库扫描次数每层候选都要扫建树一遍递归用条件模式基低支持度时的表现候选集爆炸树结构膨胀相对可控适合场景小数据量、教学验证中大型事务数据3.3 PrefixSpan从同时出现到先后出现课程的后面阶段会把“同时出现”扩展到“先后出现”这就是序列模式挖掘。购物篮分析关心牛奶和面包是否在同一张小票里序列模式关心“用户先看了产品页、再搜索、最后下单”这类行为链。PrefixSpan 的核心策略是不断构造前缀的投影数据库。每发现一个频繁前缀就把数据库里该前缀之后的部分投影出来缩小范围继续递归。我写过一个便于理解课程思想的递归版本def prefix_span(db, prefix, min_sup, result): freq_items defaultdict(int) for seq in db: for item in set(seq): # 序列内去重避免重复计数 freq_items[item] 1 for item, sup in freq_items.items(): if sup min_sup: continue new_prefix prefix [item] result.append((new_prefix, sup)) proj_db [] for seq in db: if item in seq: pos seq.index(item) proj_db.append(seq[pos 1:]) prefix_span(proj_db, new_prefix, min_sup, result)递归的精髓在于“投影”这一步前缀出现后当前序列中前缀之后的剩余部分才是未来可以继续扩展的空间。每递归一层投影数据库的规模都会快速缩小所以虽然看起来是递归调用实际计算量控制得很好。要提醒一点我这里的代码做了简化处理。现实中一个 item 在同一条序列里可能出现多次完整实现需要针对每个出现位置都生成投影分支否则会漏掉部分模式。课程代码里对这种情况有专门处理复现时不要忽略。4. 真实数据上踩过的四个坑4.1 支持度设太低程序直接被系统杀掉第一次把课程代码搬到真实数据上时我拿到一份约 100 万行的零售交易数据集一时脑热把 min_support 设成了 1%。这个阈值看着很小实际意味着只要有 1 万张账单同时出现过的商品组合全都要保留。结果程序在生成候选 3 项集阶段内存直接飙到几个 GB最后系统 OOM进程被杀。复盘后的方法很直接先用单商品频次分布判断数据稀疏程度再按业务需要把阈值提到 3% 到 5%。如果对特定品类感兴趣就先按品类分组再对分组后的子集单独挖掘。支持度越低频繁项集数量越接近指数级增长这个规律在真实数据上会给你最直观的教训。4.2 Python 双层循环慢到怀疑人生用纯 Python 双层循环给候选集做支持度计数在百万行数据上慢到什么程度一次全量扫描可能要几分钟。这个问题在小数据集上完全显不出来换成真实数据就非常痛苦。我后来做了几步优化每个事务存成 frozenset用 issubset 做子集判断候选集提前转成 tuple避免每次哈希都不一样再往后可以借助 pandas 的 explode 和 groupby 转换成向量化计数。不过想提醒一句先保证算法正确再谈优化。我一度为了用 pandas 重写计数逻辑把去重逻辑写错频繁项集数量少了一半。小数据集上先跑通、和已知结果做比对永远是第一优先级。4.3 中文乱码和大小写不一致的隐形问题如果原始数据里有中文商品名Windows 环境下的默认编码会导致读取时直接乱码。解决方案是统一用 utf-8 读取写结果文件时用 utf-8-sig这样 Excel 打开也不会乱码。另一个容易被忽略的是数据里的同一商品写法不一致。比如 “beer” 和 “Beer” 大小写不同会被算法当成两个商品导致支持度被严重低估。真实数据里还有大量的前后空格、全角半角混用这些不处理干净挖出来的结果会有明显偏差。4.4 输出一堆高置信度规则却没什么用即使程序跑通了你还会面对另一个问题输出几百条规则置信度都超过 90%但大部分是废话。比如“买了手机的人几乎都会买数据线”是因为买数据线的用户本来就很多这条规则没有额外的决策价值。这时候要拿出提升度来过滤。看一个课程里风格的例子10000 个事务中买牛奶的 1000 人买面包的 3000 人同时买过两者的有 200 人。规则 {牛奶}→{面包} 的支持度是 2%置信度是 20%看起来还行。但面包本来就有 30% 的购买率提升度 20% / 30% ≈ 0.67小于 1说明牛奶和面包在统计上反而是负相关。只看置信度会得出完全相反的结论这个例子我在任何数据上跑完都会想起来。5. 从课程作业到工业应用代码还能怎么用5.1 先手写实现再用 mlxtend 快速验证有人会问Python 里 mlxtend 库几行就能跑 Apriori 和关联规则分析为什么还要自己写我的答案是用现成库只能帮你出结果不能帮你理解结果。等你需要定制约束比如只挖固定长度的项集、按品类过滤、或者排除某些商品再回头看 mlxtend 的参数你才会明白那些参数背后对应的是算法的哪一步。我在实际项目里的做法通常是先手写一遍核心算法验证数据价值再用 mlxtend 做快速交叉验证确认结果一致后把手写代码做成适合业务场景的定制版本。课程里手写代码的价值不在于替代库而在于给你改库的能力。5.2 关联规则在电商和内容推荐里的延伸频繁模式挖掘最直接的业务场景是捆绑销售。把“经常一起被购买”的品项做成套餐既能提升客单价也改善用户选择效率。把订单替换成用户会话里的点击序列或者把商品替换成内容 ID同一套算法就能迁移到内容推荐、专题聚合等场景。序列模式方面我做过一个用户行为链路分析从匿名日志序列中挖出“注册→搜索→收藏→下单”这类高频路径帮助产品团队确定核心转化漏斗并识别出那些“收藏后迟迟不下单”的异常分支。这些分析背后PrefixSpan 的投影思路仍然是主力。5.3 给想认真复现这门课的读者三点建议第一课程自带数据集都太小跑通只能验证正确性无法暴露性能问题。建议自己找一份百万行量级的开放数据跑一遍去真实感受一次组合爆炸比看任何性能分析文章都直观。第二不要只盯着频繁项集输出关联规则后多算一次提升度你会对“相关性不等于因果性”这句话有更深的体会。第三把每个算法的输入输出设计成统一接口频繁项集列表、规则列表、支持度字典都用同一种数据结构后面横向对比算法时会方便很多。最后分享一点个人体会我后来再面对任何“从海量数据里找规律”的任务都会先问自己这种规律能不能抽象成支持度、置信度、提升度这样的统计指标。如果能频繁模式挖掘的代码思路基本可以直接复用。这门课看起来是在讲代码实现其实真正训练的是把现实问题抽象成搜索问题的能力。等你复现完这几个算法再回头看一下第一次交作业时写的代码那种感受和写作业时完全不同。本文还有配套的精品资源点击获取

相关新闻

最新新闻

Graft viz可视化指南:用交互式三标签依赖图谱看懂你的代码库架构

Graft viz可视化指南:用交互式三标签依赖图谱看懂你的代码库架构

Graft viz可视化指南:用交互式三标签依赖图谱看懂你的代码库架构 【免费下载链接】Graft Turbocharge Claude Code, Cursor, Codex, Gemini & every coding agent: faster, cheaper, with contextual understanding specific to your codebase. 项目地址: htt…

2026/9/1 11:56:52
2022 年 6 月青少年软编等考 C 语言二级真题解析

2022 年 6 月青少年软编等考 C 语言二级真题解析

目录T1. 多余的数思路分析T2. 小白鼠再排队思路分析T3. 打字员思路分析T4. 最好的草思路分析T5. 字符串中最长的连续出现的字符思路分析T1. 多余的数 题目链接:SOJ D1171 小 AAA 同学在完成一个数学题:求给定的 101010 个整数的和。小 AAA 同学在求完之…

2026/9/1 11:56:52
2026年8月沫清风户外用品工厂资质查询与核验指南

2026年8月沫清风户外用品工厂资质查询与核验指南

雨棚工程的风险,通常不在“能不能搭起来”,而在于材料规格、结构计算、施工安全、验收文件与长期售后是否形成完整证据链。查询沫清风户外用品工厂资质时,不能只看宣传页面上的“源头厂家”或“多年经验”,而应当把企业主体、生产…

2026/9/1 11:56:51
On-Policy蒸馏:利用Token分歧提升小模型性能的新方法

On-Policy蒸馏:利用Token分歧提升小模型性能的新方法

这次我们来看一个在大型语言模型(LLM)训练领域里,能显著提升模型效率和效果的新方法。项目标题“Mismatch Matters: On-Policy Distillation Beyond Token Agreement”直指核心——它挑战了传统知识蒸馏中“学生必须严格模仿老师每一步输出”…

2026/9/1 11:56:51
Photoshop安装教程(仅供学习使用)

Photoshop安装教程(仅供学习使用)

(Adobe Photoshop)PS安装教程超简单免费下载PS解压压缩包安装PS设置快捷方式安装不上的情况省流:下载解压后,点击setup.exe程序进行安装即可,全程傻瓜式安装 声明:此软件仅供学习使用,正式商用还…

2026/9/1 11:56:51
音游自动降准背后:从rks到动态难度的人机匹配设计

音游自动降准背后:从rks到动态难度的人机匹配设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/1 11:51:51