☰
MATLAB统计绘图全流程实战:从数据清洗到图表美化与排错
2026/10/13 1:12:41 网站建设 项目流程

MATLAB做数据统计时,很多人第一步就卡在"图怎么画才好看"上。明明算了一堆均值、方差、相关系数,结果出图又慢又丑,审稿人或者老板看一眼就皱眉头。其实不是你的数据有问题,而是你还没把MATLAB的统计图表工具箱用顺手。这篇内容我不打算罗列函数手册,而是从实际干活的角度,把一套完整的统计绘图流程拆开讲清楚——从数据清洗到图表美化,再到踩坑排查,全是我自己跑过、改过、重写过之后留下的经验。

1. 流程概览:统计图表到底该怎么一步步做

1.1 为什么是MATLAB而不是其他工具

我在过去几年的数据处理工作中,见过不少同事在Excel、Python和MATLAB之间反复横跳。说实话,每个工具都有它的位置,但MATLAB在统计图表方面有一个很独特的优势:交互式调整和脚本化相结合的效率。你在命令行里敲几行代码,立刻出图,不满意就在代码里改个参数再重画,整个过程非常顺滑。Python的matplotlib虽然也能做到,但默认样式和交互响应速度我个人还是觉得MATLAB更顺手,尤其是对于已经熟悉MATLAB矩阵操作的人来说,写起来几乎不用切换思维。

另一个容易被忽略的点是,MATLAB的统计绘图函数往往自带计算结果。比如histogram函数不仅画直方图,还能顺便输出每个bin的计数和中心位置;boxchart会直接给你中位数、四分位数的数值句柄。这意味着你可以少写很多计算代码,不容易出错。

1.2 常用统计图表类型和选型思路

做统计图表之前,先想清楚你手里数据是什么类型、想传达什么结论,再决定画什么图。这是我的一个习惯,能防止画了半天才发现图不对题。

数据类型目标推荐图表
单变量连续数据观察分布形态直方图、经验累积分布图
单变量连续数据识别离群值箱线图、小提琴图
两个连续变量观察相关性和趋势散点图、回归拟合图
两个以上变量对比组间差异箱线图、误差棒图
分类数据展示频数或占比柱状图、饼图
矩阵或表格看整体模式热力图

选图的时候,先用一句话回答你老板可能问的问题。比如"这批样本的分布是不是正态的?"就适合直方图加正态拟合线;"两个批次的产品有没有差异?"就适合箱线图或者误差棒图。别想着一个图解决所有问题,那最后往往什么问题都没解决。

2. 数据准备与清洗:不稳定出图的大半根源

2.1 从外部文件读取数据的四个实用方法

统计图表画得不好看,有时候根源不在绘图代码,而是数据读进来就已经是脏的。我见过太多人直接readtable后不管不顾就开始画,结果图里出现一个巨大的异常尖峰,白白浪费一晚上排查。先花几分钟读数据,能省下后面的大把时间。

  • readtable函数:适合读CSV、Excel、txt混合类型数据。读进来自动就是table格式,列名保留原样,非常好用。用的时候注意参数'NumHeaderLines',有时候表格文件前面有说明行,不指定的话第一列就会错位。
  • xlsread函数:老版本和新版本都能用,读取Excel特定sheet用xlsread('文件.xlsx', 'Sheet1')。不过新版但凡能用readtable就优先用readtable,速度和兼容性都更好。
  • fopen+textscan组合:适合读格式复杂、纯数值的文本文件。textscan可以对每一列单独设定格式类型,处理不规则空白特别稳定,但对操作熟练度要求高。
  • importdata函数:快速和简单,但遇到混合格式容易自动推断错误,建议只作为预览工具来用。

注意:如果你的数据文件包含中文表头,建议统一预处理成英文列名再读入。MATLAB对中文变量名的支持虽然有所改善,但后续写脚本时频繁切换输入法很麻烦,还容易踩编码的坑。

2.2 缺失值和异常值的处理策略

