
第一次用Matlab画柱状图的时候我猜不少人和我一样敲下bar(y)图是出来了结果又矮又扁柱子上没有数值图例乱糟糟放到报告里自己都看不过去。后来我做课程选修数据和实验对比分析时才慢慢把bar函数的用法摸透。这个函数看着简单实际牵扯到数据矩阵的摆放、分组/堆叠模式的选择、坐标轴刻度调整、标签与误差棒的叠加每一步都有一堆细节。这篇东西就围绕bar函数展开从基础语法讲到分组绘制再给一个完整的统计数据对比步骤。适合刚开始用Matlab做数据可视化的同学也适合那些只会bar(y)但一遇到多组数据就不知道该往函数里塞什么的人。我尽量用实际跑过的代码和踩过的坑来讲你看完能直接拿去改自己的数据。1. 先搞懂bar函数的基本用法别只会柱状图而已1.1 最省事的bar(y)一份数据一根柱bar(y)是入门的第一行代码。当y是一个向量的时候Matlab会把它当成一组数据横坐标自动取1到length(y)每一根柱子的高度就是向量里对应位置的值。比如data [5 8 3 9 6]; bar(data);这段代码会画出5根柱子高度分别对应data里的5个数字横坐标刻度是1、2、3、4、5。这个用法适合快速查看数据的大致分布比如一天里不同时段的访问量、一周内每天的销量。但你要是直接把它用在一堆原始数据上画出来多半没法看因为bar不是直方图它适合已经统计好的“分类汇总值”不适合拿大量原始样本直接塞进去。bar(y)还有一个容易被忽略的点如果y本身是复数或者包含NaN柱子会高度不可控。我在处理带缺失值的数据时一般先用rmmissing或者isnan把无效值清掉再传给bar否则柱子高度会莫名其妙少一截或者整根不显示。1.2 带x轴的bar(x,y)柱子位置自己说了算真实场景里横坐标不会是简单的1、2、3。你想让柱子落在具体类别名称或自定义的位置上就得用bar(x,y)。这里的x是柱子的中心位置y是对应位置的数值。x [1 2 3 4 5]; y [12 18 7 15 10]; bar(x, y);跑出来柱子还是排在1到5但x的作用是让你能后续用set(gca,XTick,x,XTickLabel,{周一,周二,周三,周四,周五})这样的方式替换刻度标签。如果不传x后面替换刻度标签时经常会出现标签和柱子对不齐的尴尬。需要留意的是x和y必须是同长度的向量。x不一定非要是整数你可以写成[0.5 1.5 2.5]这种带偏移的位置也可以画不均匀分布的柱状图。我有时候做分段数据会把x设为区间中点比如[10,30,50,70,90]柱子自然落在区间中间比默认的1到5直观得多。1.3 bar的宽度与两项最常用修饰bar(x,y,width)里的第三个参数是柱宽。默认宽度是0.8取值范围是0到1。宽度越接近1柱子越宽柱子之间缝隙越小越接近0柱子越细看起来越稀疏。bar(x, y, 0.5); % 细柱子适合数据量大的时候 bar(x, y, 0.95); % 几乎贴在一起适合强调连续性宽度参数在做分组柱状图时也特别关键。默认0.8在系列数少的时候没问题但系列一多比如一组里塞5根柱子默认宽度会让每根柱子窄得跟牙签似的。这时候把width调小一点比如0.5柱间距就明显了。另外两个常用修饰是FaceColor和EdgeColor分别控制柱子的填充色和边框色。最简单的方式是在bar里直接指定bar(x, y, FaceColor, [0.2 0.6 0.8], EdgeColor, k, LineWidth, 1);老版本Matlab用户可能习惯用set(h, FaceColor, ...)新版本推荐直接在bar调用里写参数值或者拿到bar返回的句柄再改属性效果一样可读性更好。2. 分组柱状图与堆叠柱状图多系列数据的正确打开方式2.1 矩阵直接决定分组结构真正让bar函数变形的是它处理矩阵的方式。假如Y是一个m行n列的矩阵bar(Y)会把每一行当成一个分组每一列当成一个系列。行数决定横坐标上有多少组列数决定每组里并排画多少根柱子。举个例子三门课程分别统计男生和女生的选修人数数据矩阵可以摆成data [ 86 54; % 高数男86女54 63 72; % 线代男63女72 45 67 % 英语男45女67 ]; bar(data);矩阵的第一行是高数第二行是线代第三行是英语第一列是男生人数第二列是女生人数。跑出来的图就是三组柱子每组里有两根并排的柱子第一根代表男生第二根代表女生。这一步最关键的是搞清楚行列顺序。很多人把矩阵行列摆反了画出来变成两组、每组三根柱子横坐标的含义完全变味。我的习惯是先写注释比如“每行一个课程每列一个性别”再填数据这样不容易乱。如果还想同时控制横坐标位置可以用bar(x, data)其中x必须是一个和行数等长的向量。这时每一行会落在x对应元素的位置上组内柱子的相对位置由Matlab自动算不需要你手动偏移。2.2 堆叠模式bar(data,stacked)多系列数据除了并排对比有时候更需要看“总量和构成”。比如三门课程的选修总人数以及其中男生女生各占多少。这时候把参数改成stackedbar(data, stacked);每一行会变成一个长柱子柱子的总高度等于这一行的所有数据之和不同列的数据按照顺序堆叠在柱子里用颜色区分。这种模式非常适合展示部分与整体的关系比如各季度销售额由哪几个产品线构成学校各年级人数由哪几个专业构成。堆叠图有一个视觉陷阱如果要横向对比不同柱子之间的单个系列数值stacked并不直观因为每一段的起点不一样肉眼看长短很容易误判。这种情况更适合用grouped模式并排画。我之前给一份季度销售数据做图既有总销售额又有分产品线最后是两张图配合一张堆叠看总量一张分组看细分趋势。另外堆叠图里如果数据存在负数柱子会变成“有上往下堆”的奇奇怪怪形状可读性很差。遇到正负混合数据我建议不要用堆叠改成分组柱状图或者用面积图表达。2.3 横向柱状图barh不是花活是真的好用barh是bar的水平版本用法完全一样只是横纵轴对调。比如排名数据、名称很长的分类变量用水平柱状图会更舒服。barh(data);当分类名称比较长比如“高等数学A班”“线性代数B班”这类的垂直柱状图的横坐标标签会叠成一团旋转45度也不一定看得清。换成水平柱状图名称放在左侧自然横向排列阅读顺序也很流畅。barh同样支持grouped和stacked模式也支持barh(x,y,width)语法。我常用它来做排行榜数据从大到小排序后画出来横向柱子一眼就能看出谁高谁低比垂直图更适合做PPT汇报。3. 统计数据对比实战从原始表到能放进报告3.1 数据表怎么摆成bar认的矩阵先说一个完整例子。假设我们要对比某学院三门公共课的男女选修人数原始数据可能是Excel表或者Matlab表格变量% 原始表格课程、男生、女生 courses {高等数学; 线性代数; 大学英语}; data [86, 54; 63, 72; 45, 67];直接画柱状图之前建议先把x轴的刻度标签和legend的系列名定好b bar(data, 0.7); set(gca, XTickLabel, courses); legend({男生, 女生}, Location, northwest);legend里花括号包两个字符串顺序必须和矩阵列顺序一致。矩阵第一列对应legend第一个字符串第二列对应第二个字符串。顺序反了颜色和含义就全乱了。我踩过一次图上男生女生颜色对调幸好汇报前发现了不然尴尬到家。如果源数据是从表格里读出来的比如table类型可以先取出所需的列再转为double数组。bar函数不直接接受table转成矩阵再画是最稳的。3.2 在柱子上加数据标签柱状图没标签数值都得靠眼睛估这是新手图最明显的短板。手动加标签的办法是用text函数。关键是要算准每根柱子的中心位置。分组模式下组内的柱子位置不是简单的x(i)而是要往左或往右偏移。我自己常用的计算方法是b bar(data, 0.7); [nx, ns] size(data); % nx是组数ns是系列数 w 0.7; % 与bar的宽度参数保持一致 hold on; for i 1:ns xpos (1:nx) (i - (ns 1) / 2) * (w / ns); ypos data(:, i) max(data(:)) * 0.02; text(xpos, ypos, string(data(:, i)), ... HorizontalAlignment, center, ... FontSize, 9); end这段的核心是xpos的偏移公式。系列数为2、宽度为0.7时第一列柱子偏左第二列偏右偏移量是±(0.7/2)/?吗看代码里(i - (ns 1)/2) * (w/ns)当ns2时i1得到-0.5*0.35-0.175i2得到0.175柱子中心位置就在组中心左右各偏0.175。这样标签正好落在柱顶中间不会歪。如果嫌公式麻烦新版Matlab里bar返回的Bar对象有XEndPoints和YEndPoints属性直接就是柱顶中心坐标。可以这样用text(b.XEndPoints, b.YEndPoints offset, ... string(data(:)), HorizontalAlignment, center);老版本没有这两个属性时就用上面的偏移公式。标签与柱顶之间留一点空隙这样数字不会被柱子边缘盖住看上去也更清爽。3.3 叠加误差棒统计对比里只画平均值不画误差容易被审稿人、导师、领导追问“波动多少”。柱状图上叠加误差棒的常见思路是先用bar画柱子再用errorbar在柱子顶部加误差线。因为errorbar不接受分类x轴我们要把柱子中心坐标算出来再叠加。沿用上面的偏移公式hold on; for i 1:ns xpos (1:nx) (i - (ns 1) / 2) * (w / ns); errbar errorbar(xpos, data(:, i), err(:, i), ... k, LineStyle, none, LineWidth, 1); errbar.Marker none; enderr(:,i)是每个柱子的误差值向量长度要和data行数一致。误差棒用黑色k线型设成none就只显示上下误差线不连出一条斜线。Marker设成none可以避免折线图默认的点标记。误差棒叠加时有个小坑如果不先hold on第二次画图会把之前的柱子覆盖掉如果忘记设置LineStyle为none系统默认会连出一根横线像把相邻柱子串起来图面很乱。这两点我在实际里都踩过检查顺序一般是先柱状图再hold on再errorbar。3.4 多子图与保存图片做统计对比经常需要把“分组柱状图”和“堆叠柱状图”并排放在一张图里。用subplot是常规操作subplot(1, 2, 1); bar(data, grouped); title(分性别对比); grid on; subplot(1, 2, 2); bar(data, stacked); title(各课程总人数及构成); grid on;保存图片时print函数比截图靠谱得多。想导出高清PNG我一般用print(gcf, course_comparison, -dpng, -r300);-r300表示300dpi在PPT或论文里够清晰了。如果只需矢量图可以导出-depsc格式放大不会糊。用新版Matlab的exportgraphics(gcf, course_comparison.png, Resolution, 300)效果类似还更好写。导出前记得把figure窗口宽度拉大一点不然柱状图会自动压缩标签挤在一起。4. 画柱状图时最容易踩的坑4.1 柱子分不开或挤成一团最常见的问题是一组里柱子叠在一起或者多组之间没有间距。原因多半是宽度参数太大或者直接用了bar(data)但数据规模不匹配。宽度默认0.8当系列数少比如只有1个系列时没问题系列数变成4、5默认宽度会让组内柱子相互重叠。解决方法很简单把宽度调小比如bar(data, 0.5)或者bar(data, 0.35)。宽度越小同一组内柱子之间的空隙越大。还有一种情况横坐标用了非均匀间隔向量比如x [1 2 5 10]而柱宽又是相对x刻度间隔计算的后两组会看起来特别瘦。遇到这种不均匀分组我一般会避免直接用bar改成bar加自定义宽度或者干脆重新设计坐标轴用等距分类标签代替数值坐标。4.2 坐标轴标签错位或显示不全XTickLabel替换是另一个高频问题。很多人直接bar(data); set(gca, XTickLabel, courses);如果数据只有3行当前XTick正好是1、2、3标签能对上。一旦某个分类没有数据或者x用了[1 3 4]这种不连续坐标Matlab会保留所有旧刻度而你只给了三个标签标签就会错位。稳妥做法是先把XTick定好set(gca, XTick, 1:nx); set(gca, XTickLabel, courses);先固定刻度位置再给位置贴标签顺序不能反。还要记住Matlab的XTickLabel是自动按当前刻度顺序分布的不会去重排所以标签向量长度必须和刻度数一致。垂直柱状图的横坐标标签太长时文字会互相叠。除了换barh可以旋转标签xtickangle(30);这个函数在新版Matlab里直接可用老版本要用set(gca,XTickLabelRotation,30)。旋转之后长名称基本不会再打架。4.3 中文字体乱码Matlab默认字体在Windows和Mac上对中文支持不一致画出来的图上经常是方框或者乱码。这不是bar的问题是图形字体设置问题。我一般会在画图前统一设置set(0, DefaultAxesFontName, SimHei); set(0, DefaultTextFontName, SimHei);SimHei是黑体Windows下比较稳。Mac下可以改成PingFang SC或者Heiti SC。如果依然乱码还可以用exportgraphics输出图片试试某些版本显示正常但复制到剪贴板乱码大概率是字体嵌入问题。Matlab脚本文件本身也可能中文注释乱码。复制粘贴别人代码的时候如果看到注释乱成一团可以在编辑器里设置编码为UTF-8再重新打开脚本。这个问题和bar函数没直接关系但会打断画图流程顺手说一下能省不少排查时间。4.4 颜色与样式管理的坑默认颜色是Matlab配色方案的蓝、红、黄多系列数据时颜色循环顺序固定。问题是不同图之间颜色含义不一致。比如第一张图序列1是蓝色第二张图序列1变成黄色读者会懵。颜色管理我建议在画图前统一设置坐标轴ColorOrderset(gca, ColorOrder, [0.2 0.4 0.8; 0.8 0.3 0.3; 0.3 0.7 0.3]);这样bar会按照你指定的三行颜色依次给系列上色。多组多系列时颜色仍会按列循环使用不会出现同一系列在组间变色。FaceColor如果设置成flat柱子颜色会由CData控制和图例颜色有时对不上。如果只是想做简单的统一配色建议在bar里直接传FaceColor值或者使用ColorOrder设定。对新手来说这比折腾CData安全很多。4.5 数据大小差异太大时的处理建议当一组数据是几百另一组只有几时数值小的柱子几乎看不见。直接把两组画在一起小柱子被压成一条线对比没意义。处理办法通常是用堆叠图看总量或者拆成两个子图或者对数据做缩放并标注原始值。我做过一次实验室测试数据对比对照组均值在90上下实验组只有3不到。最后没有强行画在一张柱状图里而是左边画对照组右边单独放大实验组再在标题里写明范围。这样既保留了真实数值又保证细节可见。柱状图的本质是让对比关系干净清楚不是把所有数据硬塞进一张图里。最后说句实在话bar函数学起来快真正用好还是靠多画。我在几次实战里最大的体会是数据矩阵的摆放决定一切图例和标签的顺序永远要跟着矩阵走加误差棒和标签时不要暴力复制网上的代码先理解柱子中心坐标是怎么算出来的保存图片永远用高分辨率导出截图出来的图放到论文里会糊。如果你只是想把数据快速看一眼bar默认参数就够了。但要是想让图能见导师、进报告建议把宽度、配色、标签、误差线这套流程固定成自己的模板。后面再遇到多组数据复制改数据就能出图效率高很多。