第 5 章 柱状图与直方图:bar 与 hist
学习目标
- 会用
ax.bar画分类数据的柱状图 - 会用
ax.barh画横向柱状图 - 用
yerr添加误差条 - 掌握并列柱状图与堆叠柱状图
- 用
ax.hist画连续数据的分布 - 理解柱状图(bar)与直方图(hist)的区别
5.1 bar:分类数据的柱状图
柱状图(bar chart)用柱子的高度表达分类数据的数值,横轴是类别(离散)、纵轴是数值。ax.bar(x, 高度):
import matplotlib.pyplot as plt
import numpy as np
classes = ["一班", "二班", "三班", "四班"]
scores = np.array([82, 87, 79, 91])
fig, ax = plt.subplots()
bars = ax.bar(classes, scores, color="#11557c", width=0.6)
ax.bar_label(bars, padding=3)
ax.set_title("四个班平均成绩")
ax.set_xlabel("班级")
ax.set_ylabel("平均成绩(分)")
ax.set_ylim(0, 100)
plt.show()图 5-1 真实输出。width 控制柱宽(默认 0.8);ax.bar_label(bars) 在柱顶标出数值,是 Matplotlib 3.4 起的内置功能:

柱状图适合「比大小」:一眼看出四班最高、三班最低。横轴是类别时,类别本身没有顺序意义,只是分组标签。
5.2 barh:横向柱状图
类别名很长、或想按数值排序展示时,横向柱状图更合适。ax.barh(类别, 数值) 把类别放纵轴、数值放横轴:
import matplotlib.pyplot as plt
import numpy as np
cities = ["重庆", "上海", "北京", "广州", "成都", "深圳"]
pops = np.array([3213, 2487, 2185, 1881, 2140, 1768])
order = np.argsort(pops) # 按人口升序排列下标
fig, ax = plt.subplots()
bars = ax.barh(np.array(cities)[order], pops[order], color="#ee8c18", height=0.6)
ax.bar_label(bars, padding=3)
ax.set_title("主要城市常住人口(万人)")
ax.set_xlabel("人口(万人)")
plt.show()图 5-2 真实输出:np.argsort 拿到「从小到大」的下标顺序,配合 barh 得到一条整齐的排行——这是排行榜的标准画法:

barh 的对应参数是 height(柱高/厚度),而不是 width。
5.3 误差条:yerr
科学图表常需要表达「测量值 ± 不确定度」。yerr 参数在柱顶加误差条,capsize 控制误差条两端的横线:
import matplotlib.pyplot as plt
import numpy as np
methods = ["方法 A", "方法 B", "方法 C"]
mean = np.array([5.2, 6.8, 4.9])
std = np.array([0.5, 0.8, 0.6])
fig, ax = plt.subplots()
ax.bar(methods, mean, yerr=std, capsize=6, color="#40c0a6", alpha=0.9)
ax.set_title("三种方法的平均耗时(含标准差)")
ax.set_xlabel("方法")
ax.set_ylabel("耗时(秒)")
ax.set_ylim(0, 9)
plt.show()图 5-3 真实输出:方法 B 平均最快(柱子最矮),但它的误差条也最长,说明结果最不稳定——这就是误差条传达的信息:

yerr 是数组时逐根柱子对应;误差条还支持「非对称」(上下不同),第 10 章前先用最简单的形式。
5.4 并列与堆叠:对比与构成
并列柱状图在同一个类别里并排放几根柱子,适合「同一类别下的分组对比」。做法是给每组柱子一个横向偏移:
import matplotlib.pyplot as plt
import numpy as np
classes = ["一班", "二班", "三班", "四班"]
sem1 = np.array([78, 84, 80, 88])
sem2 = np.array([82, 89, 81, 92])
x = np.arange(len(classes))
w = 0.36
fig, ax = plt.subplots()
b1 = ax.bar(x - w/2, sem1, width=w, label="上学期", color="#11557c")
b2 = ax.bar(x + w/2, sem2, width=w, label="下学期", color="#ee8c18")
ax.set_xticks(x)
ax.set_xticklabels(classes)
ax.set_title("四个班两个学期平均成绩对比")
ax.set_ylabel("平均成绩(分)")
ax.legend()
plt.show()图 5-4 真实输出:每班两根柱子并排,x - w/2 与 x + w/2 让两组柱子以类别位置为中心对称分布:

堆叠柱状图把多个分量在同一根柱子上「叠起来」,适合看「总量 + 构成」。用 bottom 参数指定上一段从哪里开始:
import matplotlib.pyplot as plt
import numpy as np
quarters = ["一季度", "二季度", "三季度", "四季度"]
product_a = np.array([30, 42, 35, 50])
product_b = np.array([20, 25, 30, 28])
product_c = np.array([15, 18, 22, 25])
x = np.arange(len(quarters))
fig, ax = plt.subplots()
ax.bar(x, product_a, label="产品 A", color="#11557c")
ax.bar(x, product_b, bottom=product_a, label="产品 B", color="#ee8c18")
ax.bar(x, product_c, bottom=product_a + product_b, label="产品 C", color="#40c0a6")
ax.set_xticks(x)
ax.set_xticklabels(quarters)
ax.set_title("四个季度销售额构成")
ax.set_xlabel("季度")
ax.set_ylabel("销售额(万元)")
ax.legend()
plt.show()图 5-5 真实输出:每根柱子的总高度是三个产品之和,分段颜色展示构成:

5.5 hist:连续数据的分布
直方图(histogram)把连续数据按数值范围分桶(bin),统计每个桶里的样本数,画出「分布」的形状。ax.hist(数据, bins=桶数):
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(5)
grades = np.clip(rng.normal(78, 12, 60), 40, 100)
fig, ax = plt.subplots()
ax.hist(grades, bins=8, color="#11557c", alpha=0.8, edgecolor="white")
ax.set_title("60 名学生成绩分布")
ax.set_xlabel("成绩(分)")
ax.set_ylabel("人数")
plt.show()图 5-6 真实输出:bins 控制分桶个数,edgecolor="white" 给每根柱描白边让分界清晰:

柱状图与直方图的本质区别:柱状图的横轴是类别(离散、无大小顺序),直方图的横轴是数值区间(连续、有大小顺序)。所以柱状图柱子可以任意排列,直方图柱子必须按数值从小到大排,且相邻柱子之间没有间隙(或只有很窄的间隙)。
5.6 读懂报错:误差条长度不匹配
yerr 必须与 y 等长。y 有 3 根柱子、yerr 只有 2 个值时:
>>> ax.bar([1, 2, 3], [4, 5, 6], yerr=[1, 2])
Traceback (most recent call last):
...
ValueError: 'yerr' (shape: (2,)) must be a scalar or a 1D or (2, n) array-like
whose shape matches 'y' (shape: (3,))解读:错误信息直接告诉你两件事——yerr 的形状是 (2,),y 的形状是 (3,),必须匹配。修法:让 yerr 与柱子一一对应,或者给所有柱子同一个标量误差。
动手实践
- 用
ax.bar画 5 门课程的平均分柱状图,并加bar_label。 - 用
ax.barh画 6 个城市人口排行(参考 5.2)。 - 画带误差条的柱状图(3 组均值 ± 标准差),复现图 5-3。
- 用并列柱状图对比两个学期的成绩,用堆叠柱状图展示季度销售额构成。
- 生成 100 个正态随机数,用
ax.hist画直方图,分别试bins=5、bins=20,观察形状差异。
常见错误
| 错误写法 | 现象 | 原因 |
|---|---|---|
ax.bar(x, y, yerr=[1, 2]) 且 x 有 3 项 | ValueError: 'yerr' ... shape matches 'y' | yerr 长度必须与柱子数一致 |
用 ax.plot 画分类数据 | 折线把不相关的类别连起来 | 类别数据用 bar,连续数据用 plot/hist |
忘记 ax.set_xticks(x); set_xticklabels(...) | 刻度是 0,1,2 | 数值横轴要手动换成类别标签 |
barh 用了 width | 柱子太粗/异常 | barh 用 height 控制柱厚 |
| 直方图柱子之间有宽间隙 | 观感不对 | hist 相邻桶无间隙是特征;有间隙的是 bar |
hist 数据里有 NaN | 图形缺一块 | 先清洗数据(见第 8 章布尔掩码思路) |
章末练习
基础
- 画一张 4 门课平均分的柱状图,加数值标签。
- 画一张横向柱状图,展示你一周 7 天每天的学习时长。
- 生成 200 个正态随机数,画
bins=10的直方图。
提高
- 用并列柱状图对比两个学期的各科平均分,加图例。
- 用堆叠柱状图展示某公司四个季度三个产品的销售额构成。
挑战
- 用
yerr画「不同样本量下的均值估计」误差条,说明样本量越大误差条越短的统计直觉。 - 判断以下情形分别该用哪种图,并各画一张:(a) 比较各省 GDP;(b) 全班成绩分布;(c) 销售额的年度构成;(d) 两学期的学科对比。
章末自测
每题选择一个最佳答案。本书不附答案:完成后交由老师或 AI 老师批改讲解。
- 画柱状图的函数是?
- A.
ax.plot - B.
ax.bar - C.
ax.hist - D.
ax.scatter
- A.
- 柱状图的横轴数据是?
- A. 连续数值
- B. 类别
- C. 时间戳
- D. 概率
- 横向柱状图的函数是?
- A.
ax.bar - B.
ax.barh - C.
ax.hbar - D.
ax.bar_left
- A.
- 在柱状图上加误差条用?
- A.
error=y - B.
yerr=std - C.
std=True - D.
capsize
- A.
- 并列柱状图让两组柱子并排的关键是?
- A. 修改柱宽
- B. 给 x 位置加偏移
- C. 加图例
- D. 用
bottom
- 堆叠柱状图让第二段从第一段顶部开始用?
- A.
yerr - B.
bottom= - C.
width= - D.
offset=
- A.
- 直方图适合表达?
- A. 类别比较
- B. 连续数据的分布
- C. 两个变量的相关性
- D. 时间趋势
ax.hist(data, bins=8)中bins指?- A. 柱子颜色
- B. 分桶个数
- C. 数据点数
- D. 坐标轴范围
- 柱状图与直方图的本质区别是?
- A. 颜色不同
- B. 横轴是类别还是数值区间
- C. 一个能加图例一个不能
- D. 没有区别
ax.bar_label(bars)的作用是?- A. 设置柱状图标题
- B. 在柱顶标出数值
- C. 修改柱子颜色
- D. 添加图例