读进来之后,第一件事是检查数据是否干净。我习惯用summary(table)看一眼每列的最小值、最大值、是否有NaN。如果发现NaN,有几个选项:

  • 直接删除整行:rmmissing函数。如果你的样本量足够大,剔掉几条也不影响结论,这是最省事的办法。
  • 插值填充:fillmissing函数,可以选'linear'、'spline'、'previous'几种方式。我个人的建议是,时间序列数据慎用spline,虽然曲线光滑,但可能把局部的真实波动抹掉。某次我在处理传感器时间序列时,用了'nearest'填充,结果后面画出来的图在填充点附近有一小段平台,后来换成'linear'才自然。
  • 按分组填充:如果你有分组数据,最好按组填充,方法是splitapply加fillmissing的组合。全表统一填充一个均值,会把组间差异扭曲掉,这个细节经常被忽视。

异常值的识别,我通常先用箱线图的逻辑:计算Q1和Q3,超过Q3+1.5*(Q3-Q1)的点视为离群值。如果你不确定这些点是不是错误数据,可以先看它们的原始记录,而不是直接删。有些异常值反而包含了非常重要的业务信号,比如设备故障前的脉冲信号。

3. 绘制常用统计图表的实操详解

3.1 直方图与正态拟合:分布可视化最常用的一招

直方图是统计作图里最简单也最容易被低估的图。很多人直接histogram(x)完事,其实这个函数有很多值得调的地方。核心参数是BinWidth或NumBins,这两个控制柱子的宽度和数量。我见过默认直方图出来非常"锯齿",完全看不出分布形状,原因就是bin数量不合适。

% 生成一份示例数据 rng(42); % 固定随机种子,确保结果可复现 x = randn(1000, 1) * 3 + 10; % 绘制直方图 figure; h = histogram(x, 'BinWidth', 1, 'FaceColor', [0.2 0.4 0.8], 'EdgeColor', 'white'); xlabel('测量值'); ylabel('频数'); title('直方图示例:均值10,标准差3的正态样本'); grid on;

这里用BinWidth=1而不是默认的自动分箱,是我从实践中总结出来的一个习惯:分箱宽度和坐标轴的实际意义对齐。比如数据如果指的是长度(毫米),那每1毫米一个bin,看图的人直观就能想到这个数据分辨率是毫米级别的。如果你不知道选多少,可以先画默认图,然后手动调节NumBins,跟数据集大小有一个经验公式:NumBins约等于sqrt(n),n是样本量。这个公式不绝对,但作为起点很靠谱。

很多情况下,你还想叠加一条正态分布拟合曲线上去。MATLAB可以用fitdist先拟合,再在同一个坐标系里画曲线:

% 拟合正态分布参数 pd = fitdist(x, 'Normal'); xs = linspace(min(x), max(x), 200); ys = pdf(pd, xs); % 叠加曲线(注意这里需要对直方图做归一化或使用右轴) hold on; plot(xs, ys * length(x) * h.BinWidth, 'r-', 'LineWidth', 1.5); hold off;

这里关键的一步是:你画出的pdf曲线必须乘以样本数再乘以bin宽度,才能和频数直方图放在同一个尺度下。我第一次画拟合曲线时忘了乘这个系数,曲线低得几乎贴在地面上,白查了好一阵子。记住这行代码:ys * length(x) * h.BinWidth。

注意:如果你的数据明显不是正态分布(比如偏态明显或者有重尾),强行叠加正态拟合曲线会起到误导作用。这时候可以改用核密度估计的拟合线,MATLAB里的ksdensity函数就是干这个的。画出来的曲线虽然不是参数分布,但更贴合数据的实际形态。

3.2 箱线图与分组对比:中位数差异一眼看清

箱线图是一个兼具简洁和信息密度的统计图表。它一次性展示了中位数、四分位距和离群值,非常适合做组间对比。MATLAB新版本推荐用boxchart,老版本的boxplot虽然功能更多,但使用起来比较繁琐。

% 构造两组数据 group1 = randn(200, 1) + 1; group2 = randn(200, 1) * 1.5 - 1; group = [ones(size(group1)); 2*ones(size(group2))]; % 绘制箱线图 figure; boxchart(group, [group1; group2], 'GroupOrder', [1 2]); xticklabels({'实验组A', '对照组B'}); ylabel('指标值'); title('两组样本的箱线图对比'); % 可选:叠加抖动散点,观察实际数据分布 hold on; x_jitter = group + (rand(size(group)) - 0.5) * 0.2; scatter(x_jitter, [group1; group2], 20, [0.6 0.6 0.6], 'filled', 'MarkerFaceAlpha', 0.4); hold off;

