大家好,我是你们的小帅学长。
上一期我们讲了排序 + 水平条,是单维对比的最稳解法。但科研里真正常见的场景往往是不只是“谁更高”,而是“在不同条件下,谁更高?”。
例如:不同模型 × 不同数据集
不同处理组 × 不同指标
不同年份 × 不同区域
这时候,如果你还用单一柱状图硬挤,图会迅速变乱。
解决方法其实很清晰:一是 分组柱(Grouped Bar)——同图对比;二是分面图(Facet / Small multiples)——分图对比。
这一篇,我想要讲清楚:什么时候用分组柱?什么时候用分面?怎么画得像论文?
01.分组柱:同一画布上的“并列对比”
适合场景:
组别不多(2–4 组)
每组类别也不多(≤6)
你希望读者直接横向比较差异
例如:3 个模型在 4 个数据集上的表现。
优点:信息密集、视觉紧凑、对比直接
缺点:组别一多就开始拥挤、颜色多了就开始难区分
一句话判断:
维度不多 → 分组柱最直接
维度变多 → 分面更清晰
02.分面:让每个组都有自己的空间
分面本质上是把“颜色区分”改成“空间区分”。而空间分离的认知负担远低于颜色识别。
适合场景:
组别多(>4)
你希望每组有完整 y 轴
你希望趋势或结构更清楚
分面最大的优点是 每个子图都有自己的基线,不会互相干扰。
03.一个核心原则:不要同时增加颜色 + 组别 + 类别
一张图里最多控制 2 个视觉变量。超过 2 个维度,就分面。
科研图不是拼信息量,是拼可读性。
04.分组柱
import osimport numpy as npimport matplotlib as mplimport matplotlib.pyplot as pltfrom matplotlib import font_manager as fmfrom matplotlib.ticker import MaxNLocator, FormatStrFormatter# == 字体设置 ==win_fonts = r"C:\Windows\Fonts"for p in [os.path.join(win_fonts, "times.ttf"),os.path.join(win_fonts, "timesbd.ttf"),os.path.join(win_fonts, "timesi.ttf"),os.path.join(win_fonts, "simsun.ttc"),]:if os.path.exists(p):try:fm.fontManager.addfont(p)except Exception:passmpl.rcParams["font.family"] = ["Times New Roman", "SimSun"]mpl.rcParams["axes.unicode_minus"] = FalseOUT_DIR = r"D:\py_figs_grouped"os.makedirs(OUT_DIR, exist_ok=True)# == 示例数据 ==categories = ["Dataset A / 数据集A","Dataset B / 数据集B","Dataset C / 数据集C"]model1 = np.array([0.91, 0.87, 0.89])model2 = np.array([0.88, 0.85, 0.86])model3 = np.array([0.84, 0.83, 0.82])x = np.arange(len(categories))width = 0.25fig, ax = plt.subplots(figsize=(7.2, 4.2))ax.bar(x - width, model1, width, label="Model 1 / 模型1")ax.bar(x, model2, width, label="Model 2 / 模型2")ax.bar(x + width, model3, width, label="Model 3 / 模型3")ax.set_xticks(x)ax.set_xticklabels(categories, fontsize=11)ax.set_ylabel("Accuracy / 准确率", fontsize=12)ax.set_title("分组对比:不同模型在各数据集表现", fontsize=14)ax.set_ylim(bottom=0)ax.yaxis.set_major_locator(MaxNLocator(nbins=6))ax.yaxis.set_major_formatter(FormatStrFormatter("%.2f"))ax.legend(frameon=False)out_path = os.path.join(OUT_DIR, "grouped_bar_basic.jpg")fig.savefig(out_path, dpi=300, bbox_inches="tight", pad_inches=0.05)plt.close(fig)print("Saved:", out_path)

05.分面
当组别多时,用分面更稳:
# ===== 分面示例 =====fig, axes = plt.subplots(1, 3, figsize=(9, 3.8), sharey=True)models = [model1, model2, model3]titles = ["Model 1 / 模型1", "Model 2 / 模型2", "Model 3 / 模型3"]for ax, data, title in zip(axes, models, titles):ax.bar(categories, data)ax.set_title(title, fontsize=12)ax.set_ylim(bottom=0)ax.tick_params(axis='x', rotation=20)ax.yaxis.set_major_locator(MaxNLocator(nbins=5))axes[0].set_ylabel("Accuracy / 准确率", fontsize=12)fig.suptitle("分面表达:空间分离优于颜色堆叠", fontsize=14)fig.savefig(os.path.join(OUT_DIR, "facet_bar_basic.jpg"),dpi=300, bbox_inches="tight", pad_inches=0.05)plt.close(fig)

06.分组柱 vs 分面
分组柱适合“并排比较”,分面适合“分开表达”;当图开始变乱时,不是你数据多了,而是你该换布局了。
下一篇我们聊一个经常被误用的图形:《堆叠柱的误区》。堆叠柱看起来信息量大,但它和堆叠面积一样,常常会让比较变得困难——下一篇我会教你什么时候该用、什么时候一定不要用,以及更稳的替代方案。
——期待你的关注——