☰
FP-Growth关联规则挖掘MATLAB实战:从FP-Tree到稳定规则集
2026/10/11 2:19:20 网站建设 项目流程

简介:这份资源是面向数据挖掘初学者与MATLAB使用者的FP-Growth关联规则挖掘实现包,聚焦交易数据中的频繁项集发现与规则提取。包内共8个文件,以7个.m脚本和1个.mat数据集为主,脚本分别承担主流程调度、FP树构建、幂集与子集计算、规则展示及树结构可视化等职责,mydata数据集用于直接运行演示,压缩包约5KB,轻量易读。已有191人学习关注。读者可借此理解FP-Growth通过构建FP树减少数据扫描次数的核心思路,掌握支持度阈值设定、条件模式基生成与递归挖掘的完整流程,并可将代码作为基础扩展到市场篮子分析、网络行为分析等实际场景,适合作为关联规则挖掘的入门实践与二次开发起点。

1. 从一份 FP-Growth 关联规则挖掘压缩包说起:它到底能跑出什么结果

如果你手上有一批事务型数据——比如超市小票、用户行为日志、故障工单里的配件组合——想找出「买了 A 的人大概率也会买 B」这类规则,那这份FP-Growth Assiciation Rule Mining.rar就是冲这个场景来的。它是一份 MATLAB 实现的关联规则挖掘资源包,核心算法是 FP-Growth,配套的还有从频繁项集到关联规则的完整流程。很多人第一次接触关联规则是从 Apriori 开始的,但 Apriori 每轮都要扫全表,数据量一上来就慢得让人想砸键盘;FP-Growth 用一棵 FP-Tree 把数据集压缩进内存,只需扫两遍数据库就能挖出频繁项集,这就是它值得单独拆一份的原因。这份资源适合两类人:一类是课程设计或毕设要做关联规则、但不想从零手写树的同学,另一类是手里有真实交易数据、想快速验证规则有效性的从业者。下面我按「它是什么 → 怎么跑起来 → 参数怎么调 → 坑在哪」的顺序,把这份包拆开讲透。

2. FP-Tree 构建与频繁项集挖掘:MATLAB 里那棵树是怎么长出来的

2.1 为什么是 FP-Growth 而不是 Apriori

先把选型理由说清楚,不然调参的时候你都不知道自己在调什么。Apriori 的核心是「逐层生成候选集 + 反复扫描数据库验证支持度」,假设有 1 万条事务、每条平均 10 个项,候选集规模会随项数组合爆炸,扫描次数等于最大频繁项集长度,I/O 开销是它的死穴。FP-Growth 换了个思路:第一遍扫描统计每个项的支持度,丢掉低于最小支持度的项;第二遍扫描把剩下的事务按支持度降序插入一棵前缀树,也就是 FP-Tree。相同前缀的路径会共享节点,数据集被压缩成树结构,之后挖掘频繁项集只需要在这棵树上递归构造条件模式基,不再碰原始数据库。

这份 MATLAB 资源包的价值就在于它把这套流程完整落地了:从数据读入、支持度统计、FP-Tree 节点结构定义,到递归挖掘和规则生成,是一条能直接跑的链路。你拿到手不用去纠结「树节点怎么存孩子指针」这种实现细节,重点放在数据格式和参数上就行。

2.2 数据准备:事务数据的两种常见组织方式

跑之前先看你的数据长什么样。关联规则挖掘的输入是事务集合,每条事务是一个项集。MATLAB 里常见两种组织方式,一种是元胞数组,每个 cell 存一条事务的项名;另一种是稀疏矩阵或 0/1 矩阵,行是事务、列是项。这份包一般按元胞数组或字符矩阵读入,具体看你拿到的脚本入口。

假设你有一个transactions.mat,里面变量trans是 1×N 的 cell,每个元素是字符串数组:

% 载入事务数据,trans 为 1xN cell,每个 cell 是一条事务的项列表 load('transactions.mat'); % 变量名按你实际文件改 % 检查前三条事务,确认格式没问题 for i = 1:3 disp(trans{i}); end % 统计事务总数和不同项的数量,心里有个底 numTrans = numel(trans); allItems = unique([trans{:}]); fprintf('事务数: %d, 不同项数: %d\n', numTrans, numel(allItems));

