第 6 章 饼图、箱线图与更多图表
学习目标
- 会用
ax.pie画占比 - 会用
ax.boxplot画五数概括 - 会用
ax.violinplot画分布形状 - 会用
ax.fill_between填充区间 - 能根据数据形态选择合适的图表类型
6.1 pie:饼图看占比
饼图(pie chart)用扇形面积表达各部分占整体的比例,适合「构成」型数据(各部分相加等于整体)。ax.pie(数值, labels=标签):
import matplotlib.pyplot as plt
import numpy as np
labels = ["房租", "餐饮", "交通", "娱乐", "其他"]
expense = np.array([3500, 2200, 800, 600, 900])
fig, ax = plt.subplots()
ax.pie(expense, labels=labels, autopct="%1.1f%%",
colors=["#11557c", "#ee8c18", "#40c0a6", "#d33f49", "#9ca3af"],
startangle=90, counterclock=False)
ax.set_title("某家庭月度支出构成")
plt.show()图 6-1 真实输出:autopct="%1.1f%%" 在每块上标注百分比;startangle 决定第一块从哪个角度开始;counterclock=False 让数据按顺时针排列:

饼图的适用范围比想象中窄:它只能表达「占比」,且类别一多(超过 5~6 个)就难读。若只是比大小,柱状图(第 5 章)通常更好。
6.2 boxplot:箱线图看分布的五数概括
箱线图(box plot)用一个箱子概括一组数据的分布:最小值、下四分位数(Q1)、中位数、上四分位数(Q3)、最大值这五个数字。箱子是 Q1~Q3,箱中横线是中位数,两端的须延伸到非异常值的最值,须外的点视为离群值(outlier)。
ax.boxplot(数据列表, tick_labels=标签):
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(21)
class_a = rng.normal(75, 10, 40)
class_b = rng.normal(82, 14, 40)
class_c = rng.normal(70, 8, 40)
fig, ax = plt.subplots()
ax.boxplot([class_a, class_b, class_c],
tick_labels=["一班", "二班", "三班"],
patch_artist=True,
boxprops=dict(facecolor="#11557c", alpha=0.6),
medianprops=dict(color="#d33f49", linewidth=2))
ax.set_title("三个班级成绩分布(箱线图)")
ax.set_xlabel("班级")
ax.set_ylabel("成绩(分)")
plt.show()图 6-2 真实输出。一个箱子就传达五维信息,适合「多组数据对比分布」:

解读:二班的箱子最「高」(成绩离散度大),三班最「矮」(成绩集中);几个圆点是离群值。
注意:Matplotlib 3.9 起,boxplot 的 labels 参数更名为 tick_labels。用旧参数 labels= 会报 TypeError,换名字即可。
6.3 violinplot:小提琴图看分布形状
小提琴图(violin plot)是箱线图的「升级版」:它不仅给出五数概括,还用核密度估计画出分布的完整形状——宽的地方数据多,窄的地方数据少。ax.violinplot(数据列表):
fig, ax = plt.subplots()
vp = ax.violinplot([class_a, class_b, class_c], positions=[1, 2, 3], showmedians=True)
ax.set_xticks([1, 2, 3])
ax.set_xticklabels(["一班", "二班", "三班"])
ax.set_title("三个班级成绩分布(小提琴图)")
plt.show()图 6-3 真实输出:showmedians=True 在每把小提琴里标出中位数。与箱线图对比:小提琴图能看出二班成绩「中间高两边低、还带一个左偏的尾巴」,这是箱线图看不到的形状细节:

6.4 fill_between:填充区间
ax.fill_between(x, y1, y2) 在两条曲线之间填充颜色,常用来画「区间」「范围带」「高于阈值的部分」。以 30 天气温为例,填充最高与最低气温之间的区域:
import matplotlib.pyplot as plt
import numpy as np
rng = np.random.default_rng(21)
day30 = np.arange(1, 31)
high = 28 + 6 * np.sin(day30 / 30 * 2 * np.pi) + rng.normal(0, 1.5, 30)
low = 20 + 5 * np.sin(day30 / 30 * 2 * np.pi + 0.5) + rng.normal(0, 1.2, 30)
fig, ax = plt.subplots()
ax.plot(day30, high, color="#d33f49", label="最高气温")
ax.plot(day30, low, color="#11557c", label="最低气温")
ax.fill_between(day30, low, high, color="#9ca3af", alpha=0.3, label="温差区间")
ax.set_title("30 天气温区间")
ax.set_xlabel("日期")
ax.set_ylabel("气温(°C)")
ax.legend()
plt.show()图 6-4 真实输出:灰色带子的高度就是每天的温差,一目了然:

fill_between 的经典用法还有:填满曲线与零轴之间、只填满足条件的区间(where= 参数,参考第 8 章的布尔掩码思路)、画置信区间带。
6.5 怎么选图:数据形态决定图表
| 你想表达 | 数据类型 | 首选图表 |
|---|---|---|
| 趋势随时间变化 | 时间序列 | 折线图(第 3 章) |
| 两个变量的关系 | 两列连续 | 散点图(第 4 章) |
| 类别间比大小 | 类别 + 数值 | 柱状图(第 5 章) |
| 数值的分布 | 一列连续 | 直方图 / 箱线图 / 小提琴图 |
| 部分的占比构成 | 类别 + 占比 | 饼图(占比类目少时) |
| 多组分布对比 | 多组连续 | 箱线图 / 小提琴图 |
| 区间或范围带 | 上下界两列 | fill_between |
判断顺序:先问「横轴是类别还是连续数值」,再问「我想突出趋势、比较、分布还是构成」。
6.6 读懂报错:饼图标签数量不匹配
ax.pie(数值, labels=标签) 要求标签数量与数值完全一致:
>>> ax.pie([1, 2, 3], labels=["a", "b"])
Traceback (most recent call last):
...
File "matplotlib/axes/_axes.py", line 3680, in pie
raise ValueError(f"'labels' must be of length 'x', not {len(labels)}")
ValueError: 'labels' must be of length 'x', not 2解读:3 个数值、2 个标签,一一对应不上。错误信息直接给出两者长度,照改即可。
动手实践
- 画一张你自己月支出的饼图,复现图 6-1。
- 生成三个班级的成绩,画箱线图(注意用
tick_labels而不是labels),复现图 6-2。 - 用同样的数据画小提琴图,与箱线图对比,说出各自能看到什么。
- 画 30 天气温的「最高/最低 + fill_between 区间」图,复现图 6-4。
- 按 6.5 的表格,为以下需求各选一种图表并说明理由:一年 12 个月的销售额变化;全班身高分布;各省 GDP 比较;支出构成。
常见错误
| 错误写法 | 现象 | 原因 |
|---|---|---|
ax.boxplot(..., labels=[...]) | TypeError: got an unexpected keyword argument 'labels' | 3.9 起参数更名为 tick_labels |
ax.pie(x, labels=[...]) 数量不一致 | ValueError: 'labels' must be of length 'x' | 标签必须与数值一一对应 |
| 类别超过 7 个还用饼图 | 图难以阅读 | 类别多改用柱状图 |
violinplot 忘记 showmedians | 没有中位数标记 | 需要时显式开启 |
fill_between(x, y1, y2) 参数写反 | 填充范围错 | 注意是「从 y1 到 y2」 |
| 把饼图数据改成「不是整体」的数据 | 占比无意义 | 饼图只适用于构成型数据 |
章末练习
基础
- 画一张 5 项支出的饼图,标注百分比。
- 生成两组正态数据,画箱线图对比。
- 生成一组数据,画小提琴图。
提高
- 用
fill_between画出sin与cos之间的区域。 - 对比同一组数据的直方图、箱线图、小提琴图,说出三种图各自的优点。
挑战
- 生成两组「一组左偏、一组右偏」的数据,用小提琴图画出,并解释为什么形状不同。
- 给 6.5 的选图表格补上「不推荐/踩坑」一列(比如:饼图类别过多、柱状图用于连续数据等),并各画一张错误示范图说明问题。
章末自测
每题选择一个最佳答案。本书不附答案:完成后交由老师或 AI 老师批改讲解。
- 饼图适合表达?
- A. 时间趋势
- B. 各部分占比
- C. 分布形状
- D. 相关性
- 箱线图的「箱子」范围是?
- A. 最小值到最大值
- B. Q1 到 Q3
- C. 平均值加减标准差
- D. 中位数附近
- 箱线图里箱中的横线代表?
- A. 均值
- B. 中位数
- C. 众数
- D. 最大值
- 箱线图须外的孤立点称为?
- A. 噪声点
- B. 离群值
- C. 空值
- D. 误差点
ax.boxplot里给各组命名的参数(3.9 起)是?- A.
labels - B.
tick_labels - C.
names - D.
legend
- A.
- 与箱线图相比,小提琴图额外能看出?
- A. 数值大小
- B. 分布的完整形状
- C. 数据总量
- D. 颜色
- 填充两条曲线之间的区域用?
- A.
ax.fill() - B.
ax.fill_between(x, y1, y2) - C.
ax.area(x, y1, y2) - D.
ax.hatch()
- A.
- 想对比 4 个班级的成绩分布,首选?
- A. 饼图
- B. 箱线图/小提琴图
- C. 折线图
- D. 散点图
autopct="%1.1f%%"的作用是?- A. 设置饼图半径
- B. 在扇区标注百分比
- C. 设置颜色
- D. 设置旋转角度
- 想表达一年 12 个月销售额的变化趋势,首选?
- A. 饼图
- B. 折线图
- C. 箱线图
- D. 小提琴图