箱线图加抖动散点是我个人非常推荐的做法,特别是样本量不太大的时候。箱线图本身会隐藏样本量信息,两组各10个样本画出来的箱线图和各1000个样本画出来的几乎一模一样,这会让读者误判结论的可靠程度。叠加半透明散点之后,图的真实性大大提升。注意散点透明度的控制,MarkerFaceAlpha设成0.3到0.5之间比较合适,太透明了等于没画,太实心了又会把箱线挡住。

3.3 散点图与相关系数标注:发现线性关系最快的方式

散点图是做相关性分析时的标配。但很少有人知道,MATLAB里自带的plotmatrix比单独画scatter更适合做多变量相关性初探。

% 构造多变量模拟数据 rng(8); data = randn(200, 4); data(:, 2) = data(:, 1) * 0.7 + randn(200, 1) * 0.5; data(:, 4) = data(:, 3) * (-0.5) + randn(200, 1) * 0.8; figure; [~, ax, bigax, ~, ~] = plotmatrix(data, 'b.'); title(ax(1,1), '变量1'); title(ax(1,2), '变量2'); title(ax(1,3), '变量3'); title(ax(1,4), '变量4'); % 修改主坐标轴标签 xlabel(bigax, '变量'); ylabel(bigax, '变量');

plotmatrix输出的矩阵图可以把多个变量之间的两两关系全部放在一张图内,对角线上是每个变量的直方图,左下角和右上角是两两散点图矩阵。这个小工具在数据探索阶段特别有用,几秒钟就能看出哪些变量之间存在线性趋势、哪些存在非线性甚至完全无关。

画完散点图之后,如果确实想标注相关系数r,可以用corrcoef函数计算,然后在图上加文字。

r_mat = corrcoef(data(:,1), data(:,2)); r_value = r_mat(1,2); annotation('textbox', [0.55 0.75 0.2 0.1], 'String', sprintf('r = %.3f', r_value), ... 'EdgeColor', 'none', 'FontSize', 12, 'FontWeight', 'bold');

我的习惯是,相关系数标注要放在图的空白处而不是数据点密集的地方,否则会干扰阅读。你可以先画图,观察哪儿空白多,再用annotation调整相对坐标。annotation用的是归一化坐标,取值范围是0到1,第一次用的人经常找不着文字去哪儿了,多试几次就习惯了。

3.4 概率图与正态性检验:验证分布假设的必备组合

在做许多参数统计分析之前(比如t检验、方差分析),正态性假设是一个前提。如果你需要向别人证明你的数据符合正态分布,除了画直方图之外,概率图是更专业的工具。概率图的原理是:如果数据来自正态分布,那么它的累积概率和理论正态分布累积概率之间会形成一条直线。

figure; probplot('norm', x); xlabel('数据值'); ylabel('概率'); title('正态概率图');

概率图上的点越接近红色参考线,说明数据越符合正态分布。尾部如果偏离得厉害,则可能是有离群值或者重尾分布。我见过不少人看了概率图后问"这算正常吗",我的回答是:看线的偏差程度和位置。如果只是中间部分轻微波动,那是正常噪声;如果两端翘起呈S形,那恐怕不是正态。

配合概率图,还可以用数值方法做确认。MATLAB的jbtest是Jarque-Bera检验,适用于大样本;也可以试试lillietest函数,它基于Lilliefors修正的Kolmogorov-Smirnov检验,适用范围更广,对小样本也能工作。

h = lillietest(x); if h == 0 disp('在95%置信水平下,数据无法拒绝正态分布假设。'); else disp('数据显著偏离正态分布。'); end

3.5 经验累积分布函数图:不从众的选择

直方图的缺点是受分箱方式影响太大,换个bin宽度形状就变了。如果你希望呈现数据分布的全貌,同时又想让不同数据集之间的对比更稳健,经验累积分布函数图是个很好的选择。它的曲线是单调递增的,到1封顶,不受分箱影响。

figure; ecdf(x); xlabel('测量值'); ylabel('累积概率'); title('经验累积分布函数图'); grid on;

在图上画两条参考线也很常见。比如想要观察第25百分位和第75百分位的值,可以用xline和yline:

p25 = quantile(x, 0.25); p75 = quantile(x, 0.75); xline(p25, '--r', 'P25', 'LabelOrientation', 'horizontal'); xline(p75, '--r', 'P75', 'LabelOrientation', 'horizontal');

