Matlab多元线性回归显著性检验:从regress到fitlm的完整实践
2026/9/18 17:28:01 网站建设 项目流程

简介:一份面向统计学、数据分析及Matlab建模初学者的多元线性回归完整实现文档。文档以研究生教材《数理统计》例4.4.1为背景,在回归方程F检验基础上,扩展了对各回归系数(x1、x2、x3)的t检验,支持用户自定义显著性水平α,提高计算精度。程序中包含数据读取、最小二乘参数估计、设计矩阵构建、F检验与t检验等关键步骤,输出结果整洁易读。使用者只需更换Excel数据文件即可应用于其他维度数据集,移植性强。资源共1个docx文件,压缩包大小50KB,文件包含程序说明、数据输入格式、完整Matlab源码及运行结果示例,适合需要对照教材理解回归显著性检验原理、并希望获得可直接运行模板的读者。目前已有246人学习下载。

1. 多元线性回归显著性检验到底查什么:先看p值还是先看模型

拿到“多元线性回归及显著性检验Matlab程序.docx”这个标题,我第一反应是:这不是一个求系数的任务,而是一个做“归因判断”的任务。多元线性回归的Matlab程序很容易写,调用一次 regress 就能拿到 b 和 stats,但标题里特意带上“显著性检验”,意味着要回答的不只是“y 和 x 是什么关系”,而是“哪些 x 真正影响 y,这种影响在统计上是否站得住脚”。在实际项目中,这个区别很现实:论文外审会问回归表和显著性标记,业务评审会问某个系数为什么是 2.3,以及它为什么能推广到新数据。

这类程序适合两类读者:一类是写课程作业、毕业论文或实证论文的人,需要把回归结果按规范格式呈现;另一类是做数据分析或算法验证的工程师,手上有一组带标签的观测数据,想快速判断特征是否有效。下面我按自己常用的做法,把模型建立、整体显著性检验、系数显著性检验、诊断和封装串成一条完整路径。你会看到同一组数据用 regress 和 fitlm 两种方式跑出来结果一致,但呈现思路不同,而显著性检验才是决定模型能否交付的关键。

2. 用Matlab跑通多元线性回归程序:regress与fitlm的数据组织和参数表

多元线性回归在 Matlab 里的入口很多,最常见的是统计工具箱里的 regress 和 fitlm。前者面向矩阵运算,输出维度直接,适合写进自动化程序;后者面向表格数据,自带回归报告,适合交互式分析和出图。在开始之前要做一件事:确认当前环境已经安装了 Statistics and Machine Learning Toolbox。无论你是刚完成 Matlab 下载,还是使用公司内网安装版本,工具箱缺失时调用 regress 会报出 Undefined function,这个问题在安装步骤里常被忽略,值得先检查。

2.1 多元回归的数据组织:设计矩阵必须有一列截距项

regress 的调用格式是[b,bint,r,rint,stats] = regress(y,X,alpha),其中 y 是 n×1 列向量,X 是 n×(p+1) 设计矩阵。这里的“p+1”是很多新手第一个踩坑点:regress 不会自动帮你补截距列,它把 X 的第一列当作 beta0 的系数列。也就是说,如果你想拟合 y = beta0 + beta1x1 + beta2x2,X 必须是[ones(n,1), x1, x2],而不是直接把原始变量拼在一起。

fitlm 的设计刚好相反,它默认包含截距项,从 table 类型的表格数据出发,用公式y ~ x1 + x2 + x3描述模型。因此在数据组织上,使用 fitlm 时只需要把变量放在一个 table 里,函数会自动为截距项分配一行参数。两种方式没有好坏之分,我通常的做法是:需要在循环里批量跑模型、后续还要做残差诊断时用 regress;需要快速查看模型报告、画预测图和残差图时用 fitlm。

2.1.1 为什么 regress 要求自己加常数项列