这段代码做三件事:载入数据、抽样打印确认每条事务是字符串数组而不是嵌套 cell、统计规模。unique([trans{:}])把所有事务拼接后去重,得到项的全集,这个数字直接决定后面 FP-Tree 的宽度。如果这里报错说维度不一致,八成是某些事务存成了数值、某些存成了字符串,得先统一类型。

2.3 最小支持度与最小置信度:两个必须一起定的参数

FP-Growth 挖掘频繁项集只受一个参数控制——最小支持度minSup。它有两种给法:绝对计数或相对比例。相对比例更通用,比如minSup = 0.01表示出现频率低于 1% 的项集直接不要。这个值定高了规则太少,定低了树爆炸、内存吃紧。

规则生成阶段再加一个最小置信度minConf,比如 0.6 表示「A 出现时 B 也出现的条件概率」至少 60% 才保留。这两个参数是联动的:minSup决定频繁项集的规模,minConf决定从这些项集里筛出多少条规则。

% 设定最小支持度(相对比例)和最小置信度 minSup = 0.02; % 2%,数据量大就往上调,规则太少就往下调 minConf = 0.6; % 60%,先跑一版看规则数量再微调 % 调用 FP-Growth 主函数,函数名以你包内实际为准 % 常见签名: [freqItemsets, rules] = fpgrowth(trans, minSup, minConf); [freqItemsets, rules] = fpgrowth(trans, minSup, minConf); % 输出频繁项集数量和规则数量,判断参数是否合理 fprintf('频繁项集数: %d\n', numel(freqItemsets)); fprintf('关联规则数: %d\n', numel(rules));

参数说明:minSup从 0.05 开始试比较稳,规则太少就降到 0.02、0.01;minConf一般从 0.5 到 0.7 之间起步。跑完先看两个数量,频繁项集几百条、规则几十到几百条是正常区间。如果频繁项集上万,说明minSup太低,树没剪干净;如果规则数为 0,要么minConf太高,要么minSup太高导致根本没有频繁项集。

2.4 从频繁项集到关联规则:支持度、置信度、提升度

频繁项集只是「哪些项经常一起出现」,关联规则才是「A → B」这种可解释的形式。一条规则的质量看三个指标:支持度是 A 和 B 同时出现的比例,置信度是 A 出现时 B 出现的条件概率,提升度是置信度除以 B 自身的支持度——提升度大于 1 才说明 A 对 B 有正向拉动,等于 1 就是独立没关系。

% 遍历规则,打印支持度、置信度、提升度 for i = 1:numel(rules) r = rules{i}; % 字段名以你包内结构为准,常见为 antecedent/consequent/support/confidence/lift fprintf('%s => %s | sup=%.3f conf=%.3f lift=%.3f\n', ... strjoin(r.antecedent, ','), strjoin(r.consequent, ','), ... r.support, r.confidence, r.lift); end

这里最容易翻车的是字段名对不上。不同实现里规则结构体可能叫antecedent/consequent,也可能叫items/predicted,跑之前先disp(rules{1})看一眼真实字段。提升度这个指标一定要看,光看置信度高就下结论是新手常犯的错——如果 B 本身就是高频项,A→B 的置信度天然就高,但提升度可能接近 1,这种规则没有实际价值。

3. 把压缩包跑通:从解压到出规则的操作链路

3.1 解压后的目录结构与入口脚本定位

拿到.rar先解压,MATLAB 资源包通常包含几个部分:主算法函数文件、示例数据、一个 demo 或 main 脚本、可能还有说明文档。你要找的是那个能直接运行的入口脚本,一般叫main.m、demo.m或test_fpgrowth.m。别一上来就改算法文件,先跑通 demo 看输出长什么样。

% 把包目录加入 MATLAB 搜索路径,避免函数找不到 addpath(genpath(pwd)); % 在解压后的根目录执行 % 确认关键函数可见 which fpgrowth % 应返回函数所在路径 % 运行入口脚本 main; % 或 demo / test_fpgrowth,按实际文件名

