MATLAB相关性分析实战:从相关系数选择到偏相关检验
简介围绕MATLAB开展相关性分析的数学建模资料包将统计方法与编程实现相结合面向数学建模竞赛选手与数据分析初学者。内容聚焦皮尔逊相关系数与斯皮尔曼秩相关系数前者刻画线性关系强度后者基于秩次衡量单调关联可适应非正态或存在离群值的数据场景并提供了相关系数临界值查阅说明。压缩包内含11个文件以m脚本、xls/xlsx体测数据、mat数据文件为主另附docx临界值说明与jpg热力图结果整体大小仅194KB下载与部署都很轻量。包内以八年级男女生体测数据为案例覆盖数据导入与预处理、相关系数计算、显著性判断、热力图可视化等完整流程配套代码和说明文档模块化清晰便于边看边练并迁移到自己的变量关系分析任务中。已有2408人浏览学习适合课程设计、数学建模备赛或科研探索初期参考。1. 相关性分析不是算个相关系数那么简单两个变量的皮尔逊相关系数算出来0.85建模群里一片欢呼结果把时间趋势去掉之后只剩0.2——这场景在数学建模里太常见了。相关性分析看起来就是一行 corrcoef 的事但指标选错、数据没洗干净、显著性没检验结果就是给评委递刀子。这篇内容从三种相关系数的适用边界讲起一直落到 MATLAB 代码实现、可视化出图、偏相关分析验证覆盖一次完整的相关性分析流程。适合正在做数学建模作业、准备建模比赛或者论文里需要量化变量关联度的从业者按章节往下走就能复现。2. 相关系数三件套皮尔逊、斯皮尔曼与肯德尔怎么选2.1 三种系数的数学定义与适用边界相关性分析的第一步不是写代码是选指标。MATLAB 里最常用的三个系数是皮尔逊Pearson、斯皮尔曼Spearman和肯德尔Kendall三者的数学假设完全不同选错了结果就没意义。皮尔逊相关系数衡量的是两个变量的线性相关程度公式是协方差除以两个标准差的乘积。它要求数据近似正态分布、变量之间是线性关系而且对异常值极其敏感——一个离群点就能把系数从 0.8 拉到 0.3。这是建模中最常用的系数但也是被误用最多的系数。斯皮尔曼系数是秩相关系数计算时先把数据排序转换成秩次再对秩次算皮尔逊相关。它不要求正态分布对异常值的容忍度高适合单调但非线性的关系。比如变量 A 增长时变量 B 以指数方式增长皮尔逊可能只算出 0.6斯皮尔曼能到 0.9。肯德尔系数也是基于秩的但它的思路是检查所有变量对的一致性——把每对观测值拿出来看排序方向是否一致一致的对多说明相关性高。它在小样本、存在大量并列秩tie的场景下表现更稳定但计算量偏大。选型的基本原则是数据近似正态且关系线性用皮尔逊数据偏态或关系单调非线性用斯皮尔曼样本量小或者数据里有大量重复值用肯德尔。实际做数学建模时我一般会三个系数都算一遍如果差异不大就以皮尔逊为主如果差异明显说明数据里存在非线性或异常值干扰这时候斯皮尔曼的结果更可信。2.2 MATLAB 实现corrcoef 与 corr 的完整调用MATLAB 里计算相关系数有两条路corrcoef 和 corr。corrcoef 在基础 MATLAB 里就有corr 属于 Statistics and Machine Learning Toolbox功能更全。建模场景下建议直接用 corr因为能同时算三种系数、拿到 p 值、指定备择假设。% 加载示例数据假设 data 是 n×m 矩阵每列是一个变量 % 生成模拟数据30个样本5个变量其中变量3和变量4存在较强相关 rng(42); n 30; data randn(n, 5); data(:, 3) data(:, 3) 0.8 * data(:, 4) 0.2 * randn(n, 1); % 计算三种相关系数矩阵 R_pearson corr(data, Type, Pearson); R_spearman corr(data, Type, Spearman); R_kendall corr(data, Type, Kendall); % 同时输出相关系数和p值 [R, P] corr(data, Type, Pearson); % 只关注某两列之间的相关系数和p值 [r12, p12] corr(data(:, 1), data(:, 2), Type, Pearson); disp(Pearson相关系数矩阵); disp(round(R, 3)); disp(p值矩阵); disp(round(P, 3));corr 函数的第一个参数可以是整个矩阵也可以是两个单独的列向量。矩阵输入时返回的是 m×m 的相关系数矩阵和对应的 p 值矩阵向量输入时返回的是标量相关系数和标量 p 值。Type 参数用来指定系数类型支持 Pearson、Spearman、Kendall 三种取值。需要注意 corr 默认的 p 值检验是「相关系数不等于 0」的双侧检验用的是 t 统计量近似。如果你只关心正相关或负相关要手动把 p 值除以 2 再做单侧判断——不过大多数建模场景里双侧检验就够用了不用纠结这个细节。2.3 从相关系数矩阵到显著性检验p 值的正确解读相关系数矩阵出来了下一步是看显著性。很多人只看系数绝对值大小0.8 就觉得稳了实际上样本量只有 15 的时候0.8 的系数完全可能不显著。反过来样本量上了 5000.15 的系数也可能显著——所以显著性检验是判断关系是否真实存在的关键环节。MATLAB 里 p 值的判断很简单p 小于 0.05 说明在 95% 置信水平下相关系数显著不为零p 小于 0.01 说明在 99% 水平下显著。但这里有个容易踩的坑多重比较问题。如果你的数据有 10 个变量相关系数矩阵里有 45 个不重复的系数就算所有变量之间完全无关纯靠随机也可能有 2~3 个 p 值小于 0.05。% 多重比较校正用Bonferroni方法调整p值 % 假设R和P来自前面的corr计算m是变量个数 m size(data, 2); % 提取上三角矩阵的非重复p值 p_values P(triu(true(m), 1)); % Bonferroni校正p值乘以比较次数 p_adjusted p_values * (m * (m - 1) / 2); p_adjusted min(p_adjusted, 1); % 校正后p值不能超过1 % 找出校正后仍然显著的变量对 [var1, var2] find(triu(P, 1) 0.05 triu(R, 1) 0.5); for k 1:length(var1) fprintf(变量%d与变量%dr%.3fp%.4f\n, var1(k), var2(k), R(var1(k), var2(k)), P(var1(k), var2(k))); endBonferroni 是最保守的多重比较校正方法做法是把 p 值乘以比较次数。45 次比较意味着原始 p 值要小于 0.0011 才算显著条件确实苛刻但如果你的论文或报告里要宣称发现了某些强相关关系这个保守操作反而能帮你挡住质疑。实际操作里我一般分两步走先不管校正把显著且系数绝对值大于 0.5 的变量对列出来再做一次 Bonferroni 校正看哪些还稳。如果校正后依然显著那这组关系可以放心写进报告如果校正后消失了就在正文里标注「校正前显著校正后边缘显著」至少态度是诚实的。3. 数据预处理相关性分析结果可信的前提3.1 缺失值与异常值的处理顺序直接拿原始数据算相关系数是新手最容易犯的错误。缺失值、异常值、趋势项、量纲差异每一项都会扭曲相关系数的计算结果而且扭曲方向不可控——可能放大也可能缩小完全看数据里的噪声分布。处理顺序上先处理缺失值再处理异常值最后考虑去趋势和标准化。顺序不能反因为异常值识别算法比如三倍标准差法本身对缺失值敏感缺失太多会把正常值误判成异常值。% 步骤一检查缺失值分布 missing_count sum(ismissing(data)); fprintf(每列缺失值数量\n); disp(missing_count); % 步骤二缺失值处理——少量缺失用行删除大量缺失用插值 if max(missing_count) 5 data_clean rmmissing(data); % 删除包含缺失值的行 else % 线性插值补全对每列单独处理 data_clean data; for col 1:size(data, 2) col_data data(:, col); nan_idx isnan(col_data); if sum(nan_idx) 0 x 1:length(col_data); data_clean(nan_idx, col) interp1(x(~nan_idx), col_data(~nan_idx), x(nan_idx), linear); end end end % 步骤三异常值检测——用三倍标准差法 data_final data_clean; for col 1:size(data_clean, 2) col_mean mean(data_clean(:, col), omitnan); col_std std(data_clean(:, col), omitnan); outlier_idx abs(data_clean(:, col) - col_mean) 3 * col_std; fprintf(变量%d异常值数量%d\n, col, sum(outlier_idx)); % 异常值替换为NaN然后再插值 data_final(outlier_idx, col) NaN; end data_final fillmissing(data_final, linear);rmmissing 删除包含缺失值的整行数据量充足时最简单可靠。如果每列缺失少于 5%行删除不会损失太多样本但如果缺失比例高还是用 interp1 做线性插值。三倍标准差法适合近似正态分布的数据偏态严重时用四分位距法更稳——但建模比赛里三倍标准差已经够用不必过度复杂。异常值检测完不要直接删替换成 NaN 再插值的好处是保留样本量。如果直接删除异常值所在的行样本量会进一步缩小影响后续的显著性检验效力。3.2 数据标准化与去趋势什么时候必须做标准化和去趋势是两个容易被忽视的预处理步骤。标准化解决量纲问题——一个变量范围是 0~1另一个是 0~10000算相关系数本身不受影响因为相关系数已经是无量纲的但如果是算协方差矩阵或者做主成分分析标准化就变得重要。相关性分析场景下标准化是可选操作去趋势则是特定条件下必须做。去趋势针对的是时间序列数据。如果两个变量都随时间同步增长比如年份和某指标算出来的相关系数虚高——它们不是真正相关只是共享同一个时间趋势。这时候必须把趋势去掉再算相关性。% 对时间序列数据做去趋势处理 % 假设 data_ts 是 n×m 的时间序列数据每行是一个时间点 n size(data_ts, 1); t (1:n); % 方法一线性去趋势——对每列拟合一次线性回归取残差 data_detrended zeros(size(data_ts)); for col 1:size(data_ts, 2) p polyfit(t, data_ts(:, col), 1); trend polyval(p, t); data_detrended(:, col) data_ts(:, col) - trend; end % 方法二直接用detrend函数 data_detrended2 detrend(data_ts, 1); % 第二个参数1表示线性趋势 % 对比去趋势前后的相关系数 R_before corr(data_ts, Type, Pearson); R_after corr(data_detrended, Type, Pearson); disp(去趋势前相关系数矩阵); disp(round(R_before, 3)); disp(去趋势后相关系数矩阵); disp(round(R_after, 3));detrend 的第二个参数传 1 做线性去趋势传 0 则只去均值。如果数据有季节性波动可以用 detrend(data, linear) 配合季节差分不过数学建模场景下线性去趋势已经能处理大部分虚高相关的问题。做完去趋势后对比前后相关系数矩阵的变化——如果某个系数从 0.8 掉到 0.3说明原来的相关主要来自共同趋势不是变量间的真实联系。3.3 多重共线性初筛相关性矩阵的边界用途相关性矩阵还有一个用途是初筛多重共线性。在回归建模中如果两个自变量高度相关|r| 0.8同时放进模型会导致系数估计不稳定、标准误膨胀。相关性矩阵能帮你快速发现这些潜在问题。但相关性矩阵只能发现两两之间的共线性三个变量之间的多重共线性比如 X3 X1 X2在相关性矩阵里可能完全不明显因为两两之间的相关系数都不高。这种情况下要用方差膨胀因子VIF来判断MATLAB 里可以自己算。% 计算方差膨胀因子VIF % 假设 X 是 n×k 的自变量矩阵每列一个自变量 X data(:, 1:4); k size(X, 2); VIF zeros(k, 1); for i 1:k y_i X(:, i); % 当前变量作为因变量 X_rest X; X_rest(:, i) []; % 其余变量作为自变量 % 线性回归 b X_rest \ y_i; y_hat X_rest * b; resid y_i - y_hat; R2_i 1 - sum(resid.^2) / sum((y_i - mean(y_i)).^2); VIF(i) 1 / (1 - R2_i); end disp(各变量的VIF值); disp(round(VIF, 2)); % VIF大于10说明存在严重多重共线性VIF 的计算逻辑是把第 i 个自变量当作因变量用其余自变量做回归看 R² 多大。R² 越接近 1说明这个变量能被其他变量完全解释VIF 越大。经验法则是 VIF 大于 10 需要处理大于 5 就要警惕。如果发现 VIF 超标的变量对优先考虑删掉其中一个或者用主成分分析做降维。4. 避坑相关性分析的五个翻车现场4.1 皮尔逊系数被异常值带偏现象算出来的皮尔逊相关系数高达 0.85画散点图发现只有一个点死死拉住了斜线去掉这个点后相关系数掉到 0.2。原因皮尔逊系数对异常值极度敏感。一个离群点会同时放大协方差和标准差而且通常协方差被影响的程度更大导致系数虚高或虚低。散布图上拉高斜率的单点就是典型情况。解决先做异常值检测三倍标准差法或四分位距法处理完异常值后重算相关系数。同时对比皮尔逊和斯皮尔曼的结果——如果差异大于 0.3说明异常值在起作用。后续报告里可以把两个系数都写出来用斯皮尔曼作为稳健性验证。4.2 样本量太小导致 p 值不可信现象样本量只有 12算出来 r 0.75p 0.02看着挺美好但换一组样本结果完全不一样。原因小样本下相关系数的抽样波动极大。n 12 时置信区间宽度接近 0.5而且 p 值对单个样本点非常敏感删掉一个点就可能从显著变成不显著。解决样本量小于 30 时用肯德尔系数替代皮尔逊或斯皮尔曼肯德尔在小样本下的稳定性更好。更有说服力的做法是画 Bootstrap 置信区间——对样本做有放回抽样 1000 次每次重算相关系数观察 2.5% 到 97.5% 分位数区间。如果区间跨过 0关系不可靠。4.3 非线性关系被线性系数完美漏掉现象两个变量呈现明显的 U 型关系画出散点图很漂亮但皮尔逊系数算出来接近 0报告里写「变量间无显著相关」。原因皮尔逊系数只衡量线性相关对非线性关系完全失效。U 型关系是典型例子——左半段负相关、右半段正相关叠加之后相互抵消。解决算系数之前先画散点图。如果散点图呈现明显的曲线形态考虑对变量做变换对数、平方、倒数后再算线性相关或者直接改用斯皮尔曼系数。斯皮尔曼对单调关系有效但 U 型这种非单调关系依然测不出来——这时需要用距离相关性或互信息这类更通用的指标。4.4 把相关性当成因果性现象分析发现某地区的冰淇淋销量和溺水人数高度相关r 0.9于是得出结论「吃冰淇淋导致溺水」。原因这是经典的第三个变量问题——天气炎热同时推高了冰淇淋销量和下水游泳的人数。相关性只说明两个变量之间有联动不说明谁导致谁甚至可能没有直接关系。解决报告里严格区分「相关」和「因果」。如果需要在建模中验证因果关系用偏相关分析控制潜在的混淆变量或者使用 Granger 因果检验时间序列场景。但大多数相关性分析场景下写到「存在显著正相关」就足够了不要往因果方向过度发挥。4.5 多重比较不校正假阳性满天飞现象10 个变量的相关性矩阵里有 45 个系数其中 4 个显著感觉发现了「多个变量之间的关联网络」但校正之后全部不显著。原因显著水平 0.05 意味着纯随机情况下每 20 个检验就有 1 个假阳性。45 个检验的期望假阳性数是 2.25 个——你看到的 4 个「显著」可能有一半是噪声。解决做完整相关矩阵分析时先做 Bonferroni 校正再报告结果。或者换一种思路不用相关性矩阵做全局扫描而是先根据理论或建模需求指定 3~5 对关键变量只检验这几对把多重比较次数降下来。少即是多预先指定的假设检验比事后扫描更站得住脚。5. 相关性可视化从散点图到矩阵热力图5.1 散点图矩阵plotmatrix 与自定义组合相关性分析不能只给数字数学建模比赛和论文评审都要求可视化。散点图矩阵是展示多变量两两关系最直接的方式——对角线上是每个变量的分布非对角线是两两散点图。% 使用plotmatrix绘制散点图矩阵 % data_clean是预处理后的数据变量数量不要超过6个 variable_names {温度, 湿度, 风速, PM2.5, 气压}; figure(Position, [100, 100, 800, 800]); [R_scatter, ax_list] plotmatrix(data_clean); % 添加变量名标注 for i 1:size(data_clean, 2) xlabel(ax_list(end, i), variable_names{i}); ylabel(ax_list(i, 1), variable_names{i}); end % 在对角线上方标注相关系数 for i 1:size(data_clean, 2) for j (i1):size(data_clean, 2) r_val corr(data_clean(:, i), data_clean(:, j)); text(ax_list(i, j).XLim(2)*0.7, ax_list(i, j).YLim(2)*0.9, ... sprintf(r%.2f, r_val), FontSize, 8, Color, red); end endplotmatrix 返回的 ax_list 是子图的句柄数组可以逐个访问调整坐标轴标注。变量名要提前放在 cell 数组里以免图上只显示「V1、V2」这种没有信息量的标签。在散点图右上角标注相关系数是建模报告的常见做法比单独给个系数表更直观。5.2 热力图与聚类heatmap 与矩阵重排散点图矩阵适合变量少的情况变量超过 8 个时图会变得拥挤不清。这时候改用热力图更合适——颜色深浅直接传递相关性强弱配合聚类重排能发现变量之间的分群结构。% 绘制相关系数热力图 R_final corr(data_final, Type, Pearson); figure(Position, [100, 100, 700, 600]); % 使用heatmap函数MATLAB R2017a及以上版本支持 h heatmap(variable_names, variable_names, R_final, ... Colormap, parula, ... ColorLimits, [-1, 1], ... CellLabelFormat, %.2f); h.Title 变量相关系数矩阵热力图; h.FontSize 12; % 对相关系数矩阵做层次聚类并重排 % 将相关系数转成距离度量距离 1 - |r| dist_matrix 1 - abs(R_final); % 层次聚类 tree linkage(squareform(dist_matrix), average); leaf_order optimalleaforder(tree, squareform(dist_matrix)); % 按聚类结果重新排列变量顺序 R_reordered R_final(leaf_order, leaf_order); variable_names_reordered variable_names(leaf_order);热力图里把 colorLimits 固定到 [-1, 1] 很重要否则 MATLAB 会根据数据范围自动缩放色标导致 0.3 和 0.9 显示为差不多的颜色误导观察。聚类重排的意义在于把相关性高的变量排在相邻位置让热力图呈现出明显的块状结构——一眼就能看出哪些变量聚成一群。5.3 把可视化结果导出为报告素材建模报告和论文对图片分辨率有要求直接在 MATLAB 里截图保存的 PNG 往往不够清晰。推荐用 exportgraphics 函数导出 300dpi 以上的图片而且支持白底背景适合直接插入文档。% 导出高清图片 % 确保当前figure是激活的 exportgraphics(gcf, correlation_heatmap.png, Resolution, 300); % 如果需要在论文中自适应大小可以导出为矢量图 exportgraphics(gcf, correlation_heatmap.pdf, ContentType, vector);exportgraphics 是处理导出最好的选择300dpi 足够印刷要求。矢量图 PDF 格式适合插入论文——放大缩小不模糊。注意 exportgraphics 导出的是当前 figure 的完整内容导出前先用 axis 调整好坐标轴范围不要出现空白边距过大的问题。另外给图片命名时直接用英文和下划线中文文件名在部分 LaTeX 编译环境下会出问题。6. 进阶偏相关分析——剥离混淆变量后的真实相关6.1 偏相关分析的基本思想与适用场景两变量之间显示出的相关性往往受到第三个变量的间接影响。举个例子变量 A 与变量 B 的相关系数是 0.7看起来很强但如果变量 C 同时驱动 A 和 B那么这个 0.7 可能是「假的」。偏相关分析做的事情就是在控制变量 C 的影响后重新计算 A 与 B 的真实相关性。它通常算的是剔除第三个变量影响后的残差之间的皮尔逊相关。这才是写进报告里最站得住的数字。6.2 MATLAB 手写偏相关系数残差化方法MATLAB 的统计工具箱里其实有 partialcorr 函数但很多建模场景下拿不到工具箱授权。我自己更习惯用残差化思路手写偏相关——把 A 对 C、B 对 C 分别做线性回归取残差再求残差之间的皮尔逊相关。两者结果一致而且代码完全透明评审追问细节时不心虚。% 手写偏相关系数控制Z后计算X与Y的相关 % X, Y, Z均为列向量长度相同 function [r_partial, p_partial] partial_corr_manual(X, Y, Z) % 步骤1对X和Y分别关于Z做线性回归取残差 n length(X); Z_design [ones(n, 1), Z]; % 加一列截距项 % 回归X~Z beta_XZ Z_design \ X; resid_X X - Z_design * beta_XZ; % 回归Y~Z beta_YZ Z_design \ Y; resid_Y Y - Z_design * beta_YZ; % 步骤2计算残差的皮尔逊相关系数 [r_partial, p_partial] corr(resid_X, resid_Y); end % 示例控制温度和湿度后分析风速与PM2.5的相关 % 假设data_final列为[温度, 湿度, 风速, PM2.5] X data_final(:, 3); % 风速 Y data_final(:, 4); % PM2.5 Z [data_final(:, 1), data_final(:, 2)]; % 控制温度和湿度 [r_raw, p_raw] corr(X, Y); % 原始相关 [r_part, p_part] partial_corr_manual(X, Y, Z); % 偏相关 fprintf(原始相关系数%.3fp%.3f\n, r_raw, p_raw); fprintf(偏相关系数%.3fp%.3f\n, r_part, p_part); if abs(r_part) abs(r_raw) fprintf(控制Z后相关性下降说明Z存在混淆效应\n); end偏相关分析的结论判断是如果偏相关系数明显小于原始相关系数说明原始相关部分是由控制变量驱动的。比如原始 r 0.7、偏相关 r 0.2那 X 和 Y 的「真正关系」其实很弱。如果偏相关依然显著且数值变化不大X 和 Y 的关系是独立存在的。这个思路在建模报告的「稳健性检验」部分非常好用。现在距离完成内容只剩一步——如果正文组织唯恐落后于实际把偏相关代码段拿去做实际数据验证。我一般习惯的做法是把偏相关分析写成函数存成独立的 .m 文件每次拿到新数据直接调用省去反复复制粘贴。用偏相关分析校验过每一条核心相关性关系之后报告里写结论能踏实很多——至少不用怕评委问一句「那第三个变量呢」一句话就能答上来方案有据、系数能复现、有解释力希望帮到你。本文还有配套的精品资源点击获取