经验累积分布图在报告里不太常见,所以用了反而让人觉得你专业。尤其在做两组数据对比时,一条曲线完全压在另一条右上方的,说明前者在各分位点上都更大,这个信息比单纯比较均值要有说服力得多。

3.6 误差棒图:展示均值和变异性的经典搭配

误差棒图是科研报告、实验数据分析里最常见的图之一。它展示的通常是均值加减某种误差度量(标准差、标准误或者置信区间)。用MATLAB画误差棒图有两种思路:一种是用errorbar函数直接画,另一种是用bar加errorbar组合。

% 构造三组数据的均值与标准差 means = [12.3, 15.8, 9.2]; stds = [1.5, 2.1, 1.2]; categories = {'方法一', '方法二', '方法三'}; figure; b = bar(means, 0.5, 'FaceColor', [0.6 0.6 0.8]); hold on; errorbar(1:3, means, stds, 'k', 'LineStyle', 'none', 'LineWidth', 1.2, 'CapSize', 10); hold off; xticks(1:3); xticklabels(categories); ylabel('样本均值'); title('不同方法的均值与标准差');

画误差棒有一个小坑:如果你用bar+errorbar组合,坐标轴范围会自动适配bar的高度,但不会自动给误差棒留空间。误差棒比柱子高出一大截被截断的情况我遇到过很多次。解决办法是在bar画完后,调用ylim手动扩展范围,比如ylim([0 max(means + stds) * 1.2])。类似这样的自动适配问题还有很多,养成画完图检查坐标轴边界的习惯,能省掉很多返工。

关于误差棒到底用标准差还是标准误,我的意见是:描述样本本身的离散程度用标准差,描述样本均值估计的精确程度用标准误。作图的时候就看你想要表达什么。如果没想明白,就默认用标准差,因为它更不容易引起过度解读。

3.7 热力图与相关矩阵可视化:多变量分析的好搭档

当你手里的变量超过5个,散点图矩阵就开始显得拥挤,这时候热力图就派上用场了。最常见的热力图用途是展示相关矩阵,把变量两两之间的相关系数用颜色深浅表示出来。

% 用前面构造的data矩阵计算相关系数 corr_matrix = corrcoef(data); % 变量名 var_names = {'变量1', '变量2', '变量3', '变量4'}; figure; h = heatmap(var_names, var_names, corr_matrix, 'Colormap', parula, ... 'CellLabelColor', 'k', 'FontSize', 12); h.Title = '变量相关系数热力图'; h.XLabel = '变量'; h.YLabel = '变量';

这里我用了parula作为色标。MATLAB默认的parula色图对色盲人群相对友好,蓝到黄的变化也比较细腻。如果你不喜欢parula,可以试试'turbo',虽然色彩丰富但饱和度高、容易喧宾夺主;也可以试试自定义的颜色映射,比如用bluewhitered这个用户函数,这个在MATLAB社区流传很广,把相关值-1到1映射到蓝色到红色,中间0为白色,非常直观。

热力图有一个好处是可以通过Colorbar的刻度直接读出数值,但我建议还是要显式标注每个格子里面的相关系数数字,CellLabelColor设置为黑色,否则色差大,看起来直观,但具体数值还是要凑近看色标转化,麻烦。

4. 图表美化与细节精调

4.1 Figure对象管理和常用属性设置

很多脚本运行完,生成的图窗口尺寸很小,字体糊成一团,直接截图根本没法用。这里有一个基础但重要的常识:画图前先设置figure的大小和分辨率。

% 设置画布大小 16x10 厘米,分辨率150dpi figure('Units', 'centimeters', 'Position', [2 2 16 10], 'PaperUnits', 'centimeters', ... 'PaperSize', [16 10], 'PaperPosition', [-0.5 -0.5 17 11], 'PaperPositionMode', 'auto');

这行很长的代码不必每次手敲,你可以封装成一个函数存到默认路径里,每次画图前调用。设置PaperSize和PaperPosition主要是为了后续导出图片时保证尺寸不变形。另一方面,如果你在写循环,记得每次循环把旧的figure关闭或者清空,否则窗口一多,内存占用会迅速上升,电脑直接卡死。

4.2 字体、图例、网格和坐标轴的精调规范