addpath(genpath(pwd))把当前目录及所有子目录加进路径,这样跨文件夹调用函数不会报 undefined。which fpgrowth是确认函数能被找到的最快方式,返回空就说明路径没加对或者函数名和你想的不一样。

3.2 用自带数据跑第一版结果

先别急着换自己的数据,用包内示例数据跑一遍,确认整条链路是通的。示例数据一般规模小、项数少,跑起来快,输出也容易看懂。

% 载入示例数据(文件名以包内实际为准) load('sample_data.mat'); % 用默认参数跑一版 minSup = 0.1; minConf = 0.5; [freqItemsets, rules] = fpgrowth(trans, minSup, minConf); % 打印前 5 条规则看看长什么样 for i = 1:min(5, numel(rules)) disp(rules{i}); end

这一步的目的是建立「正常输出」的基准。记住示例数据在默认参数下的频繁项集数和规则数,换成自己数据后如果数量级差太多,就知道是数据问题还是参数问题。示例跑不通,后面全是白搭,所以这一步别跳。

3.3 换成自己的数据:格式对齐是第一步

自己的数据往包里塞,最常见的失败就是格式不匹配。包内函数期望的是元胞数组,你给的是表格或矩阵,直接报错。转换逻辑不复杂,但要注意每一条事务的项必须是同一类型。

% 假设原始数据是 N x M 的 0/1 矩阵,行是事务、列是项 % 转成 cell 数组,每个 cell 存该事务中值为 1 的项名 itemNames = arrayfun(@(x) sprintf('item%d', x), 1:size(data,2), 'UniformOutput', false); trans = cell(size(data,1), 1); for i = 1:size(data,1) trans{i} = itemNames(data(i,:) == 1); end % 过滤掉空事务,空 cell 会让树构建出问题 trans = trans(~cellfun(@isempty, trans)); fprintf('有效事务数: %d\n', numel(trans));

data(i,:) == 1找出该事务包含的项,映射成项名。空事务必须过滤,否则插入 FP-Tree 时会出现空路径,轻则结果异常重则报错。如果你的原始数据是长表格式(每行一条「事务ID, 项名」记录),那就先用unique和accumarray或分组聚合把它转成事务级 cell,这一步没有捷径,数据清洗的活省不掉。

3.4 结果导出与规则排序

跑出规则后一般要导出成表格,方便排序和筛选。按提升度降序排是最实用的看法,提升度高的规则才是真正有洞察的。

% 把规则整理成表格并按提升度降序排列 nRules = numel(rules); ante = cell(nRules,1); cons = cell(nRules,1); sup = zeros(nRules,1); conf = zeros(nRules,1); lift = zeros(nRules,1); for i = 1:nRules ante{i} = strjoin(rules{i}.antecedent, ','); cons{i} = strjoin(rules{i}.consequent, ','); sup(i) = rules{i}.support; conf(i) = rules{i}.confidence; lift(i) = rules{i}.lift; end T = table(ante, cons, sup, conf, lift, 'VariableNames', {'前件','后件','支持度','置信度','提升度'}); T = sortrows(T, '提升度', 'descend'); writetable(T, 'association_rules.csv'); disp(T(1:min(10,height(T)), :));

导出 CSV 的好处是可以丢进 Excel 或 BI 工具继续分析。排序后重点看提升度前 10 条,这些是数据里最值得关注的组合。支持度和置信度作为辅助过滤,比如你只关心支持度大于 0.05 的规则,在表格里筛一下就行。

4. 避坑与排查:跑 FP-Growth 时最容易翻车的五个地方

4.1 现象:函数报 undefined,明明文件就在那

原因:MATLAB 只搜索当前工作目录和已加入路径的目录,解压后的子文件夹不会自动进路径。解决:在包根目录执行addpath(genpath(pwd)),再用which 函数名确认。如果which返回空,检查函数文件名和调用名大小写是否一致,MATLAB 在 Linux 下区分大小写。

4.2 现象:频繁项集数量爆炸,内存直接拉满

原因:minSup设得太低,大量低频项进入 FP-Tree,树的分支数失控。解决:先把minSup往上调一个数量级试跑,比如从 0.01 调到 0.1,看频繁项集数量是否回到合理区间,再逐步往下找平衡点。数据项数超过几千时,minSup低于 0.01 基本不可行。