这和 regress 的底层实现有关。它直接对线性方程组 Xb = y 做最小二乘求解,X 的每一列都对应 b 的一个分量。如果你不给常数项列,求出的回归直线会被强制穿过原点,模型实际上是 y = beta1x1 + beta2*x2,截距被丢弃。这在变量中心化且理论上确实过原点的问题里没问题,但绝大多数业务数据并不满足这个前提。一个简单的检查方法是:在调用 regress 前查看size(X,2) - size(unique(X,'rows'),2),如果设计矩阵没有全 1 列,结果会很反常。

2.1.2 一个可复现的示例数据集

为了演示,我构造一组已知真实关系的样本数据:y 与三个变量 x1、x2、x3 满足线性关系,并加入随机噪声。这样后续的显著性检验结果能和我们预设的系数对照,方便判断程序是否正确。样本量设为 30,足以支撑 3 个自变量的检验。

2.2 regress 的返回值与 stats 参数含义

regress 的五个输出参数中,b 是回归系数向量,bint 是每个系数在置信水平 1−alpha 下的置信区间,r 是残差,rint 是每个残差的置信区间,stats 是模型统计量汇总。前四个参数对后续残差图很有用,第五个参数则是显著性检验的核心数据。下面是一张参数速查表,适合放在程序注释旁边。

输出参数维度说明
b(p+1)×1回归系数,b(1) 是截距项
bint(p+1)×2每个系数的置信区间,第一列为下限,第二列为上限
rn×1残差向量,等于 y 减去拟合值
rintn×2残差置信区间,用于 rcoplot 画诊断图
stats1×4依次为 R²、F 统计量、F 对应的 p 值、误差方差估计

要注意 stats 的四个分量顺序固定:stats(1) 是确定系数 R²,stats(2) 是回归方程显著性 F 统计量,stats(3) 是 F 检验的 p 值,stats(4) 是均方误差的估计值。alpha 参数只影响 bint 和 rint 的置信水平,不会改变 b 和 stats 中的 F、p 值。所以在写程序时,把 alpha 单独定义成一个变量,方便后续换成 0.01 或 0.1 观察显著性变化。

2.3 最小可运行程序示例

% 多元线性回归及显著性检验最小程序 clear; clc; rng(0); % 固定随机种子,保证可复现 % 生成 30 个样本,3 个自变量 x1 = rand(30,1) * 10; x2 = rand(30,1) * 5; x3 = rand(30,1) * 2; % 构造设计矩阵:第一列为常数项 X = [ones(30,1), x1, x2, x3]; % 真实关系为 y = 3 + 2*x1 + 1.5*x2 - 1.2*x3 y = 3 + 2*x1 + 1.5*x2 - 1.2*x3 + randn(30,1) * 0.8; alpha = 0.05; [b, bint, r, rint, stats] = regress(y, X, alpha); disp('回归系数 b:'); disp(b); disp('每个系数的 95% 置信区间 bint:'); disp(bint); disp('模型统计量 [R^2, F, p, 误差方差估计]:'); disp(stats);

这段代码先用固定随机种子生成数据,再调用 regress。核心是X = [ones(30,1), x1, x2, x3],它把截距项作为第 1 列。运行后可以看到 b 大约在 3、2、1.5、-1.2 附近,bint 包围真实值,stats 第二个数是很大的 F 值,第三个数很小,说明整体回归显著。这里每个变量的系数都以真实值为中心,是因为样本噪声不大,且样本量足够覆盖 3 个自由度。

2.4 用 fitlm 输出回归表,和 regress 相互验证

如果觉得 regress 的结果不够直观,可以用 fitlm 生成带文字描述的回归表。先把变量放入 table,再指定公式。fitlm 会输出每个系数的估计值、标准误、t 统计量和 p 值,底部还会给出 F 检验结果,和 regress 的 stats 对应。

% 用 fitlm 生成结构化回归报告 tbl = table(x1, x2, x3, y, 'VariableNames', {'x1', 'x2', 'x3', 'y'}); mdl = fitlm(tbl, 'y ~ x1 + x2 + x3'); disp(mdl);

