做数据分析汇报的时候,扇形统计图(饼图)是我用得最多的展示方式,没有之一。占比、构成、份额,一张饼甩出去,领导一眼就能看懂。但用matlab画饼图有个很现实的问题——默认配色是真的丑,那种饱和度极高的红黄蓝绿,配上默认字体,出来的图透着一股上世纪教科书的气质。这篇就系统梳理一下我在实际项目里用matlab做扇形统计图的完整流程,重点放在很多人忽略的配色环节,从基础绘图到颜色调配,再到导出前的细节修正,一次性讲透。
1. 整体思路与方案选型
1.1 为什么用matlab而不是Excel或Python
做扇形统计图,可选工具太多了。Excel点点鼠标就能出图,Python的matplotlib也足够灵活。但我之所以在大多数场景下坚持用matlab,原因很实际:
第一,matlab的数据预处理太顺手了。很多时候数据不是现成的比例,而是几十行甚至上百行的原始记录,需要先聚合、去重、排序,才能得到“各分类对应的数值”。这些操作在matlab里就是几行矩阵运算的事,全程不需要切换工具。Excel要做数据透视表,Python还得来回倒腾pandas和matplotlib的接口,远不如matlab一条龙顺滑。
第二,matlab的图是“对象化”的。图上每一个元素——扇区、文字、图例——都是可以单独定位修改的属性对象。这意味着我可以精确控制某一个扇区的突出显示、某一行字体的位置,甚至给某一个扇区单独上色。这种精细度对于要放进PPT汇报的场景来说,价值极高。
第三,大家经常忽略的是,matlab的饼图自带归一化逻辑。你传入的数据向量,不需要自己提前算百分比,matlab会自动求每个分量占总和的比例,然后映射到360度。这省掉了一步手动计算,也避免了四舍五入带来的百分比误差累积。
当然,如果你只是画一张图快速看看数据长什么样,用不上这么重的工具。matlab的优势在于“数据清洗 + 可视化 + 后续处理”整个链条都在一个环境里完成。这个场景对我来说是刚需。
1.2 扇形统计图的类型选型
matlab里做扇形统计图,实际有三类方案可选,它们的适用场景差别挺大:
- pie(X):二维平面饼图。最常规的选择,适合展示占比结构,例如“各部门预算占比”“用户渠道来源”“成本构成”。
- pie3(X):三维立体饼图。带厚度和透视效果,视觉冲击力强,但三维倾斜会导致扇区面积比例看起来失真,数据精度要求高的场景慎用。
- pie(X, explode):带分离效果的饼图。通过explode向量指定某些扇区向外偏移突出,适合强调关键占比。
我的建议是:正式的数据汇报优先用二维饼图,信息传达最准确。三维饼图偶尔用在非正式场合或者封面图上,调节一下气氛。至于分离式饼图,适合突出“重点中的重点”,比如“问题主要出在某个环节”这种场景。
另外还有一个变通方案:如果你觉得饼图太单调,可以用半圆饼或者环形图,matlab里通过控制坐标轴范围和patch对象可以手工实现,但这个属于进阶玩法,不是本篇重点,后面有机会单独写。基础篇先把标准饼图玩明白。
2. 核心细节解析与实操要点
2.1 数据准备与格式要求
matlab的pie函数对输入数据的格式要求非常宽松,但宽松不等于可以乱来。几个关键细节要格外注意:
输入数据必须是数值向量。不管是行向量还是列向量都行,但里面不能有NaN、Inf等非数值元素。一旦数据里出现NaN,饼图上会直接少一块,而且对应位置的标签也不会显示,排查起来很隐蔽。
数据量不宜太多。饼图对扇区数量天然不友好。数据超过6类的时候,小扇区的标签文字就会挤成一团。我个人的经验阈值是7个,超过7个就建议合并成“其他”类别。这不是matlab的限制,而是信息可视化本身的原则——人眼能分辨的颜色和区域大小有限,扇区太多,读者会下意识忽略小占比的类别,你的数据等于白画。
数值大小决定了角度,不是顺序。matlab会按照输入向量的顺序,从3点钟方向开始逆时针排列扇区。如果你想让最大的扇区出现在“第一眼”的位置,就要先对数据排序。实操中我经常用sort函数降序排列,让占比最大的类别位于起点。
还有一个类似数据归一化的细节——输入数值的总和并不要求是100或者1,matlab内部会自动求比重。例如传入[30, 20, 50],它自动换算成30%、20%、50%来分配角度。这个特性很方便,意味着你直接从原始统计结果里拉数据就能画图,省了一道换算手续。
2.2 标签、图例与百分比的显示策略
pie函数默认在扇区旁边显示的是类别标签。有两种常见做法:
方法一:直接用pie(X, labels),labels是字符串数组,长度必须和X一致。扇区旁边会显示你传入的文本。如果这个文本已经包含了百分比信息,比如“产品A 35%”,那你需要自己提前算好百分比并拼接到字符串里。
方法二:只显示类别名,不做百分比标注,然后用legend图例来对应。这种方式适合扇区比较多的情况,因为扇形旁的文本一多必然重叠。
这里有一个很多人踩过的坑:默认显示百分比需要把标签留空或者直接用数值型标签。实际上matlab里要想在扇区旁同时显示类别名和百分比,最常用的手法是用strcat拼接字符串,例如:
labels = {'产品A', '产品B', '产品C'}; percent = X / sum(X) * 100; label_with_pct = cellfun(@(x, p) sprintf('%s\n%.1f%%', x, p), labels, num2cell(percent), 'UniformOutput', false); pie(X, label_with_pct);这个写法用了一个换行符\n,让类别名和百分比分两行显示,视觉上比挤在一行里清爽得多。cellfun可能对新手有点绕,但效果值回票价。
图例的对齐也是细节。默认图例会放在图形右侧,如果扇区形状偏横长,图例往往会遮挡部分图形区域。我习惯用legend('Location', 'eastoutside')把图例固定在右侧外侧,再通过legend('Box', 'off')去掉边框,整张图看起来就干净利落。有时候还需要用legend('NumColumns', 2)把图例排成两列,进一步压缩图例占用区域。
2.3 扇区突出显示的explode机制
explode参数是饼图功能里最实用但经常被忽略的一个。它是一个与X同长度的逻辑向量(也可以为0/1数值向量),对应位置的值为1时,该扇区会从中心向外偏移一小段距离。
例如:突出显示最大的扇区:
X = [35, 25, 20, 20]; explode = [1, 0, 0, 0]; pie(X, explode);这里有个细节值得展开说一下:explode向量中值为1的扇区在排序后位置要对应准确。如果你先对X做了排序,explode向量也要同步重排,否则突出的不是你想突出的那一块。
从视觉逻辑上讲,突出显示的扇区会加重该部分在读者眼中的权重。这正好契合汇报场景的需求——你需要告诉听众“看这一块”,它就更显眼。但要注意,explode的偏移量是matlab自动计算好的,不需要手动调整。如果你觉得偏移不够明显,可以后续通过修改底层patch对象的XData和YData来做微调,这个属于进阶玩法。
三维饼图pie3也支持explode参数,实际效果是扇区沿径向向外拉伸一段距离,并且会带出厚度差。视觉上更明显,但精度观感有损失,建议只在图上只有两三个扇区时使用。
3. 实操过程与核心环节实现
3.1 基础饼图五步走
从零开始画一张合格的饼图,我总结为五步:整理数据、创建画布、绘制饼图、修饰细节、导出图片。下面用一个完整案例演示。
假设手头有这么一份数据——某公司四个产品线的季度营收占比,原始数据是各产品线的营收金额(单位:万元):
% 第一步:准备数据 revenue = [420, 310, 180, 90]; % 各产品线营收 products = {'产品A', '产品B', '产品C', '产品D'}; % 第二步:排序,让大扇区位于起点 [revenue_sorted, idx] = sort(revenue, 'descend'); products_sorted = products(idx); % 第三步:拼接待百分比标签 total = sum(revenue_sorted); pct = revenue_sorted / total * 100; labels = cellfun(@(x, p) sprintf('%s\n%.1f%%', x, p), ... products_sorted, num2cell(pct), 'UniformOutput', false); % 第四步:绘制饼图,突出最大扇区 explode = zeros(1, length(revenue_sorted)); explode(1) = 1; pie(revenue_sorted, explode, labels); % 第五步:基础修饰 title('产品线营收占比分析', 'FontSize', 14, 'FontWeight', 'bold'); legend(products_sorted, 'Location', 'eastoutside', 'Box', 'off');这里每一步都有讲究。排序那一步很多人忽略,但人的视线在饼图上会习惯从“切第一刀”的地方开始读,把最大块放在起点是顺应阅读习惯的做法。拼接百分比标签用cellfun一行完成,不用写循环,代码干净不少。
运行这段代码后,你得到是最基础的饼图——图形能看,但配色还是matlab默认的红绿蓝循环。接下来进入本文的重头戏——颜色调配。
3.2 颜色调配的核心方法
matlab的饼图上色机制要理解清楚:每个扇区对应一个patch对象,patch的FaceColor属性控制填充颜色。pie函数创建扇区时会自动分配颜色,默认情况下用的是当前坐标系的ColorOrder属性,也就是一堆默认RGB值轮换。
所以要改变饼图配色,本质上就是“改变每个patch对象的FaceColor值”。基于这个思路,有三套由浅入深的配色方案。
方案一:用colormap快速换风格
这是最省事的方法。在调用pie之前,先用colormap设置当前图形的颜色映射,pie会按顺序取色:
colormap(winter); pie(X, labels);matlab自带的colormap种类很多,parula是冷色调渐变,winter是蓝绿渐变,autumn是红黄渐变,jet是彩虹色。但说实话,这些自带colormap大多适合热力图和曲面图,用在饼图上要么太花,要么对比度不足。我平时用得比较多的是lines——它本质上是matlab的默认线图配色,共7种颜色,来自经典的ColorOrder,干净、柔和、互相不打架:
colormap(lines);方案二:手动指定RGB颜色矩阵
如果自带colormap不满足需求,直接自定义一个颜色矩阵传给colormap即可。每一行是三个数值,取值0到1之间,对应RGB归一化值。例如:
% 自定义一组高级莫兰迪色调 cmap = [0.55 0.63 0.72; 0.80 0.62 0.55; 0.94 0.83 0.60; 0.62 0.71 0.59]; colormap(cmap); pie(X, labels);这里有一个特别容易踩的坑:RGB分量是0到1的小数,不是0到255的整数。如果你从设计软件里看到的颜色值是(140, 160, 185),必须手动除以255,转成小数值再用。这个错误我在新手期犯过好几次,出来的图要么全白要么全黑。
方案三:逐个扇区精确上色
如果你想突出某一个扇区,而其他扇区保持统一色调,可以基于patch对象逐一设置颜色:
h = pie(X, labels); % 直接找到扇区对应的patch对象并单独上色 colored = [0.90 0.42 0.42]; % 重点色,类似红橙色 grayish = [0.75 0.75 0.75]; % 其他统一灰色 for k = 1:length(X) h(k).FaceColor = grayish; end % 假设第一个扇区是需要突出的(已在explode中突出) h(1).FaceColor = colored;这里有个细节要说明:pie返回的句柄向量中,前length(X)个元素对应的是扇区的patch对象(从第1个扇区开始按顺序排列),后面的元素才对应文本对象。所以修改颜色时,循环只动前段句柄即可,否则会试图给文本对象设置FaceColor,导致报错或者无效操作。
3.3 调色原理与组合经验
很多教程只会告诉你“怎么设置颜色”,不会告诉你“为什么这么设置好看”。这里补一节调色原理层面的经验。
颜色设计有三个维度需要同时考虑:色相(Hue)、饱和度(Saturation)、明度(Value)。优秀饼图配色的经验可以提炼成几条原则:
第一,相邻扇区的色相差距要足够大。如果色相太接近,比如深蓝和浅蓝相邻,读者会本能地把它们归为同类,这对展示占比结构是致命的。我的经验是:相邻色相的间隔至少跨越色环的60度以上。
第二,整体饱和度不要拉满。全饱和的颜色放在一起,视觉上会“打架”,图显得噪。行业里通用的做法是压低饱和度,控制在0.6以下,显得温和专业。实际调的时候,可以在RGB转HSV空间调整后再转回来。
如果你觉得手动调色效率太低,推荐一个取巧路线:直接到一些配色网站上看别人设计好的色板,把具体色值抄下来除以255,做成自己的色板矩阵存成mat文件,下次直接load调用。我做项目积累了几套常用色板,包括政务风(蓝灰)、金融风(深蓝金)、科技风(青紫),每套都是一行一个R G B,用时两行代码套上去,非常省事。
一条更实际的提醒:导出前一定要在屏幕上预览一次。同一个颜色,在matlab图形窗口、PPT放映环境、大屏投影仪上,观感差异很大。有些颜色在屏幕上看起来很舒服,投到投影仪上灰成一片。所以正式汇报前,有条件的话,最好在目标设备上过一眼。
3.4 三维饼图及透明度设置
如果你确实要用三维饼图撑一下场面,那么会有一个额外问题:三维饼图默认的显示效果很笨重,整个饼像一块实心奶酪,而且颜色偏暗。解决办法是调整透明度。
pie3返回的句柄中,除了扇区顶面的patch对象,还有侧面(厚度)的patch对象。默认情况下,三维饼图的透明度是1(完全不透明),导致整个图形看起来很沉重。可以把透明度调低一点:
h3 = pie3(X, explode, labels); % 调整顶面透明度 for k = 1:length(X) h3(k).FaceAlpha = 0.85; endFaceAlpha取值范围0到1,0是完全透明,1是完全不透明。建议只做轻微调整,比如0.8到0.9之间,留一点通透感就行。调太低会导致颜色混叠,反而显得脏。
另外,pie3的厚度比例是固定的,想调厚薄一般通过旋转视角来弥补。用view函数改变观察角度,比如view([-45, 25]),能让饼图看起来更立体且有层次感。
4. 常见问题与排查技巧实录
4.1 扇区标签重叠与遮挡
这是饼图最常见的翻车现场。类别多、文字长、角度小,标签就叠成一团。
排查思路分三步:
先看分类数是否超过7个。超过了,优先合并“其他”,这是根治手段,不是微调手段。
再看标签文本。很多人在标签里写全称,比如“华东地区客户分布占比”,十几个字,贴在扇区旁边必然互相挤。解决办法是标签只放短名称加百分比,全称放到图例里展示。图例空间大,不受扇区位置限制。
最后用鼠标手动拖。matlab图形窗口里,文本对象是可以直接拖动位置的。点击图形后进入编辑模式,选中标签文字,拖到合适位置即可。这个操作听起来“不那么程序员”,但在做正式汇报图时非常实用,我几乎每张饼图都要手动挪一两个标签。
4.2 颜色自动分配不合预期
有时候你觉得“我没指定颜色啊,为什么出来的颜色和上次不一样”。这个问题的根源是ColorOrder循环取色机制。每次调用pie,matlab会从当前坐标系的ColorOrder里按顺序取颜色,取一轮没取完就从头再循环。如果你的图形窗口之前设置过其他colormap,饼图的颜色顺序就会被带偏。
解决方法是:每次画新饼图之前,重置颜色环境:
set(gca, 'ColorOrder', lines(7)); colormap(lines);用lines(7)显式生成一个7行的颜色矩阵,强制设定取色序列。这样不管之前画过什么图,颜色顺序是稳定的。
4.3 数值异常导致图形缺失
如果数据向量中包含NaN或全0类别,pie的某个扇区会消失,标签也会少出一截。排查方法是用isfinite检查数据:
valid = isfinite(X) & (X > 0); X = X(valid); labels = labels(valid);这里有个常见细节:数值是0的扇区其实也没有绘制意义。0%在图上就是一个0度角,理论上不该出现。但你传入的数据里如果有0,matlab不会报错,只是扇区缺失,标签照样显示。所以建议在绘制前把0值类别过滤掉,或者合并进“其他”类别。
还有一类隐蔽问题:数据全为负值。此时sum(X) < 0,matlab计算比例时会出现负百分比,饼图角度分配直接被破坏。这时需要确认你的数据业务含义——占比类数据不可能为负,如果有负值,说明数据处理阶段有问题,先把数据修掉再画图。
4.4 导出图片模糊或变形
matlab图形窗口里看着很好,导出到PPT里却模糊,这是像素不够的典型症状。保存图片的时候,用exportgraphics或print指定高分辨率:
exportgraphics(gca, 'pie_chart.png', 'Resolution', 300);300dpi是印刷和PPT播放的通用标准,不要用屏幕截图替代导出。截图只有72dpi到96dpi,在大屏幕上放大会糊。
另外,导出之前随手把图形窗口拉成正方形或接近正方形的比例。饼图是圆形,如果窗口是细长矩形,导出的图里圆会被压成椭圆,边距也会一大一小。这个细节看似不起眼,但在汇报材料里,椭圆形的“饼”是非常掉价的。
4.5 颜色雷同与图例错位
如果你自定义的颜色矩阵里有两行RGB值非常接近,扇区在显示时几乎分不出彼此,图例上的色块也接近,读者会误认为两个类别是同一个。配色完成后,推荐做一个快速自检——把颜色矩阵打印出来,肉眼扫一遍相邻RGB值的差异:
cmap % 直接命令行执行,查看矩阵我自己一般会保证相邻两行的欧氏距离在0.2以上,距离计算方法简单粗暴:
d = sqrt(sum(diff(cmap).^2, 2));一旦发现哪两行颜色过近,就替换其中一行。别嫌这一步麻烦,图例对了但颜色接近,读者会一路误解到底,这种错误在正式汇报中很难被观众当面指出,但事后复盘会非常尴尬。
图例错位的问题也有规律可循。当legend函数没有明确指定字符串时,它会自动从坐标轴的Children对象中读取显示名。如果图形里有多个patch对象叠加,图例顺序可能和饼图扇区顺序不一致。最稳妥的写法是显式传字符串元胞数组给legend:
legend({'产品A', '产品B', '产品C', '产品D'}, 'Location', 'eastoutside');别依赖自动识别。自动识别的内部逻辑是被多个对象共享的,顺序一旦乱了,排查成本远高于手动输入这几个字符串的成本。
5. 从能用图到可汇报图的三处点睛
到了这一步,你已经能画出一张配色正确、标签清晰的饼图了。但距离可以直接放进汇报PPT,还差三处点睛细节。
第一处是标题与字体。matlab默认的宋体字形在Windows下显示偏细,投影效果不好。建议统一设置为雅黑风格且加大字号:
set(gca, 'FontName', 'Microsoft YaHei', 'FontSize', 12); title('产品线营收占比', 'FontSize', 16, 'FontWeight', 'bold');这个设置不仅对饼图生效,对整个坐标轴的文字都生效。标题加粗放大后,整张图的层级感立刻出来了。
第二处是比例信息的高亮。扇区是3.7%这种极小占比时,数字挤在角落里,读者容易忽略。除了用explode突出,还可以在标签文本里手动拼接一个箭头提示:
labels{4} = sprintf('产品D\n3.7%% ← 增速最快');当然,这个操作更适合重点突出的场景,别给每个扇区都加箭头,那就成闹市区了。
第三处是多子图排版。数据对比场景常常需要并排放两张饼图,这时可以用subplot。注意,两张饼图的颜色序列、字体大小必须一致,否则视觉上会觉得是两张不同时期的图,误导对比结论。建议定义公共色板,然后用同一个cmap分别给两个子图上色:
cmap = [0.55 0.63 0.72; 0.80 0.62 0.55; 0.94 0.83 0.60; 0.62 0.71 0.59]; subplot(1, 2, 1); colormap(cmap); % 画第一张饼图... subplot(1, 2, 2); colormap(cmap); % 画第二张饼图...我个人的实际体会是,图表工具的学习很容易止步在“能跑通”这个层面,但真正拉开差距的往往是对细节的敏感度。配色、标签、间距、字体、导出清晰度,每一个环节多花两分钟,最终呈现的图就能从“学生作业”进阶到“专业材料”的水平。
最后分享一个小技巧:做饼图前,试着把所有类别数据降序排列之后再画。你会发现不仅是视觉上更舒服,连标签重叠的概率都小了很多。原因很简单——大扇区空间多,标签自然放得开。把需要“讲清楚”的类别放在前面,是图表叙事里性价比最高的操作。