ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

MATLAB统计图表绘制:核心技巧、参数调优与避坑指南

MATLAB统计图表绘制:核心技巧、参数调优与避坑指南

做数据分析的都知道,统计图表这块,MATLAB是绕不开的一把好手。我这些年处理传感器采样数据、实验记录和业务报表,几乎每个项目都要走一遍“数据导入—清洗—统计量计算—绘图—导出”的流程。用下来最大的感受是,MATLAB把统计计算和可视化放在同一个环境里,不用来回切工具,省掉的事情比想象中多。

这篇文章我把自己用MATLAB绘制统计图表的核心经验整理出来,包括不同图表的适用场景、数据预处理的关键细节、常用绘图参数怎么调,以及一些踩过之后才记得住的坑。适合正在学MATLAB的数据分析入门者,也适合需要在项目交付或论文里产出规范统计图表的工程师和研究人员。废话不多说,直接进入正题。

1. 为什么统计图表这条路我选MATLAB

1.1 统计函数和绘图功能一体化

很多人做统计图表喜欢用Excel,点几下就能出图,但数据量大一点、逻辑复杂一点就特别笨重。也有不少人用其他语言配合开源绘图库,功能确实强,但环境配置和语法学习成本摆在那里。MATLAB的优势在于,它把统计计算和可视化放在同一个平台里。你算均值、方差、偏度、峰度,用的是工作区同一份数据;画直方图、箱线图、散点图,不用把数据导来导去再接一遍流程。

举一个最简单的例子:算一组数据的描述性统计量,MATLAB里几行代码就能同时输出均值、中位数、标准差、四分位数,而且直接存放在工作区变量里,紧接着就能传给绘图函数。我处理某设备振动信号时,经常需要对比多个工况下的幅值分布。这种“边算边画”的交互体验,比纯脚本式的流程顺手太多。

1.2 交互探索加脚本固化,两不误

实际干活时,一开始根本不知道数据长什么样,上来就写一大段脚本只会反复报错。我常用的思路是:先用命令行和图形窗口做交互式探索。histogram、boxplot、scatter逐个试,看清数据分布形态之后,再把确定下来的参数固化到脚本里。这样既能快速试错,又能保证后续重复复现。

不管是给项目组做阶段汇报,还是给论文补图,同一套脚本跑出来的图一定是一致的。这也是MATLAB这类“交互加脚本”双模环境最大的价值。如果一个项目要产出一打图表,脚本化以后改一个数据源就能全部刷新,效率差距非常明显。

2. 什么样的数据画什么样的图

2.1 直方图和频数分布图最基础

直方图做的事,就是把连续数据切分成若干区间,然后统计每个区间里的数据个数。MATLAB里的histogram函数比老版本hist函数好用很多:自动选箱宽的效果更合理,返回的直方图对象还能直接改属性,改完图自动刷新。

以某设备振动信号的幅值为例,想看幅值集中在哪个范围,直接对数据画histogram。如果数据量在几千条,默认箱数基本靠谱;如果数据量到了几十万条,默认箱数画出来往往太粗糙。这时我用BinWidth或NumBins手动控制。我的经验是先分别试0.5和1.0两个箱宽,选那个既能看清主峰、又不会出现太多毛刺的版本。

另一个常用技巧是histfit,它会在直方图上叠加一条正态分布拟合曲线。注意,它不是对所有数据都适合。如果数据明显偏态,硬叠一条正态曲线会严重误导看图的人。我在报告里只在数据接近正态时才用histfit,否则就用纯histogram加统计量文字标注。

2.2 箱线图用于组间对比

当你需要对比多个类别或工况的数据分布时,箱线图比直方图直观得多。boxplot函数把数据按分组画成箱体:箱体上下边对应上下四分位数,中间线是中位数,须线延伸到非异常值范围,超出部分单独用点标出。

我第一次用boxplot时犯过错:直接传了一个矩阵进去,以为它会自动按列分组。MATLAB确实按列分组了,但横轴标的是Column 1、Column 2这种默认名字,拿给项目组看根本不知道对应哪个工况。后来我一直用分组向量加Labels参数的写法,把每组的条件名标清楚。这样图拿出去不用解释,别人一眼能看懂。