4.3 现象:规则数为 0,但频繁项集明明有一堆

原因:minConf设得太高,或者频繁项集里全是单项集,无法构成「A → B」的规则。解决:先把minConf降到 0.3 看有没有规则出来,如果有再逐步往上调。同时检查频繁项集里两项及以上的占比,如果几乎全是单项集,说明minSup还是偏高,把长项集都剪掉了。

4.4 现象:提升度算出来是 Inf 或 NaN

原因:后件的支持度为 0 导致除零,或者规则结构里支持度字段没正确赋值。解决:检查规则生成阶段是否过滤了后件支持度为 0 的规则,正常实现应该在计算提升度前就排除掉。如果是字段读取错误,disp(rules{1})看结构体真实字段名,别照着记忆里的名字硬写。

4.5 现象:换了自己的数据后结果完全不合理

原因:数据格式没对齐,比如事务里混入了数值和字符串,或者项名里有空格、逗号导致strjoin后无法区分。解决:统一项类型为字符串,项名里避免分隔符;转换后用disp(trans{1:3})肉眼确认每条事务的项列表干净。数据清洗占整个流程七成时间,这不是夸张。

5. 进阶技巧:用多组参数扫描找到稳定的规则集

单组参数跑出来的规则有偶然性,minSup稍微一动结果就变,这种规则拿去做决策是不踏实的。我一般会做参数扫描:固定minConf,让minSup从高到低走几个档位,观察规则数量和提升度分布怎么变。真正稳定的规则会在多个支持度档位下都出现,那些只在某一档冒出来的,大概率是噪声。

% 参数扫描:固定 minConf,遍历多个 minSup minConf = 0.6; supList = [0.1, 0.05, 0.03, 0.02, 0.01]; resultSummary = zeros(numel(supList), 3); for k = 1:numel(supList) [fi, rl] = fpgrowth(trans, supList(k), minConf); resultSummary(k,:) = [supList(k), numel(fi), numel(rl)]; fprintf('minSup=%.3f | 频繁项集=%d | 规则=%d\n', supList(k), numel(fi), numel(rl)); end % 找出规则数量开始急剧上升的拐点,那个 minSup 附近通常最合适

看resultSummary的第三列,规则数随minSup下降会先缓增后暴增,暴增点说明大量低频噪声项开始生成规则,拐点前那一档就是比较稳的选择。这个方法比拍脑袋定参数靠谱得多。

另一个技巧是交叉验证规则的稳定性:把数据随机分成两半,各自跑一遍,取两边都出现的规则作为稳定规则集。实现上就是加一层随机抽样和规则匹配,匹配键用「前件+后件」字符串拼接。

% 数据分半,取两边都出现的规则作为稳定规则 idx = randperm(numel(trans)); half1 = trans(idx(1:floor(end/2))); half2 = trans(idx(floor(end/2)+1:end)); [~, r1] = fpgrowth(half1, 0.03, 0.6); [~, r2] = fpgrowth(half2, 0.03, 0.6); key1 = cellfun(@(r) [strjoin(r.antecedent,',') '=>' strjoin(r.consequent,',')], r1, 'UniformOutput', false); key2 = cellfun(@(r) [strjoin(r.antecedent,',') '=>' strjoin(r.consequent,',')], r2, 'UniformOutput', false); stableKeys = intersect(key1, key2); fprintf('稳定规则数: %d / 单边规则数: %d, %d\n', numel(stableKeys), numel(key1), numel(key2));

intersect取两边都有的规则键,稳定规则数占单边的比例越高,说明规则越可靠。这个比例低于三成的话,要么数据量不够,要么minSup太低引入了太多偶然组合。

从那以后我每次跑关联规则,都强制先做一遍参数扫描加数据分半验证,两组都活下来的规则才拿去汇报。这份 MATLAB 资源包把 FP-Tree 和规则生成的核心逻辑都封装好了,你要做的就是把数据格式对齐、参数扫一遍、稳定性验一遍,剩下的就是解读规则背后的业务含义了。希望帮到你。

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

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

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

立即咨询