1. 从“能用”到“好看”:为什么你需要掌握bar函数的高级技巧
很多刚开始用MATLAB的朋友,可能觉得画个柱状图还不简单?不就是 bar(y) 敲进去,图就出来了吗?我刚开始也这么想,直到有一次,导师把我画的图打回来,说“这图拿出去汇报,太丑了,不专业”。我才意识到,能用和好用、好看之间,隔着一条巨大的鸿沟。
你想想看,无论是毕业论文里的数据对比,还是工作汇报中的业绩展示,一张清晰、美观、信息传达准确的柱状图,往往比大段的文字更有说服力。基础的 bar 函数就像给你一块未经雕琢的木头,它能立起来,但粗糙、不精致。而高级应用技巧,就是你的刻刀和砂纸,让你能把这根木头打磨成一件精美的工艺品。这些技巧不仅仅是“美化”,更是为了更有效地沟通。通过调整柱子的分组方式、堆叠顺序、颜色搭配、坐标轴细节,你可以引导观众的视线,突出关键数据,让复杂的数据关系一目了然。
我见过太多因为图表太丑而被低估的优秀工作。所以,花点时间把 bar 函数玩透,绝对是一笔划算的投资。接下来,我就把我这些年踩过坑、总结出来的实战经验,从最基础的参数讲起,一直到那些能让你图表脱颖而出的定制化技巧,一步步分享给你。咱们的目标是:告别默认的“蓝柱子”,画出有自己风格和灵魂的专业图表。
2. 基础不牢,地动山摇:彻底搞懂bar函数的几种核心模式
在玩花样之前,咱们得先把基本功打扎实。bar 函数最核心的,其实就是三种绘图模式:分组式、堆叠式和三维式。每种模式适合不同的数据故事,用错了场景,效果会大打折扣。
2.1 分组柱状图:对比的利器
分组柱状图是默认模式,也是最常用的。当你想对比多组数据中,各个分类项的情况时,它就派上用场了。比如,对比A、B、C三个部门在Q1、Q2、Q3、Q4四个季度的销售额。
% 示例数据:3个部门,4个季度的销售额(单位:万元)
sales_data = [120, 135, 118, 145; % 部门A
95, 110, 105, 130; % 部门B
80, 98, 120, 110]; % 部门C
quarters = 1:4;
figure('Position', [100, 100, 800, 400]) % 设置图形窗口大小
subplot(1,2,1)
% 最基本的分组图,x轴自动编号为1,2,3,4
bar(sales_data')
title('默认分组柱状图')
xlabel('季度')
ylabel('销售额 (万元)')
legend({'部门A', '部门B', '部门C'}, 'Location', 'northwest')
subplot(1,2,2)
% 指定x轴坐标,并设置柱子宽度为0.7(默认0.8),让柱子稍细,分组更清晰
bar(quarters, sales_data', 0.7)
title('指定x坐标与宽度')
xlabel('季度')
ylabel('销售额 (万元)')
legend({'部门A', '部门B', '部门C'}, 'Location', 'northwest')
运行这段代码,你会看到左右两幅图。左边是基础版,右边我们做了两处小优化:一是用 quarters 变量明确指定了x轴刻度位置,二是将 width 参数设为0.7。别小看这个宽度参数,当数据组很多时,默认宽度可能会导致柱子太挤,调小一点,图表会显得更清爽。分组图的精髓在于,同一季度下三个部门的柱子紧挨在一起,方便你进行横向对比,一眼就能看出哪个部门在哪个季度表现最好。
2.2 堆叠柱状图:展示构成的秘密武器
当你不仅想知道总量,还想知道总量里各个部分的构成时,堆叠图就是最佳选择。比如,展示每个季度的总销售额,同时看清每个部门对总销售额的贡献比例。
figure
% 关键参数:'stack'
bar(quarters, sales_data', 0.7, 'stacked')
title('堆叠柱状图:展示各部门贡献')
xlabel('季度')
ylabel('总销售额 (万元)')
legend({'部门A', '部门B', '部门C'}, 'Location', 'northwest')
grid on % 添加网格线,便于观察高度
看,现在每个季度的柱子高度变成了三个部门销售额的总和。柱子内部用不同颜色区分了各部门的贡献。你可以清晰地看到,第二季度总销售额最高,而且主要是由部门A拉动的。堆叠图非常适合展示“部分-整体”的关系。这里有个小坑我踩过:如果你的数据中有负值,堆叠起来会有点反直觉(一部分向上堆,一部分向下堆),这时候就需要谨慎使用,或者考虑其他图表类型了。
2.3 三维柱状图:给数据增加一个维度
有时候数据本身就有两个维度需要同时展示,比如,你想看不同产品在不同地区的销量。这时,二维平面可能就不够用了,可以考虑三维柱状图 bar3。
% 示例数据:4种产品在3个地区的销量
product_sales = [30, 25, 40; % 产品1 在地区1,2,3
45, 30, 35; % 产品2
20, 50, 25; % 产品3
35, 20, 45]; % 产品4
products = {'产品1', '产品2', '产品3', '产品4'};
regions = {'北美', '欧洲', '亚洲'};
figure
% 使用bar3绘制三维分组图
h = bar3(product_sales);
title('三维柱状图:产品-地区销量分布')
zlabel('销量 (千台)')
% 设置坐标轴标签
set(gca, 'XTickLabel', regions, 'YTickLabel', products)
% 为每个系列设置不同的颜色(后面会详细讲颜色定制)
colormap(jet(4)) % 使用jet色图,生成4种颜色
for i = 1:length(h)
h(i).CData = h(i).ZData; % 根据高度着色
h(i).FaceColor = 'interp'; % 面颜色插值
end
三维图视觉冲击力强,能同时展示两个分类维度和一个数值维度。但它也有缺点:如果柱子太多,容易显得杂乱,并且从某个角度观察时,后面的柱子可能会被挡住。所以我的经验是,三维图适合在数据组数不多(比如每个维度不超过5个),且需要在汇报中给人留下深刻印象时使用。日常分析,优先考虑清晰的二维图。
3. 化腐朽为神奇:深度定制颜色与样式
默认的MATLAB配色(那套蓝橙黄紫…)看久了真的会腻,而且很难与你的报告或PPT主题色匹配。掌握颜色和样式的定制,是让你的图表脱离“学生气”,走向“专业范”的关键一步。
3.1 告别单调:为柱子赋予丰富的色彩
设置颜色最直接的方法,是在调用 bar 函数时使用 FaceColor 和 EdgeColor 参数。但更灵活、更强大的方式,是在画出图形后,通过操作图形对象句柄来修改。
% 准备数据
categories = {'方案A', '方案B', '方案C', '方案D', '方案E'};
scores = [85, 92, 78, 88, 95];
figure
% 绘制基础柱状图,并获取句柄。'FaceColor', 'flat'是为后续逐条着色做准备。
h_bar = bar(1:5, scores, 'FaceColor', 'flat');
title('自定义柱体颜色方案')
xlabel('评估方案')
ylabel('得分')
set(gca, 'XTick', 1:5, 'XTickLabel', categories)
% 方案1:手动指定RGB颜色数组。RGB值范围是[0, 1]。
% 这里定义一套深蓝、深绿、古铜金、深紫、珊瑚红的配色
custom_colors = [0.1, 0.3, 0.6; % 深蓝
0.1, 0.5, 0.3; % 深绿
0.7, 0.5, 0.2; % 古铜金
0.5, 0.2, 0.6; % 深紫
0.9, 0.4, 0.3]; % 珊瑚红
% 将自定义颜色赋给每个柱子的CData属性
h_bar.CData = custom_colors;
% 方案2:使用MATLAB内置的色图(colormap)生成颜色。
% 比如,你想用‘parula’色图,根据得分高低生成渐变色。
figure
h_bar2 = bar(1:5, scores, 'FaceColor', 'flat');
title('基于色图的渐变色方案')
xlabel('评估方案')
ylabel('得分')
set(gca, 'XTick', 1:5, 'XTickLabel', categories)
colormap('parula') % 应用parula色图
% 将每个柱子的CData设置为其Y值(得分),颜色会自动映射
h_bar2.CData = scores';
第一种方案给你完全的控制权,适合需要严格匹配品牌色的场景。第二种方案利用色图,能自动生成与数据值相关的渐变色,比如得分越高颜色越暖(红/黄),得分越低颜色越冷(蓝),这样数据趋势一眼就能看出来,非常直观。
3.2 精雕细琢:调整边框、宽度与样式
柱子的外观远不止颜色。边框的粗细和颜色、柱子的宽度、甚至是填充的图案,都能影响图表的质感。
figure
% 绘制分组数据
group_data = rand(3, 4) * 100; % 3组,每组4个数据
h_bars = bar(group_data);
% 定制每组柱子的样式
% 第一组:粗边框,无填充色(中空效果)
h_bars(1).FaceColor = 'none'; % 面颜色:无
h_bars(1).EdgeColor = [0, 0.45, 0.74]; % 边框色:MATLAB经典蓝
h_bars(1).LineWidth = 2.5; % 边框线宽
% 第二组:细边框,浅色填充,带阴影效果(通过调整FaceAlpha实现)
h_bars(2).FaceColor = [0.85, 0.33, 0.10]; % 面颜色:橙色
h_bars(2).FaceAlpha = 0.6; % 面透明度,产生半透明效果
h_bars(2).EdgeColor = 'k'; % 边框色:黑色
h_bars(2).LineWidth = 1;
% 第三组:更宽的柱子,点状边框
h_bars(3).FaceColor = [0.47, 0.67, 0.19]; % 面颜色:绿色
% 注意:要单独设置每个柱子的宽度比较麻烦,通常是在bar函数中用width参数统一设置。
% 这里我们演示另一种思路:用BarWidth属性(但此属性是只读的,说明不能直接改)。
% 所以,调整宽度最好在绘图时就用 width 参数设定,例如 bar(data, 1.2) 表示加宽20%。
% 我们可以修改边框样式
h_bars(3).LineStyle = '--'; % 边框线型:虚线
h_bars(3).LineWidth = 1.5;
title('多组柱子样式混合定制')
xlabel('类别')
ylabel('数值')
legend({'组1: 中空粗边', '组2: 半透明填充', '组3: 虚线边框'})
通过这个例子,你可以看到如何把同一张图里的不同数据组,用截然不同的样式区分开。这在对比“实际值”与“预测值”、“对照组”与“实验组”时特别有用。FaceAlpha(面透明度)是个神器,当柱子有重叠或你想展示底层网格线时,调低透明度能让图表层次感更强。
4. 细节决定成败:坐标轴、基线与其他高级设置
图表的美观和专业,很大程度体现在细节上。默认的坐标轴、那条灰色的基线、背景网格,往往还有很大的优化空间。
4.1 掌控坐标轴:范围、刻度与标签
MATLAB默认的坐标轴范围有时会留出太多空白,或者刻度太密。手动调整它们,能让数据占据视觉中心。
performance = [88, 92, 79, 85, 96, 90];
models = {'Model-A', 'Model-B', 'Model-C', 'Model-D', 'Model-E', 'Model-F'};
figure
barh(performance) % 使用水平柱状图,有时更利于标签展示
set(gca, 'YTickLabel', models) % 设置Y轴刻度标签为模型名称
title('模型性能对比 (F1 Score)')
xlabel('F1 Score')
% 精细调整坐标轴
ax = gca; % 获取当前坐标轴句柄
ax.XLim = [70, 100]; % 设置X轴显示范围为70到100,聚焦数据区间
ax.XTick = 70:5:100; % 设置X轴刻度线位置,每5个单位一个
ax.XGrid = 'on'; % 开启X轴方向的网格线
ax.GridLineStyle = ':'; % 网格线设为点线,更柔和
ax.GridAlpha = 0.3; % 网格线透明度,0.3表示30%不透明
% 美化字体
ax.FontName = 'Arial'; % 设置坐标轴字体
ax.FontSize = 11;
title('模型性能对比 (F1 Score)', 'FontSize', 14, 'FontWeight', 'bold') % 加粗标题
这里用了 barh 画水平柱状图,因为模型名字通常较长,水平排列更易读。XLim 和 XTick 的配合使用,能有效消除图表两侧的无效空白,让观众的注意力完全集中在数据分布的区间内。网格线用点线 (:) 并降低透明度,是为了提供参考的同时不喧宾夺主。
4.2 处理基线:让它消失或变得有用
基线就是柱状图底部的那条参考线,默认是显示的一条细线。在很多设计精美的图表里,这条线是被隐藏的,以追求更简洁的视觉风格。
data_with_negative = [3, -2, 5, -1, 4]; % 包含负值的数据
figure
subplot(1,2,1)
bar(data_with_negative)
title('默认基线 (显示)')
grid on
subplot(1,2,2)
h = bar(data_with_negative);
title('隐藏基线并移动X轴')
grid on
% 获取并修改基线属性
bl = h.BaseLine; % 基线的句柄
bl.Visible = 'off'; % 隐藏基线
% 更高级的操作:将X轴移动到y=0的位置,这对于有正负值的数据非常清晰
ax = gca;
ax.XAxisLocation = 'origin'; % 将X轴移动到y=0处
ax.YAxisLocation = 'origin'; % 将Y轴移动到x=0处(可选)
box off % 去掉坐标轴的盒子边框,让图表更简洁
左边是默认效果,基线存在。右边我们做了两件事:一是把基线隐藏了,二是把X轴移到了y=0的位置。当数据有正有负时,这样处理能让正负分区一目了然,图表也显得非常干净利落。box off 是另一个提升简洁度的小技巧,它去除了图表四周的边框线。
4.3 添加数据标签与文本注释
让读者去对着坐标轴刻度猜柱子的具体数值,是非常不友好的。直接在柱子上方或内部标注数值,是专业图表的标配。
monthly_growth = [2.1, 5.3, -1.2, 4.5, 3.3, 6.7];
months = {'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'};
figure
h_bars = bar(1:6, monthly_growth);
ax = gca;
ax.XTick = 1:6;
ax.XTickLabel = months;
ylabel('增长率 (%)')
title('月度增长率(带数据标签)')
ylim([-3, 8]) % 为标签留出空间
% 循环为每个柱子添加数据标签
for i = 1:length(monthly_growth)
% 获取柱子的顶端坐标
x_pos = h_bars.XData(i) + h_bars.XOffset; % XOffset对于分组图很重要
y_pos = h_bars.YData(i);
% 根据数值正负决定标签放在柱内还是柱上
if y_pos >= 0
text_pos = y_pos + 0.2; % 正数:标签放在柱子上方一点
vert_align = 'bottom';
label_color = 'k'; % 黑色
else
text_pos = y_pos - 0.3; % 负数:标签放在柱子下方一点
vert_align = 'top';
label_color = 'r'; % 红色,突出负值
end
% 格式化标签文本,保留一位小数
label_text = sprintf('%.1f%%', y_pos);
% 添加文本
text(x_pos, text_pos, label_text, ...
'HorizontalAlignment', 'center', ...
'VerticalAlignment', vert_align, ...
'FontSize', 10, ...
'FontWeight', 'bold', ...
'Color', label_color);
end
% 添加一条y=0的参考线,更清晰
yline(0, 'k--', 'LineWidth', 1); % 黑色虚线
这段代码的关键在于 h_bars.XOffset。在分组柱状图中,每个柱子组的X坐标是整数,组内每个柱子有一个偏移量 XOffset,正确计算这个值才能把标签放在柱子正上方。我们根据数值的正负,动态调整标签位置和颜色(负值用红色),并添加了一条y=0的参考线。这样一来,图表的信息量大大增加,读者无需费力读刻度,就能准确获取每个数据点。
5. 实战组合拳:打造一份可直接用于汇报的图表
学了这么多散招,最后我们来打一套组合拳,从头到尾制作一张可以直接放进学术报告或商业PPT的、出版级质量的柱状图。我会把前面讲到的技巧都融合进去。
假设我们要展示一项实验中,三种不同算法在四个不同数据集上的运行时间对比。
% 1. 准备数据
datasets = {'Set-1', 'Set-2', 'Set-3', 'Set-4'};
algorithms = {'Algo-A', 'Algo-B', 'Algo-C'};
% 运行时间数据 (秒),行是算法,列是数据集
run_time = [12.5, 25.1, 8.7, 30.2; % Algo-A
18.3, 30.5, 15.2, 35.8; % Algo-B
9.8, 20.4, 7.5, 28.6]; % Algo-C
% 2. 创建图形窗口,设置大小和DPI(确保导出清晰)
fig = figure('Position', [100, 100, 900, 500], 'Color', 'w'); % 白色背景
% 设置渲染器,避免导出时样式丢失
set(fig, 'Renderer', 'painters');
% 3. 绘制分组柱状图,并获取句柄
% 使用1.05的宽度,让柱子稍微分开一点,更清晰
h = bar(run_time', 1.05); % 注意这里对数据做了转置,因为bar按列分组
hold on; % 保持图形,以便添加其他元素
% 4. 定制颜色方案(使用一套高级灰配色)
algo_colors = [0.2, 0.4, 0.6; % 深蓝灰
0.8, 0.3, 0.3; % 砖红色
0.3, 0.6, 0.4]; % 橄榄绿
for i = 1:length(h)
h(i).FaceColor = algo_colors(i, :);
h(i).EdgeColor = 'none'; % 去掉边框,现代简约风格
h(i).FaceAlpha = 0.85; % 轻微透明度,增加质感
end
% 5. 坐标轴与标签美化
ax = gca;
ax.FontName = 'Helvetica'; % 使用无衬线字体,更现代
ax.FontSize = 12;
ax.Box = 'off'; % 去掉坐标轴盒子
ax.TickDir = 'out'; % 刻度线朝外
ax.XLim = [0.25, length(datasets)+0.75]; % 控制X轴范围,让图表两侧有适当留白
ax.YLim = [0, ceil(max(run_time(:))/5)*5 + 5]; % Y轴上限取整,留出标签空间
% 设置X轴刻度标签
ax.XTick = 1:length(datasets);
ax.XTickLabel = datasets;
xlabel('数据集', 'FontSize', 13, 'FontWeight', 'bold')
ylabel('运行时间 (秒)', 'FontSize', 13, 'FontWeight', 'bold')
% 6. 添加标题和图例
title('不同算法在各类数据集上的运行效率对比', ...
'FontSize', 16, 'FontWeight', 'bold', 'Interpreter', 'none')
legend(algorithms, 'Location', 'northwest', 'FontSize', 11, 'Box', 'off') % 图例去边框
% 7. 添加数据标签(只标注最大值和最小值,避免杂乱)
for algo_idx = 1:size(run_time, 1)
for ds_idx = 1:size(run_time, 2)
current_time = run_time(algo_idx, ds_idx);
% 只标注该数据集上最快和最慢的算法
times_per_dataset = run_time(:, ds_idx);
if current_time == min(times_per_dataset) || current_time == max(times_per_dataset)
x_pos = ds_idx + h(algo_idx).XOffset;
y_pos = current_time;
% 最小值标绿色,最大值标红色
if current_time == min(times_per_dataset)
color = [0.2, 0.6, 0.2]; % 绿色
offset = -0.5;
else
color = [0.8, 0.2, 0.2]; % 红色
offset = 0.5;
end
text(x_pos, y_pos + offset, sprintf('%.1f', current_time), ...
'HorizontalAlignment', 'center', ...
'VerticalAlignment', 'bottom', ...
'FontSize', 10, ...
'FontWeight', 'bold', ...
'Color', color);
end
end
end
% 8. 添加轻量级网格线,仅Y轴方向
ax.YGrid = 'on';
ax.GridLineStyle = '-';
ax.GridAlpha = 0.1; % 非常淡的网格
ax.GridColor = [0.3, 0.3, 0.3]; % 深灰色网格
% 9. 优化整个图形的边距,让内容更居中
tightfig; % 如果你有FileExchange的tightfig函数,可以用它。或者手动调整:
% set(gca, 'Position', [0.13, 0.15, 0.82, 0.75]); % 手动调整坐标轴位置
hold off;
这张图几乎用到了我们之前讨论的所有技巧:自定义配色、去除边框、调整透明度、美化坐标轴和字体、智能添加数据标签(只标出极值以避免拥挤)、添加淡雅的网格线。最终得到的图表,颜色协调、信息突出、风格专业,直接复制到Word或PPT里都完全没问题。画图到最后,其实比拼的就是对这些细节的掌控和审美。多练习,多参考优秀的图表设计,你也能很快形成自己的风格。
452



被折叠的 条评论
为什么被折叠?