还有个细节:boxplot默认用四分位距的1.5倍做异常值判断,这个阈值对大多数场景够用。如果业务场景对异常值有特殊定义,可以通过Whisker参数调整,比如改成2.5或3,让判定范围更宽松。

2.3 散点图与相关性分析

数据量不大时,用scatter画散点图,可以直接看两个变量之间是线性关系还是非线性关系。但数据量一大,几万个点叠在一起,看到的只剩一团黑,什么规律都看不出来。这种时候我推荐两个替代方案:一是用scatter的密度着色,让点的颜色代表局部密度;二是用scatterhist,在散点图边上同时画两个变量的分布直方图,信息量比纯散点图大很多。

分析某跨平台系统的压测数据时,我拿请求延迟和并发数两列做散点图,默认样式点一多就完全没法看。后来换scatterhist,边上的直方图立刻暴露出延迟数据是长尾分布。这直接影响了统计指标的选择策略——均值在这种分布下容易被少数峰值拉高,得看中位数和95分位数。这种洞察,如果只盯着原始数据表格,很难凭肉眼发现。

2.4 概率图与正态性检验

判断数据是否符合正态分布,经常是选统计检验方法的前提。normplot是快速判断正态性的一个很好的工具:它把数据的经验分布和理论正态分位数画在同一张图上,如果数据近似正态,点基本落在一条直线上;偏离明显,则说明数据有非正态特征。

normplot只负责检查正态性,想对比数据是否服从威布尔分布或指数分布,可以用probplot,通过指定分布类型来画。另外cdfplot可以画经验累积分布函数,当你想知道“有百分之多少的数据低于某个阈值”时很直观。算P95、P99这类性能指标时,我几乎每次都顺手画一张cdfplot。

3. 实操全流程:从原始数据到一张能用的统计图

3.1 先处理数据,再谈画图

统计图表不好看,十有八九不是参数问题,而是数据本身没处理干净。数据里混着NaN、缺失值或者离群值,轻则让统计量算出奇怪结果,重则直接让绘图报错。我处理数据的第一步永远是检查质量。

假设数据存在CSV文件里,列分别是时间和测量值,我用readtable读进来,马上检查缺失情况:

data = readtable('measurements.csv'); disp(sum(ismissing(data))); % 看每列缺了多少

缺失值不多,我直接用rmmissing整行删掉;缺失集中在某些时段,就得考虑插值。但插值在统计图表里要慎重,插出来的数据不反映真实波动,汇报时标注清楚很重要。离群值方面,我一般先用summary或分位数看一眼范围,再决定是否用filloutliers处理,而不是无脑剔除。

3.2 描述性统计量先算一遍

绘图之前,我习惯把核心统计量算出来,心里有底,后面标注释、写结论都方便。至少包括均值、中位数、标准差、极值、四分位数;要做分布形态判断,再加偏度和峰度。

x = data.measure_value; mu = mean(x, 'omitnan'); med = median(x, 'omitnan'); sd = std(x, 'omitnan'); qr = quantile(x, [0.25 0.5 0.75]); sk = skewness(x); ku = kurtosis(x);

这里我一直带omitnan选项,就是防止某个缺失值让统计量变成NaN。这个习惯是踩过坑之后才养成的,早期数据预处理不严,项目里出现过统计结果全空的问题,排查半天才发现是数据里混了几个NaN。现在哪怕确定数据是干净的,我也会习惯性带上。

3.3 核心绘图代码与参数说明

画一张标准的直方图,我的模板如下:

figure('Position', [100 100 800 500]); histogram(x, 'BinWidth', 0.5, 'FaceAlpha', 0.7, 'EdgeColor', 'w'); xlabel('幅值'); ylabel('频数'); title('振动信号幅值分布'); grid on;

FaceAlpha控制填充透明度,EdgeColor设为白色,能让相邻柱子的界限清晰。这两个细节会让直方图看起来精致不少。如果长尾干扰了主体观察,再加一句xlim,把超出兴趣范围的部分切掉。