默认的MATLAB字体和坐标轴格式偏“学术生硬风”,如果你要拿到客户或者领导面前展示,我建议统一做一些调整。首先是字体,MATLAB默认的Helvetica在中文系统上会显得有些细,可以换成宋体或者微软雅黑,尤其是中文标题的情况下:

set(gca, 'FontName', 'Microsoft YaHei', 'FontSize', 11); title('统计量分布', 'FontSize', 14, 'FontWeight', 'bold', 'FontName', 'Microsoft YaHei');

图例的位置也是一个讲究。默认的'best'位置有时候会把图例放在数据最密集的地方,遮住关键信息。手动指定位置是稳妥的选择:'northwest'、'northeast'、'southoutside'等等。

网格线方面,我一般只在半透明子图或者精确读取数据的情况下才打开网格,而且用浅灰色([0.7 0.7 0.7])加虚线,不要用默认的实线黑色网格,那样会抢走数据的注意力。

grid on; set(gca, 'GridColor', [0.7 0.7 0.7], 'GridLineStyle', '--', 'GridAlpha', 0.6);

坐标轴刻度的格式也值得花点心思。如果你的数据跨度非常大,直接显示原始数字会让坐标轴标签挤成一团。可以改用对数坐标(set(gca, 'XScale', 'log')),或者自定义刻度标签。如果你要比较两条不同量纲的曲线,让它们共用一个y轴经常不够用,这时候用yyaxis left/right分列左右轴,但要注意标注清楚哪条线对应哪个轴。

yyaxis left; plot(x_data, y1, 'b-'); ylabel('左轴变量'); yyaxis right; plot(x_data, y2, 'r--'); ylabel('右轴变量');

4.3 多子图布局的排列与对齐

当你的统计报告需要展示多个图表时,subplot是最直接的布局工具。但默认的subplot间距很大,标签之间挤压得很难看。新版MATLAB推荐用tiledlayout,它在布局控制和图例统一方面比subplot好很多。

figure; tiledlayout(2, 2, 'TileSpacing', 'compact', 'Padding', 'compact'); % 第一个子图 nexttile; histogram(x, 'BinWidth', 1); title('直方图'); % 第二个子图 nexttile; boxchart(group, [group1; group2]); title('箱线图'); % 第三个子图 nexttile; ecdf(x); title('累积分布'); % 第四个子图 nexttile; scatter(data(:,1), data(:,2)); title('散点图');

tiledlayout的'TileSpacing'和'Padding'两个参数很关键。默认的'spacing'会让子图之间留下大片空白,哪怕放在文档里也显得很松散。设成'compact'之后整体紧凑非常多,适合论文或报告排版。

用tiledlayout还有一个很实用的特性:相同位置的子图可以共享同一个图例。如果你在多个子图中都用同一种颜色表示同一组数据,只需要在其中一个子图里添加图例,其他子图会自动共享,省掉了分别添加图例导致大小不一的麻烦。

5. 常见问题排查与经验速查

5.1 典型错误和解决方向速查表

我在实际使用和帮助别人的过程中,整理了一份高频问题清单,分享给大家参考:

症状可能原因解决方向
图形窗口不显示脚本运行完后窗口自动关闭在脚本末尾添加hold on或pause函数,或检查figure是否被覆盖
直方图出现巨大单柱尖峰数据里有无穷值或异常大值先用isinf和isoutlier清理数据再画图
箱线图各组颜色全部一样没有正确设置GroupOrder或Color参数指定GroupOrder并在boxchart中用'Colors'参数
中文乱码字体不支持中文或编码问题设置FontName为Microsoft YaHei或SimHei,检查文件编码UTF-8
曲线显示为蓝色一支多个plot命令之间没有hold on在第一次plot后添加hold on
图片导出不清晰分辨率设置太低使用exportgraphics并设置Resolution不低于300
图例显示不全图例数量多于实际曲线或数据句柄设置错误显式传入需要显示图例的句柄数组

5.2 出图速度慢或卡顿的处理经验

曾经有一次我在处理大约50万个数据点的散点图时,直接scatter画出来要等好几秒,而且拖动窗口时一卡一卡的。这提醒了我:MATLAB的绘图性能在处理大数据量时是有瓶颈的。优化的思路有几个:

  • 降低点的数量:用datasample随机抽一部分点画散点图,反正散点图看的是整体趋势,抽个1万到2万个点已经足够。
  • 使用histogram2代替大量重叠散点:二维直方图用颜色表示密度,在数据重叠严重的情况下比散点图信息表达更清晰。画法跟histogram类似,只不过输入两个向量:
figure; histogram2(x, y, 100, 'DisplayStyle', 'tile', 'FaceColor', 'flat'); colorbar;

这个图在大量数据点重叠的情况下,能清楚展示哪里密度高,哪里密度低,不会因为点太多糊成一团。100表示在两个维度方向上各分100个bin。如果你的数据点有50万个,这个方案几乎不卡顿。

  • 关闭图形界面,只保存图片:用set(gcf, 'Visible', 'off')或者figure('Visible', 'off')创建不可见图窗,画完之后直接saveas或exportgraphics,避免图形的实时渲染拖慢脚本。

5.3 导出图片时的清晰度与嵌入格式选择

图表最终要放进报告或者PPT里,导出这个环节最容易翻车。直接截图最不可靠,分辨率不稳定且背景不透明。我的做法是统一用exportgraphics导出,这个函数是从R2020a开始推荐的标准方式,用法很简洁:

exportgraphics(gcf, 'output.png', 'Resolution', 300);

如果目标载体是PDF论文,推荐导出为向量图格式,类似'svg'或者'pdf'。向量图的好处是无论放大多少倍都不会产生锯齿。PPT里需要的图,我一般导出png格式600dpi,足够清晰,文件体积也不会太大。注意不要把png换成jpg,尤其是带有网格线和文字的统计图,jpg压缩会带来严重的文字伪影。

5.4 脚本可复现性的一个建议:固定随机种子

统计绘图经常涉及模拟数据。为了让你的分析结论可以复现,脚本开头建议固定随机种子。我一般在脚本最前面写一行:

rng(2024);

这看起来是个小细节,但作用很大。有了固定种子,你每次运行脚本生成的随机数序列都一样,后面画出来的图、算出来的统计量就和之前完全相同。没有这一行的话,数据一变,图也变,你记录下来的结论可能就不能复现,这在正式的统计分析中是很致命的问题。

踩过几次随机性的坑之后,我现在养成了一个习惯:不论脚本是给谁看的,固定随机种子的代码一定放在最前面,注释里写明这是在模拟验证阶段固定的种子,如需不同随机数请注释掉这一行。这样既保证了复现性,也留了灵活性。

6. 一个完整实操示例:从数据清洗到成图全流程

前面讲了这么多,很多知识是孤立的,容易学了就忘。我在这里把整个流程串成一个完整的例子,你可以直接复制运行,改一改用自己的数据就能上手。

背景:某次实验中,记录了四组样本的响应值,数据分别存在一个CSV文件里。目标:对比四组的分布和均值差异,并输出一张包含直方图、箱线图和累积分布图的组合图。