从 fitlm 输出中可以同时看到回归系数和显著性标记。rows 对应 Intercept、x1、x2、x3,Columns 包含 Estimate、SE、tStat、pValue。这个输出适合直接截图放进报告,而 regress 的输出更适合后续程序化处理。两种方式用同一份数据时结果一致,这可以作为一个验证手段:先用 regress 计算,再用 fitlm 复现,如果系数差异超过浮点精度范围,说明设计矩阵或数据组织存在问题。

3. 回归方程显著性检验:F统计量、决定系数R²和程序判定

模型有没有解释力,不是看 b 是否为 0,而是看所有自变量的联合解释力是否显著。这一步对应的是回归方程显著性检验,也就是通常说的 F 检验。它的作用域是“整个方程”,而不是某一个具体变量。很多初学者把注意力放在 R² 上,觉得 R² 达到 0.9 就把结果报上去,这是不够的。R² 描述的是拟合优度,F 检验描述的是统计显著性,二者一个看数据解释比例,一个看抽样波动下的置信度,必须同时出现在程序输出里。

3.1 F检验的原理:从方差分解到统计量计算

多元线性回归的 F 检验把总离差平方和 SST 分解成回归平方和 SSR 和残差平方和 SSE。原假设 H0 是所有自变量系数同时为 0,备择假设是至少有一个系数不为 0。F 统计量定义为 SSR 除以回归自由度 p,再除以 SSE 除以残差自由度 n−p−1。当 F 值较大,超过 F 分布临界值时,拒绝原假设,认为回归方程整体显著。

regress 已经把这个过程封装进 stats,第二个元素就是 F 值,第三个元素是对应的 p 值。用代码表示,F 值也可以从基础量计算出来,这样能加深理解。给定回归结果后,n = length(y)p = size(X,2) - 1,残差平方和由SSE = sum(r.^2)得到,那么:

% 从 regress 的结果中手动计算 F 统计量 n = length(y); p = size(X, 2) - 1; ybar = mean(y); SST = sum((y - ybar).^2); SSE = sum(r.^2); SSR = SST - SSE; F = (SSR / p) / (SSE / (n - p - 1)); pF = 1 - fcdf(F, p, n - p - 1);

把这段结果和 stats(2)、stats(3) 对比,会发现两者完全一致。fcdf 是 F 分布的累积分布函数,1 - fcdf(F, p, n-p-1)得到右侧尾部概率,也就是 p 值。F 值越大,p 值越小,这解释了为什么很多回归报告里只看 p 值就能判断整体显著性。注意自由度 n−p−1 是残差自由度,p 是自变量个数,不含截距项。

3.2 R²与调整R²在显著性判断中的角色

R² 等于 1 减去 SSE 除以 SST,表示自变量解释了 y 中变异的比例。但它有一个固有缺陷:只要增加自变量,R² 就会上升,哪怕新变量和 y 毫无关系。调整 R² 把自由度惩罚引入公式,在 p 增大时抵消一部分 R² 的虚增。因此,在做显著性检验时,我通常同时输出 R² 和调整 R²,并观察二者差距是否过大。如果差距明显,说明模型里很可能混入了不显著的解释变量。

指标计算公式使用建议
1 − SSE/SST描述拟合优度,不惩罚变量数量
调整 R²1 − (1−R²)(n−1)/(n−p−1)比较不同自变量个数的模型时更可靠
F 统计量(SSR/p)/(SSE/(n−p−1))判断整体显著性,伴随 p 值
误差方差估计SSE/(n−p−1)即 stats(4),用于计算系数标准误

调整 R² 的计算在 Matlab 里一行就能完成:adjR2 = 1 - (1-stats(1)) * (n-1) / (n-p-1)。把它放在报告输出中,优先级高于单纯的 R²。当 R² 为 0.95、调整 R² 只有 0.60 时,说明模型塞进了太多低解释力变量,此时再看 F 检验和单个系数的 t 检验,往往会发现大量不显著项。

3.3 用程序判定整体显著性并输出结论

实际使用中,我习惯在回归程序后加一段自动判定代码,把 stats 的三个关键量格式化输出。比如设置 alpha = 0.05,当stats(3) < alpha时,可以判断模型整体显著;否则需要检查数据是否存在严重共线性、变量个数是否过多,或者原始变量是否需要变换。下面这段代码可以直接嵌在上一章的程序后:

% 整体显著性判定 if stats(3) < alpha fprintf('模型整体显著:F=%.3f,p=%.4g\n', stats(2), stats(3)); else fprintf('模型整体不显著:F=%.3f,p=%.4g\n', stats(2), stats(3)); end fprintf('R^2=%.4f,调整R^2=%.4f\n', stats(1), 1-(1-stats(1))*(n-1)/(n-p-1));

这里有个细节:p 值在极小的时候,disp会显示成 1.2345e-12,用%.4g格式化后更友好。整体显著不等于每个自变量都显著,这一结论直接引出下一章的单变量显著性检验。如果整体不显著,后面的 t 检验单独看意义也不大,应当先回到变量选择和数据质量问题上。因此程序里要把整体检验放在变量筛选之前。

4. 回归系数显著性检验:t检验、置信区间和变量筛选

F 检验回答的是“模型整体有没有用”,t 检验回答的是“每个变量是不是真的有用”。二者互补,但不互相替代。一个典型的误读场景是:F 检验显著,业务方就认为所有自变量都该留下,结果发现 x3 的 p 值是 0.35,根本没有通过显著性检验。反过来也可能出现整体不显著但某个变量单独显著的情况,这通常由多重共线性或样本量不足造成。多元线性回归的显著性检验,最终要落在每个解释变量头上。

4.1 为什么必须对每个回归系数做 t 检验

t 检验的原假设是 H0: beta_j = 0,备择假设是 beta_j 不等于 0。检验统计量是参数估计值除以它的标准误:t = b_j / se(b_j),其中 se(b_j) 是 b_j 的抽样标准差,由噪声方差和设计矩阵共同决定。回归方程里有 p 个自变量,就有 p 个这样的 t 检验。regress 返回的 bint 就是基于 t 分布构造的置信区间,如果区间跨过 0,说明在给定置信水平下,无法排除该系数为 0 的可能。

这一环节必须结合自由度来解释。残差自由度是 n−p−1,t 统计量服从自由度 n−p−1 的 t 分布。样本量越小,临界值越大,相同 t 值下显著性越难显现。比如 n=10、p=3 时,自由度只有 6,95% 置信水平下的临界 t 值超过 2.4;而 n=100 时临界值接近 1.98。所以在写程序时,不要用固定的 1.96 作为 t 临界值,应当用 tinv 或 tcdf 按实际自由度计算。

4.2 从 regress 结果手动计算 t 统计量和 p 值

regress 没有直接返回 t 统计量,但 stats(4) 给出了误差方差估计值 MSE。系数协方差矩阵的估计是 MSE 乘以 X'X 的逆矩阵,对角线开方就是每个系数对应的标准误。拿到标准误后,t 统计量和 p 值可以用以下代码得到:

% 手算 t 统计量与 p 值,和 bint 互相验证 [b, bint, ~, ~, stats] = regress(y, X, alpha); n = size(X, 1); k = size(X, 2) - 1; df = n - k - 1; MSE = stats(4); C = inv(X' * X); se = sqrt(diag(MSE * C)); % 每个系数的标准误 tstat = b ./ se; pval = 2 * (1 - tcdf(abs(tstat), df)); fprintf('变量\t系数\t标准误\tt值\tp值\n'); for j = 1:length(b) fprintf('x%d\t%.4f\t%.4f\t%.4f\t%.4g\n', j-1, b(j), se(j), tstat(j), pval(j)); end

代码中用tcdf计算 t 分布累积概率,双尾 p 值乘 2。注意这里 b(1) 对应截距项,它的 p 值一般不作为变量筛选依据,只有业务上关心截距是否为 0 时才需要解读。严格来说,inv(X'*X)在 X 存在严重复共线性时不稳定,更稳的写法是使用pinv(X'*X)或直接调用regress内部的结果;对于常规数据,这个公式便于理解。

4.3 用置信区间和 t 检验结果快速筛选变量

除了看 p 值,bint 是一个更直观的判定工具。某个系数的 bint 区间如果落在 0 的同侧,说明下限和上限同号,此时区间不含 0,可以拒绝原假设。如果下限为负、上限为正,则意味着系数在正负之间波动,不能确认它对 y 有真实作用。可以用符号判断写一段筛选代码:

% 用 bint 识别不显著的回归系数 coef_sign = sign(bint(:, 1)) .* sign(bint(:, 2)); insig_idx = find(coef_sign <= 0); % 区间包含 0,不显著 if ~isempty(insig_idx) fprintf('以下系数未通过显著性检验:'); fprintf('%d ', insig_idx - 1); fprintf('\n'); else fprintf('所有系数均通过显著性检验\n'); end

对于上述模拟数据,x1、x2、x3 通常都会通过检验。为了让筛选逻辑更通用,下面用一张示意表说明不同情况下的判定结果。实际项目里,x1 的系数可能是显著的,x2 处于临界,x3 不显著,这时应该把 x3 从模型中去掉并重新拟合。

变量bsetp判定
x12.030.316.550.0002显著
x21.470.364.080.0010显著
x3-0.210.30-0.700.4950不显著
x4-1.341.18-1.140.2680不显著

需要特别提醒的是,一次检验不显著不代表变量本身和 y 无关,也可能是样本量不够,或者该变量与其他变量高度相关。在删除变量之前,先回到数据相关性矩阵和 VIF 检查,这一步可以避免误删有效特征。如果只是做预测,保留不显著变量虽然会增加参数复杂度,但不一定降低预测精度;只有要解释变量作用时,才必须严格执行剔除规则。

5. 显著性检验后的诊断:残差图、多重共线性与stepwisefit

显著性检验做完并不代表模型可以交付。回归模型的可靠性建立在几个假设上:残差独立、方差齐性、残差近似正态,以及自变量之间不存在严重多重共线性。如果这些假设被踩破,刚才的 F 和 t 检验都会失真。最常见的例子是:两个自变量相关系数达到 0.95,模型整体 F 值很大,但每个系数的 t 检验都不显著,原因是系数估计的标准误被共线性放大。此时如果直接看 p 值删除变量,会得出完全错误的结论。

5.1 残差与异常点:rcoplot和normplot怎么看

regress 的 r 和 rint 是专门喂给残差图的。rcoplot(r, rint) 会画出每个样本的残差竖线和置信区间,如果某条残差置信区间不跨过 0,说明该样本对拟合结果影响较大,可以视为异常点。normplot(r) 用于检查残差是否服从正态分布,如果散点大致落在一条直线上,说明正态假设合理;如果两端发散严重,后续 p 值的可信度就要打折。

% 显著性检验后的残差诊断 figure; rcoplot(r, rint); % 残差及置信区间图 title('残差置信区间诊断'); figure; normplot(r); % 正态概率图 title('残差正态性检验');

在 rcoplot 中看到个别样本的区间明显偏离其他样本时,不要急着删除。先检查该样本的原始数据是否存在录入错误,再考虑是否属于业务上的特殊情况。一段稳健的做法是:删除异常点后重新跑回归,对比前后系数和显著性是否发生剧烈变化。如果变化不大,说明模型稳定;如果显著性结论反转,说明原结果本身脆弱,需要在报告里如实说明。

5.2 多重共线性检验:计算VIF替代直接删变量

多重共线性的常用指标是方差膨胀因子 VIF。对第 j 个变量,先把其他自变量作为输入,对这种变量做回归,得到决定系数 R_j²,然后 VIF_j = 1 / (1 − R_j²)。VIF 大于 10 通常被认为存在严重共线性,5 到 10 之间需要警惕。计算 VIF 的代码可以这样写:

% 计算各自变量的 VIF Xall = [x1, x2, x3]; [nobs, px] = size(Xall); VIF = zeros(1, px); for j = 1:px Xm = Xall; Xm(:, j) = []; % 去掉当前变量 Xm = [ones(nobs, 1), Xm]; % 补截距列 [~, ~, ~, ~, stat_j] = regress(Xall(:, j), Xm); Rj2 = stat_j(1); VIF(j) = 1 / (1 - Rj2); end disp('各变量 VIF:'); disp(VIF);

Xm 不需要截距之外的额外处理,关键是用 regress 对当前变量做回归时,它的设计矩阵要包含全 1 列。若某个变量的 VIF 超过 10,我通常先在相关矩阵里找到与之相关系数最高的变量,然后根据业务含义决定剔除哪一个。也可以保留该变量但改用岭回归,不过岭回归的系数不再是无偏估计,显著性检验的解释会变复杂。

5.3 用stepwisefit自动筛选显著变量

如果自变量数量较多,手动检验每个 t 值很繁琐。Matlab 的 stepwisefit 可以基于显著性检验自动选择变量,参数 penter 控制变量进入模型的 p 值阈值,premove 控制变量被剔除的阈值。一般 penter 设 0.05,premove 设 0.10,使变量一旦进入就不要因为轻微波动立刻离开。

% 逐步回归:基于显著性检验自动筛变量 [inmodel, ~, ~] = stepwisefit(Xall, y, 'penter', 0.05, 'premove', 0.10); disp('保留的变量索引:'); disp(find(inmodel));

inmodel 是逻辑向量,1 表示该变量保留在模型中。stepwisefit 返回的模型用全部样本重新估计,得到的系数和 p 值可以直接作为最终报告。需要注意的是,逐步回归的自动搜索会放大偶然显著性,尤其在变量多、样本少的情况下,最终入选的变量可能在一组新样本中不再显著。因此它只能作为变量初筛工具,最终模型还是要回到业务判断和残差诊断上。

6. 把多元回归程序封装成函数:显著性标记与结果导出

回顾前面的步骤,从 regress 到显著性判断,再到诊断,零散的脚本在多个项目里复用时会很别扭。常见做法是封装一个函数,输入 y、X 和显著性水平,输出结构化的回归结果表,把 t 值、p 值、置信区间和显著性标记一次性算好。这样无论是课程报告还是业务分析,一个命令就能生成结论。

function tbl = mlr_report(y, X, alpha) % 多元线性回归及显著性检验结果表 if nargin < 3 alpha = 0.05; end [b, bint, ~, ~, stats] = regress(y, X, alpha); n = size(X, 1); k = size(X, 2) - 1; df = n - k - 1; MSE = stats(4); se = sqrt(diag(MSE * inv(X' * X))); tval = b ./ se; pval = 2 * (1 - tcdf(abs(tval), df)); % 显著性星号标记 sig = cell(size(b)); for j = 1:numel(b) if pval(j) < 0.01 sig{j} = '**'; elseif pval(j) < 0.05 sig{j} = '*'; elseif pval(j) < 0.1 sig{j} = '.'; else sig{j} = ''; end end % 构造变量名 if all(X(:, 1) == 1) varnames = {'Intercept'}; start = 2; else varnames = {}; start = 1; end for j = start:size(X, 2) varnames{end + 1} = sprintf('x%d', j - start + 1); end tbl = table(b, bint(:, 1), bint(:, 2), se, tval, pval, sig, ... 'VariableNames', {'beta', 'CI_low', 'CI_up', 'SE', 't', 'p', 'sig'}, ... 'RowNames', varnames); end

调用时直接传入数据和设计矩阵即可:tbl = mlr_report(y, [ones(30,1), x1, x2, x3], 0.05); disp(tbl);。输出表里每一行是一个回归系数,p 值小于 0.01 显示双星,小于 0.05 显示单星,小于 0.1 显示点,没有星号说明不显著。接下来可以把这张表用 writetable 导出到 Excel 或 CSV,方便写论文或做汇报:writetable(tbl, 'regression_result.xlsx', 'WriteRowNames', true)。需要注意,X'*X在变量量纲差距极大时可能产生较大数值误差,如果发现标准误异常,可以把 inv 换成 pinv 并观察结果是否稳定。对于样本量小于 30 的小样本数据,建议把 alpha 提高到 0.1,或者用 fitlm 里的robust选项配合稳健标准误重复一遍;如果两种方法给出的显著性标记一致,这个模型才值得写进结论里。

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

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

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

立即咨询