频繁模式挖掘大作业实战:Python实现Apriori与FP-Growth
发布时间:2026/10/1 10:38:50来源:尧图网络
简介一份数据仓库与数据挖掘课程设计/期末大作业级别的Python频繁模式挖掘完整方案基于Apriori算法从多角度、多篮子粒度展开挖掘并在Gutenberg、DBLP等多个数据集上配置了运行示例。代码段注明逻辑要点适合初学者在理解算法的同时快速部署复现也可直接作为结课报告的实验基线。资源共41个文件主体为8个Python源码含Apriori核心实现及任务脚本搭配24个txt数据文件、2个Markdown文档说明、1份PDF报告、若干图片与缓存文件压缩包仅5.84MB目录分层一目了然便于查找数据、脚本和实验结果。已有817人学习下载具备一定的参考热度。除源码和数据外资料还包含DBLP与Gutenberg数据集上的关联分析、分组挖掘、主题挖掘等具体任务示例以及文档说明和报告PDF能帮助读者快速熟悉频繁模式挖掘的实验流程、结果整理和报告撰写是一套功能完整、上手门槛低的高分大作业参考资源。1. 频繁模式挖掘大作业一份能跑的代码比算法原理更稀缺期末前两周老师布置了一门数据仓库与数据挖掘的大作业题目是「频繁模式挖掘」。概念课上十分钟就讲完了Apriori 先找频繁项集再生成关联规则FP-Growth 不生成候选集直接压缩事务。但等到真动手写代码你会发现所有问题都卡在工程细节上候选集怎么 join、支持度算出来为什么虚高、FP-Growth 怎么建树、报告里的结果怎么和代码对得上。这门课真正难的不是原理而是让源码、文档说明、报告 PDF 三样东西相互自洽并且随便抽一个函数都能讲清楚。这篇文章就是用来解决这个问题的。我会按「数据准备 → 算法实现 → 文档报告 → 踩坑排查 → 进阶加分」的顺序把一份能直接改用的 Python 频繁模式挖掘大作业拆开讲代码、参数、输出格式都给到位。适合正在赶大作业的学生也适合想快速拿一份参考实现去做业务数据关联分析的从业者——照着我这个思路写差不多能少熬两个通宵。2. 从数据仓库到频繁模式先搞懂你要挖的是什么2.1 数据仓库里的数据形态为什么大作业常用购物篮数据频繁模式挖掘的输入不是一个二维表而是一组「事务」。每个事务包含若干个同时出现的项比如一次购物订单里的所有商品、一门课所有学生的选课组合、一次就诊记录里的所有药品。在数据仓库里这些事务通常不是现成的而是由事实表和维度表 join 之后聚合出来的。大作业里最常见的数据来源是模拟的超市订单数据结构通常是这样订单明细表里有order_id、product_name、quantity、price客户维表和商品维表分别解释订单和商品。要做频繁模式挖掘第一步就是把明细表按订单聚合把同一个订单里的商品名收集成一个列表。这个聚合动作看似简单实则有一半的坑都埋在这里。事实表是多行存储的一个订单包含五个商品就会产生五行记录如果直接把这五行当成事务输入行数是订单数的五倍支持度全都会被冲高。所以必须先做去重和分组我一般会写出这样的 SQL 去数据仓库里把原始数据抽出来-- 从数据仓库抽取事务明细一个订单一行商品用逗号分隔 SELECT fact.order_id, GROUP_CONCAT(DISTINCT dim.product_name ORDER BY dim.product_name SEPARATOR ,) AS items FROM dw_order_fact AS fact JOIN dw_product_dim AS dim ON fact.product_key dim.product_key WHERE fact.order_date BETWEEN 2024-01-01 AND 2024-06-30 GROUP BY fact.order_id HAVING COUNT(DISTINCT dim.product_name) 2 ORDER BY fact.order_id;这段 SQL 的作用是把半年内的订单明细按订单号聚合成一个个「购物篮」。GROUP_CONCAT(DISTINCT ...)保证了同一个订单里重复购买同一商品只算一项HAVING COUNT(DISTINCT ...) 2过滤掉只买一件商品的无意义事务这种事务对关联规则没有任何贡献只会让支持度分母变大。抽取出来之后在 Python 里再按逗号分割成列表就得到了标准的 transactions 格式[[bread, milk], [beer, diaper]]。如果你拿到的数据已经是一张用户-商品矩阵每一行是一个用户每一列是商品单元格是 0/1那反而要小心。这种宽表数据要先用melt或者循环把它还原成事务列表不能直接把 0/1 矩阵喂给频繁模式算法否则算法会把大量不存在的「项」当作单例项集做计数。2.2 Apriori 和 FP-Growth 怎么选一份对照表很多大作业要求同时实现两个算法做对比这也是高频出题点。Apriori 的核心是先验性质如果一个项集是频繁的那么它的所有子集也必须是频繁的。算法一层层生成候选集每次扫描整个事务库来计数。FP-Growth 则不同它把原始事务一次性压缩成一棵 FP 树之后在树上递归挖掘条件模式基不需要反复扫描原始数据。两者的边界在哪里我直接用一张表说清楚写报告的时候这段比对也能直接用对比维度AprioriFP-Growth原理候选集逐层生成 剪枝FP 树压缩 条件模式基递归扫描次数每层频繁项集至少全库扫描一次建树扫一次后续在树上递归内存占用低但候选集多时 CPU 开销大树越大内存越高需要控制节点规模适合数据量千级事务、项数少万级以上、事务较长的场景代码难度简单适合课程代码演示树结构稍复杂递归要小心报告加分点剪枝条件讲清楚条件模式基的构建过程配图实际选型时我的判断标准很简单如果事务数在一万以内用 Apriori 写起来快出规则也方便如果数据量到十万级或者单个事务平均长度超过 20Apriori 的候选集组合数会把 CPU 打满这时候老老实实上 FP-Growth。做课程大作业两个都实现一遍是性价比最高的选择既能体现对基础算法的掌握又能展示对性能优化的理解。2.3 用 Python 把原始表变成事务数据集DataFrame 清洗与编码从数据仓库抽出来的数据通常还带着脏东西最常见的有三种空订单、商品名首尾空格、同一商品的不同写法比如「牛奶」和「蒙牛纯牛奶」。清洗这一步直接决定算法结果能不能讲通我一般会在代码里留一个清洗函数。import pandas as pd def load_transactions_from_sql_csv(file_path: str) - list: 从 SQL 导出 CSV 加载事务返回去重后的项集列表 df pd.read_csv(file_path, dtype{order_id: str, items: str}) transactions [] for order_id, items_str in zip(df[order_id], df[items]): if pd.isna(items_str): continue items [item.strip() for item in str(items_str).split(,)] items list(set(item for item in items if item)) # 去空、去重 if len(items) 2: continue # 过滤单元素事务 transactions.append(items) return transactions这段代码把 CSV 里读出的订单序列和商品序列变成一个 Python 列表的列表。item.strip()处理首尾空格set()去掉同一个事务里的重复项len(items) 2的过滤提早去掉无意义事务。注意这里order_id用字符串读防止 ID 以科学计数法显示导致后续 join 出错。清洗完后如果你要用 FP-Growth 但不想处理字符串比较的性能损耗可以再做一次整数编码把每个商品映射成一个整数 id事务列表里存整数而不是字符串。这也是后续 FP-Growth 建树能跑得快的关键一步因为整数 key 的哈希和比较远快于字符串。字符串到整数的映射要在全量商品上先构建否则不同事务的同一商品会编出不同 id。def encode_transactions(transactions: list) - tuple: 把事务项编码为整数并返回解码表 item_to_id {} id_to_item [] for trans in transactions: for item in trans: if item not in item_to_id: item_to_id[item] len(id_to_item) id_to_item.append(item) encoded [[item_to_id[i] for i in trans] for trans in transactions] return encoded, item_to_id, id_to_item编码之后Apriori 和 FP-Growth 的操作对象从字符串变成 int速度快一大截。后面输出规则时再通过id_to_item把整数翻译回商品名这样报告里看到的规则就是「牛奶 → 面包」而不是「3 → 7」。3. 手写频繁模式挖掘代码Apriori 与 FP-Growth 的落地实现3.1 Apriori 最小可跑版本候选集生成、剪枝与支持度计数大作业要求「源代码」所以我不推荐直接from mlxtend.frequent_patterns import apriori交差那会被一眼看穿。自己写一个能跑、能讲清楚的 Apriori 并不难关键是在候选集生成和剪枝两个环节写注释。这里给一份我常用的 Apriori 实现大约 50 行足够应付答辩。它输出所有满足最小支持度的频繁项集附带每个项集的支持度。from itertools import combinations from collections import defaultdict def apriori(transactions: list, min_support: float) - dict: Apriori 算法 :param transactions: 事务列表每一项是 set 类型 :param min_support: 最小支持度阈值0~1 :return: {项集: 支持度计数} n len(transactions) trans_sets [set(t) for t in transactions] # 1. 统计单项集支持度计数 item_count defaultdict(int) for trans in trans_sets: for item in trans: item_count[frozenset([item])] 1 freq_items {} k 1 current_candidates {item for item, count in item_count.items() if count / n min_support} if not current_candidates: return freq_items while current_candidates: # 2. 记录当前 k 项集及其支持度 for cand in current_candidates: freq_items[cand] item_count[cand] k 1 # 3. 由 (k-1) 项频繁集生成 k 项候选集 candidate_sets set() cand_list list(current_candidates) for i in range(len(cand_list)): for j in range(i 1, len(cand_list)): union cand_list[i] | cand_list[j] if len(union) k: candidate_sets.add(union) # 4. 剪枝任一子集不在频繁集中则去掉 candidate_sets { cand for cand in candidate_sets if all((item in freq_items) for item in [frozenset(sub) for sub in combinations(cand, k - 1)]) } # 5. 扫描数据集统计候选集支持度 item_count.clear() for cand in candidate_sets: item_count[cand] sum(1 for trans in trans_sets if cand.issubset(trans)) current_candidates {cand for cand in candidate_sets if item_count[cand] / n min_support} return freq_items逻辑分五步每一步对应答辩时最容易问到的问题为什么用frozenset作为字典键——因为 set 本身不可哈希frozenset 可以为什么候选集只用两两合并——因为 k 项集只能由两个蕴含 k-1 项子集的项集合并而来这是 Apriori 生成法则剪枝那步为什么检查所有大小为 k-1 的子集——如果某个子集不频繁那么它超集的计数一定不达标这是 Apriori 性能的核心所在。有一点要特别说明上面的剪枝实现为了简洁直接查freq_items字典但item_count里存的计数是上一轮更新过的逻辑上没问题性能上算中等。如果你要交「性能优化」作业可以改成先把所有候选集的子集检查提前到生成阶段但那样代码长度会翻倍不建议作为大作业初版。3.2 FP-Growth 实现从建树到条件模式基FP-Growth 的代码分两段建树和挖掘。树的结构不复杂但用 class 写会让递归清楚很多我给出的版本按标准教材思路实现能直接输出频繁项集。class FPNode: def __init__(self, item, parent): self.item item # 商品编码 self.count 1 # 路径计数 self.parent parent # 父节点 self.children {} # 子节点item - FPNode self.next None # 指向同项节点的链表指针 def build_fptree(encoded_transactions: list, min_support_count: int): 构建 FP 树并返回头指针表 # 第一次扫描统计单项支持度 item_count defaultdict(int) for trans in encoded_transactions: for item in set(trans): item_count[item] 1 # 过滤非频繁项并按支持度降序 frequent_items {item: cnt for item, cnt in item_count.items() if cnt min_support_count} if not frequent_items: return None, None # 为每个事务排序只保留频繁项按支持度降序 sorted_transactions [] for trans in encoded_transactions: filtered [item for item in trans if item in frequent_items] if filtered: filtered.sort(keylambda i: (frequent_items[i], -i), reverseTrue) sorted_transactions.append(filtered) # 第二次扫描建树 root FPNode(None, None) header_table {item: [] for item in frequent_items} for trans in sorted_transactions: node root for item in trans: if item in node.children: node.children[item].count 1 else: new_node FPNode(item, node) node.children[item] new_node header_table[item].append(new_node) node node.children[item] return root, header_table def fp_growth(header_table: dict, min_support_count: int, prefix: set, freq_items: dict): 递归挖掘 FP 树 for item, nodes in sorted(header_table.items(), keylambda x: len(x[1])): new_prefix prefix | {item} support_count sum(node.count for node in nodes) if support_count min_support_count: freq_items[frozenset(new_prefix)] support_count # 构造条件模式基 conditional_transactions [] for node in nodes: path [] parent node.parent while parent.item is not None: path.append(parent.item) parent parent.parent if path: for _ in range(node.count): conditional_transactions.append(path) # 递归条件树 if conditional_transactions: root, cond_header build_fptree(conditional_transactions, min_support_count) if cond_header: fp_growth(cond_header, min_support_count, new_prefix, freq_items)这个实现是标准的单路径递归没有做头指针表next优化但作为课程源码完全够用。答辩时老师多半会问「条件模式基是怎么来的」你要能说清楚FP 树上项item的每一个节点从它一直向上走到根节点经过的路径就是一条条件模式基把所有路径按节点计数重复就构成条件事务库对这个库再次建树就是条件 FP 树。注意递归的终止条件条件事务库为空或者条件树里没有达到最小支持度的项时build_fptree返回None递归自然终止。还有一点容易踩conditional_transactions里如果某条路径只有根节点没有父项路径为空不能append([])进去否则会把空事务当成一个条件模式基导致递归死循环。3.3 参数怎么设支持度、置信度、提升度的计算与输出拿到了频繁项集之后下一步是生成关联规则。这里的三个参数是报告里必写的支持度是「同时买了 A 和 B 的事务占所有事务的比例」代表规则覆盖范围置信度是「买 A 的事务里同时买 B 的比例」代表规则可靠程度提升度是「B 在 A 条件下的概率除以 B 的全局概率」大于 1 表示 A 和 B 正相关等于 1 表示独立。大作业里最少要输出三列规则、支持度、置信度加一列提升度就是加分项。def generate_rules(freq_items: dict, transactions: list, min_conf: float) - pd.DataFrame: 从频繁项集生成关联规则返回 DataFrame n len(transactions) support_dict {itemset: count / n for itemset, count in freq_items.items()} rules [] for itemset in support_dict: if len(itemset) 2: continue for l_size in range(1, len(itemset)): for antecedent in combinations(itemset, l_size): antecedent_set frozenset(antecedent) consequent itemset - antecedent_set if not consequent: continue support support_dict[itemset] confidence support / support_dict[antecedent_set] if confidence min_conf: lift confidence / support_dict[consequent] rules.append({ antecedent: 、.join(id_to_item[i] for i in antecedent), consequent: 、.join(id_to_item[i] for i in consequent), support: round(support, 4), confidence: round(confidence, 4), lift: round(lift, 4), }) return pd.DataFrame(rules)这段代码用combinations把一个频繁项集拆成前件和后件confidence min_conf做剪枝。参数上我的经验值是支持度 0.02~0.05置信度 0.5~0.7提升度只要大于 1 就可以放进报告但要单独标出提升度最高的几条规则——那才是答辩时老师眼前一亮的点。id_to_item是上一章编码时留下的解码表没有它你输出的会是数字报告里根本没法看。如果你没做整数编码直接把antecedent_set里的字符串项用、.join(...)拼出来效果也一样只是性能差一些。4. 写文档和报告把代码过程变成能答辩的素材4.1 文档说明里必须有的五个部分老师查大作业通常先看文档说明如果文档和源码对不上直接扣大分。一份能自洽的文档说明我建议固定五个部分一、环境依赖Python 版本常见的做法是 3.8 以上pip 包只列pandas、matplotlib、itertools标准库不用列。不要忘了写python安装和pycharm配置python环境的步骤哪怕只是三行字也能证明代码不是在别人机器上才能跑。二、数据说明数据来自哪个表、多少条事务、平均事务长度、预处理做了什么要一一写清。三、代码结构文件清单和每个函数的作用用树形目录。四、运行步骤从清洗到出结果每个命令怎么敲输出文件在哪里。五、算法参数表和规则结果表。这五个部分缺一不可尤其是参数表很多同学只写「min_support0.02」但不解释为什么是 0.02老师一问就尴尬。我一般会在参数表旁边补一句「设置 0.02 是因为事务总数为 5000最小支持度计数为 100确保 2 项集至少有 100 条记录支撑。」4.2 报告 PDF 的实验设计数据集、对比指标和结果截图报告 PDF 是给老师看的第一印象不是代码复制粘贴。实验设计部分要回答三个问题我在什么数据上做、我用了什么算法、我怎么判断结果好坏。数据部分可以画一张事务分布直方图横轴是订单里商品数量纵轴是订单数能说明数据的稀疏程度。算法部分写清楚两个算法的时间复杂度对比Apriori 是O(k * |D| * C^k)FP-Growth 是O(|D| * 平均路径深度)不用推公式但要指出 FP-Growth 为什么不再反复扫描数据库。结果部分要有对比表比如同一支持度下两个算法跑出的频繁项集数量以及运行时间。运行时间可以用time.time()包一下但注意 Python 的计时波动很大建议每轮跑三次取中位数并写入报告。我之前见过有人直接把一次计时写进报告结果答辩现场重跑时数据对不上整组都很被动。报告转 PDF 的常见做法是把代码和图表放在 Jupyter Notebook 里跑完然后导成 HTML 再打印为 PDF也可以直接用 Markdown 写报告用浏览器打印成 PDF。无论哪种PDF 里必须能看到代码输出和截图不能只给结论。4.3 用 Python 自动生成图表频繁项集分布与关联规则网络报告里最好有两张图一张是支持度最高的前 N 个频繁项集条形图一张能体现规则的散点图或网络图。条形图用 matplotlib 就能画代码量小效果理性。import matplotlib.pyplot as plt import matplotlib # 报告里把字体设置写清楚 matplotlib.rcParams[font.sans-serif] [Microsoft YaHei, SimHei, PingFang SC] matplotlib.rcParams[axes.unicode_minus] False def plot_top_itemsets(freq_items: dict, n15): 画支持度最高的 n 个频繁项集 sorted_items sorted(freq_items.items(), keylambda x: x[1], reverseTrue)[:n] names [、.join(id_to_item[i] for i in itemset) for itemset, _ in sorted_items] counts [cnt for _, cnt in sorted_items] plt.figure(figsize(12, 5)) plt.barh(names[::-1], counts[::-1]) plt.xlabel(支持度计数) plt.title(Top {} 频繁项集.format(n)) plt.tight_layout() plt.savefig(freq_itemset_top.png, dpi150) plt.show()注意字体那两行必须放在画图之前否则中文全变成方块。用Microsoft YaHei或SimHei要看你的操作系统Ubuntu 上要换成Noto Sans CJK SCMac 上PingFang SC更稳。另一个容易翻车的地方是barh的顺序直接用列表原顺序画最上面的会是倒数第 N 名所以[::-1]反转一下让最大的项集在上面报告里看着更舒服。散点图可以画每个规则的置信度与提升度横轴置信度、纵轴提升度再用颜色映射支持度。这个图能直观说明「低置信度但高提升度」的规则存在写报告时可以顺势提出高提升度才是真正的强关联单纯把置信度排序会漏掉长尾中的价值规则。5. 频繁模式挖掘避坑指南支持度虚高、内存爆炸和结果不可复现5.1 现象支持度算出来虚高我用 SQL 抽了几百条事务丢进 Apriori 跑结果是「牛奶 → 面包」支持度 0.6明显不对劲正常购物篮里牛奶和面包同时出现撑死百分之十几。原因我在GROUP_CONCAT之前没有对订单明细里的商品做DISTINCT同一个订单里买了两箱牛奶join 进事务后商品重复出现但更隐蔽的是我在做事实表和维度表 join 时一张订单触发了多行维度记录比如商品有多个分类属性导致同一订单同一商品被展开成多行。这样事务里重复项没有被去重支持度计数全部被放大。解决在 SQL 层面对order_id, product_name做DISTINCT事务列表里再用set()去重。最好在清洗函数中加一句断言assert len(trans) len(set(trans))测试通过再往下跑。5.2 现象FP-Growth 内存爆炸事务量不大五万条而已但 FP-Growth 一跑就内存飙升甚至卡死。原因FP 树里每一个节点都是一个带children字典的 Python 对象五万条事务如果每条平均有 20 个不同商品节点数能到几十万。再加上我在递归挖掘时对每个条件模式基都保存了一份conditional_transactions新列表内存直接爆掉。解决第一编码成整数字符串节点会让 Python 做大量哈希运算内存和时间都翻倍。第二在build_fptree第一步就过滤掉低于最低支持度计数的单项目不要保留低频项它们会导致树的分支非常多。第三如果条件模式基特别长可以在递归函数里加一个参数控制最大递归深度或者把conditional_transactions改成批量生成、边生成边处理而不是一次性全存进列表。5.3 现象报告里的结果和代码输出对不上答辩前我自己跑了一遍代码结果记录在报告里答辩现场老师让我再跑一遍结果规则数量不一样有几个频繁项集消失了。原因我在预处理阶段用了random.sample对原始数据做了抽样但没有设随机种子。Python 每次启动随机数都不同抽样结果自然不同后续的频繁项集会随之变化。解决所有涉及随机的操作在脚本最顶部统一写三行import random random.seed(42)顺带检查pandas的sample函数也要传random_state42。你还可以更进一步把抽样方式写进文档说明明确「为了结果可复现使用固定种子抽样 80% 数据」。这样就算结果变化也是合理预期而不是一致性错误。5.4 现象Apriori 跑了几分钟不出结果支持度设成 0.001事务数五千理论上应该很快结果 Apriori 跑了一分多钟还在第 2 层循环。原因min_support太低导致候选集爆炸。比如商品总数有 500 种最小支持度 0.001 意味着只要出现 5 次就算频繁那么单项频繁集可能有 300 个。这 300 个单项两两组合产生 4 万多个候选 2 项集再继续生成 3 项集就是千万级别。也就是说Apriori 的复杂度随候选集数量是指数级的。解决先看数据统计出现次数前 20 的单品画一个频次分布图然后把min_support设在能砍掉一半以上商品的位置。我常用的经验值是先试 0.02跑不通再往上调而不要往下调。另外可以把候选集生成改成先做「Apriori 剪枝」再计数我给出的apriori函数里已经包含剪枝如果直接用网上的暴力版代码性能会差出一个量级。5.5 现象文档和报告里中文乱码Python 控制台输出中文正常但导出到 PDF 后全是乱码或者 matplotlib 图表里的中文变成方块。原因有三层一是pandas写出 CSV 时默认utf-8Excel 打开还是乱码二是 matplotlib 默认字体DejaVu Sans不支持中文三是报告转 PDF 时用的工具没有嵌中文字体。解决写 CSV 时用encodingutf-8-sig这个参数会给文件头加 BOMExcel 能自动识别matplotlib 加rcParams[font.sans-serif]我一般把所有常见中文字体列在后面系统里有哪个用哪个写 PDF 前检查字体Jupyter 导出 HTML 时选择系统已安装的中文字体。这个坑几乎每个人都遇过提前在清洗函数里统一设置好后面就不会在报告阶段返工。6. 让这份大作业更耐看规则评估、交叉验证与代码结构化如果你的目标是分数冲到前 20%光跑出规则还不够。我给三条进阶方向都是可以在文档报告里单独开小节的内容。第一条用全置信度、Kulczynski 指标补充提升度的不足。提升度有一个已知缺陷对同时出现的项过于敏感两个都是高频项时提升度会虚高。这时候可以引入all_confidence support(A∪B) / max(support(A), support(B))和不平衡比|support(A)-support(B)| / (support(A)support(B))这两个指标不受事务总量影响能评价规则的平衡性。把它们加进规则表末尾并写一段「单一指标不足以衡量规则质量」的分析这个层次比单纯贴 Apriori 结果容易拿分得多。第二条做交叉验证式的稳定性验证。把事务按订单日期切成两半分别跑同一参数下的频繁模式挖掘统计两组频繁项集的重叠率。重叠率超过 60% 就说明规则稳定反之说明数据本身偏稀疏需要换参数或换算法。我在做数据仓库课程设计时用过这种切分法报告里的价值在于你不光展示了算法还展示了验证思路。代码上只需在transactions列表上做切片mid len(all_transactions) // 2 half1 all_transactions[:mid] half2 all_transactions[mid:] # 分别调用 fp_growth再比较频繁项集的 set 交集第三条把代码按函数拆成模块而不是一个脚本跑到底。我常用的结构是data_loader.py、algorithms.py、analysis.ipynb、config.py。config.py单独放所有超参数答辩时老师问「调整参数看效果」你只需改一个 dict 里的数值重新跑不用翻代码。这个习惯让大作业的工程感提升一个档次也方便导师把你的代码往实际项目方向复用。我自己的习惯是写大作业时永远先定义好「结果可复现」的标准再开始写代码。所有数据、所有参数、所有随机种子都固定下来这样就算答辩现场临时改阈值也能立刻对比新旧结果解释差异在预期之内。频繁模式挖掘这门课的技术含金量不在背 Apriori 推导而在你能不能把一份数据变成有解释力的规则并且能让别人照着你的文档重跑一遍。把这些坑提前填平你的源码、文档说明、报告 PDF 才算真正自洽也才算真正把这个方向学明白了。希望这份实践笔记能帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网