% 固定随机种子,确保结果可复现 rng(2024); % 生成模拟数据并写入临时CSV文件(实际操作中直接读取你的文件) group_id = repelem(1:4, [120 150 100 180])'; values = [randn(120,1)+5; randn(150,1)*1.5+7; randn(100,1)*0.8+4; randn(180,1)*2+6]; data_table = table(group_id, values); writetable(data_table, 'simulated_data.csv'); % 从CSV读取数据 data = readtable('simulated_data.csv'); % 检查缺失值 if any(ismissing(data)) data = rmmissing(data); end % 按分组清理异常值(以3倍标准差为阈值,可根据需要修改) groups = unique(data.group_id); clean_values = []; clean_groups = []; for i = 1:length(groups) g = groups(i); subset = data.values(data.group_id == g); mu = mean(subset); sd = std(subset); keep = subset(abs(subset - mu) < 3 * sd); clean_values = [clean_values; keep]; clean_groups = [clean_groups; repmat(g, length(keep), 1)]; end % 生成组合图 figure('Units', 'centimeters', 'Position', [2 2 18 12]); % 第一幅:直方图叠加核密度 subplot(2, 2, 1); histogram(clean_values(clean_groups == 1), 'BinWidth', 1, 'FaceColor', [0.3 0.5 0.8]); hold on; [f, xi] = ksdensity(clean_values(clean_groups == 1)); plot(xi, f * length(clean_values(clean_groups == 1)), 'r-', 'LineWidth', 1.5); hold off; xlabel('响应值'); ylabel('频数'); title('第1组直方图与核密度'); grid on; set(gca, 'GridColor', [0.7 0.7 0.7], 'GridLineStyle', '--'); % 第二幅:箱线图 subplot(2, 2, 2); boxchart(clean_groups, clean_values, 'GroupOrder', [1 2 3 4]); xticklabels({'组1', '组2', '组3', '组4'}); ylabel('响应值'); title('四组样本箱线图'); hold on; for i = 1:4 x_jitter = i + (rand(size(clean_values(clean_groups == i))) - 0.5) * 0.2; scatter(x_jitter, clean_values(clean_groups == i), 10, [0.6 0.6 0.6], 'filled', 'MarkerFaceAlpha', 0.3); end hold off; % 第三幅:经验累积分布对比 subplot(2, 2, 3); hold on; for i = 1:4 ecdf(clean_values(clean_groups == i)); end hold off; xlabel('响应值'); ylabel('累积概率'); title('四组经验累积分布'); legend({'组1', '组2', '组3', '组4'}, 'Location', 'southeast'); % 第四幅:相关图(示例,用分组均值误差棒替代) group_means = grpstats(data, 'group_id', {'mean', 'std'}); subplot(2, 2, 4); errorbar(group_means{:, 1}, group_means{:, 'mean_values'}, group_means{:, 'std_values'}, ... 'o-', 'LineWidth', 1.5, 'MarkerSize', 8, 'CapSize', 12); xlabel('组别'); ylabel('均值±标准差'); title('组间均值与标准差'); xticks(1:4); xticklabels({'组1', '组2', '组3', '组4'}); grid on; set(gca, 'GridColor', [0.7 0.7 0.7], 'GridLineStyle', '--'); % 导出高质量图片 exportgraphics(gcf, 'statistical_summary.png', 'Resolution', 300);

这段脚本有几个细节值得留意:

  • 异常值清理采用3倍标准差准则,比1.5倍四分位距宽松一些。如果你的样本量少,3倍准则更不容易误删真实值。
  • ecdf函数在一个坐标区里多次调用时,可以用legend直接生成图例,不需要手动管理句柄,比较省事。
  • 整个脚本从生成模拟数据、清洗、绘图到导出一气呵成,这就是我推荐的工作方式:把流程固化在脚本里,以后换数据只需要改读取文件那两行代码。

关于这个脚本,我再分享几个自己在实际运行中遇到的问题。第一,grpstats返回的table变量名不一定就是'mean_values',如果你的表列名不是values,输出结果列名可能是'mean_values'或者类似的命名,需要根据实际输出调整。第二,第二幅图里的抖动散点循环,如果样本量特别大(比如几千个),画散点会稍慢,可以考虑减少散点数量或用半透明的二维直方图替代。第三,导出png时如果图窗不是当前活动窗口,exportgraphics(gcf)可能导出的不是你想要的图,所以要确保当前figure是你要导出的那一个,尤其是在脚本循环里跑多张图时,要格外注意这一点。

个人经验中,整个流程最不容易被注意到却又最影响出图效果的就是坐标轴留白和字体大小的一致性。你在子图布局下,不同子图的y轴范围差别很大,如果不手动统一字体大小和坐标轴刻度长度,最后拼接出来的整页图看起来就不协调。我的做法是在脚本最后统一运行一段set操作对每个子图进行字体统一设置,类似:

all_axes = findall(gcf, 'Type', 'axes'); set(all_axes, 'FontName', 'Microsoft YaHei', 'FontSize', 10);

findall配合set对整个figure里的所有坐标轴做统一设置,是省时省力的技巧,强烈推荐。

统计本身是个多步骤过程,绘图往往是最后一步,但也是给读者留下第一印象的关键一步。数据清洗和统计分析做得再好,如果图一塌糊涂,你的工作量就很难被看见。反过来,一张结构合理、标注清晰、美观度合格的统计图,本身就能替你说清楚大部分数据故事。我这里写的内容都是最常用、最常踩坑的部分,如果你后续有特定的需求,比如生存分析里的KM曲线、时间序列里的自相关图、贝叶斯后验分布可视化,再针对性地去查对应函数的细节就好。工具是死的,思路是活的。希望这这篇文章能帮你少走一些弯路。

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

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

立即咨询