画箱线图时,我用分组向量而不是矩阵输入:

group_labels = [ones(n1,1); 2*ones(n2,1); 3*ones(n3,1)]; val_all = [group1; group2; group3]; boxplot(val_all, group_labels, 'Labels', {'工况A', '工况B', '工况C'}); ylabel('测量值');

分组向量的顺序必须和数据拼接的顺序一致,这是新手最常翻车的点。我习惯把val_all和group_labels写在一起,降低两者错位的概率。

散点图想处理大样本,可以用histcounts2加imagesc做二维密度图,数据再多也能看出分布层次。这属于进阶玩法,新手还是先用scatter和scatterhist把基本场景覆盖住。

3.4 图的美化和导出

统计图不是画完就结束。导出前我会统一检查字体、坐标轴范围、网格线是否协调。MATLAB默认图形的审美风格偏工程风,我一般会手动调一下:

set(gca, 'FontName', 'Helvetica', 'FontSize', 12); ax = gca; ax.XGrid = 'off'; ax.YGrid = 'on';

导出则分场景:论文插图用exportgraphics输出向量格式PDF或EPS,放大不糊;给项目组做演示,PNG格式分辨率调到300dpi足够。saveas确实方便,但直接保存fig格式体积大,普通场合没必要。

exportgraphics(gcf, 'vibration_histogram.pdf', 'ContentType', 'vector');

4. 绘制统计图表遇到的那些坑

4.1 高频问题速查表

这些年反复踩过的问题,我整理成一张速查表,对照排查效率很高。

问题常见原因解决办法
图窗被下一张覆盖没有新建figure绘图前新建figure,或对指定图窗用figure(f)
数据里有NaN导致统计全空缺失值未处理用rmmissing清理,或给统计函数加omitnan
箱线图横轴是默认标签没传Labels参数使用分组向量加Labels显式指定
直方图箱子方向不对数据经过异常变换先用最小箱数确认数据范围,再调BinWidth
图例和实际曲线对不上绘图顺序与legend顺序不一致在legend中显式传入各图对象的句柄
中文符号导出成方块字体不支持中文用FontName指定支持中文的字体,或改用英文标注
导出PDF后文字缺失字体未正确嵌入使用常规字体,并检查导出字体嵌入设置

其中“图例与实际对不上”这个问题我吃过一次大亏。当时一张图里叠了三条不同工况的曲线,用了默认legend顺序,结果和绘图顺序不一致,图上曲线和图例错位。后来改成显式传入句柄的方式,再没出过问题。

4.2 我常用的几个实操技巧

第一个技巧:所有统计图统一加数据量标注。看图的人最关心的往往就是“这个分布基于多少条数据”。我习惯在标题或副标题里写清楚n=数量,这样图就算被单独引用,数据来源也一目了然。

第二个技巧:多组数据对比时固定坐标轴范围。如果画三张图分别对应三个工况,想对比分布差异,那么三张图的x轴和y轴范围必须一致。否则视觉差异会被坐标伸缩放大或缩小。用ylim和xlim显式指定,是最简单也最容易被忽略的一步。

第三个技巧:散点图点多了加透明度。scatter函数支持MarkerFaceAlpha参数,设成0.2或0.3之后,重叠区域的密集程度能通过颜色深浅看出来,比硬调点大小有效得多。

第四个技巧:脚本开头固定写clear; close all; clc。早期不清理环境,上一个脚本遗留的变量串进下一个脚本,统计结果和图表莫名其妙对不上。后来统一以这句开头,这类问题几乎绝迹了。

最后再分享一个习惯:每张用于交付的图,我都在脚本注释里写清楚数据来源、清洗规则和关键绘图参数。一开始是逼自己养成的,后来发现作用远超备忘。隔几个月重新打开脚本,还能准确还原当时的分析口径,接手的人也能少走弯路。图是给人看的,而让图和数据对得上的注释,才是图表真正有说服力的那部分。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进