Skip to content

第 1 章 认识 Matplotlib

学习目标

  • 知道 Matplotlib 是什么、解决什么问题
  • 完成 Matplotlib 的安装并验证版本
  • 会用 import matplotlib.pyplot as plt 绘制并保存第一张图
  • 解决中文与负号显示问题
  • 认识一张图的骨架:Figure、Axes、标题、标签、刻度
  • 读懂第一条报错

1.1 Matplotlib 是什么

Matplotlib 是 Python 生态中最基础、使用最广的 2D 绘图库,核心能力是把数据变成图形:折线图、散点图、柱状图、直方图、饼图等。它解决一个具体问题:数据是数字,人眼不擅长读数字,擅长看图。一张图能在一瞬间传递表格里几千个数字才能表达的信息。

它有两个常用的入口:

  • matplotlib.pyplot:一个「状态机」风格的模块,约定简写为 plt。你用 plt.plotplt.title 一步步往「当前这张图」上添加内容,适合快速画图。
  • 面向对象接口:直接操作 Figure(画布)和 Axes(坐标系)对象,适合复杂布局与精细控制。

本书两种写法都会用:前几章先用 pyplot,第 2 章正式讲清对象体系,之后以面向对象写法为主。所有示例基于 Matplotlib 3.x(写作时最新为 3.11.1),代码在交互环境(REPL)或脚本文件中运行,约定运行方式用 uv run python(见 1.2)。

1.2 安装 Matplotlib

1.2.1 用 uv 安装(推荐)

本书配套仓库使用 uv 管理 Python 环境。在项目根目录:

$ uv sync

完成后验证:

$ uv run python -c "import matplotlib; print(matplotlib.__version__)"
3.11.1

1.2.2 用 pip 安装

如果你已有自己的 Python 环境,直接:

$ pip install matplotlib
$ python3 -c "import matplotlib; print(matplotlib.__version__)"

输出 3.x.x 即安装成功。后续所有示例,把 uv run python 换成你自己的 python3 即可。

1.3 导入 pyplot 与第一张图

导入是用 import 语句把库加载进当前程序。Matplotlib 社区约定统一简写为 plt:

python
import matplotlib.pyplot as plt

绘图从数据开始。假设你记录了某地一周每天的最高气温(单位 °C):

python
import matplotlib.pyplot as plt

days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
temps = [28, 31, 33, 30, 29, 32, 34]

plt.plot(days, temps)
plt.title("Daily Max Temperature")
plt.xlabel("Weekday")
plt.ylabel("Temperature (C)")
plt.show()

plt.plot(x, y)xy 一一对应连成一条折线;plt.title 设置标题;plt.xlabel / plt.ylabel 设置两个坐标轴的标签。plt.show() 在交互环境弹出图形窗口。

图 1-1 就是上面代码的真实输出(本书把 plt.show() 换成 plt.savefig() 后保存,用于嵌进正文):

图 1-1 第一张折线图

观察这张图:横轴是星期,纵轴是气温,一条折线把 7 个点连起来。周四是低谷、周日是高峰——图比 [28, 31, 33, 30, 29, 32, 34] 这串数字直观得多。这就是 Matplotlib 的价值。

注意 plt.plot 的第一个参数如果省略,默认用 0, 1, 2, ... 作横坐标,只画 y:

python
import matplotlib.pyplot as plt

temps = [28, 31, 33, 30, 29, 32, 34]
plt.plot(temps)
plt.show()

1.4 中文与负号:必须解决的第一步

上面的图用的是英文标签,因为 Matplotlib 的默认字体 DejaVu Sans 不含中文字形。直接用中文标签,会得到一个个「方框」(tofu):

python
import matplotlib.pyplot as plt

days = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
temps = [28, 31, 33, 30, 29, 32, 34]

plt.plot(days, temps)
plt.title("一周最高气温")
plt.xlabel("星期")
plt.ylabel("气温(°C)")
plt.show()

真实输出(注意标题与坐标轴标签处的方框):

图 1-2 未配置中文字体,中文变成方框

解决办法是告诉 Matplotlib 使用一个含中文字形的字体。用 rcParams(全局配置,第 11 章展开)把中文字体加进「无衬线字体候选列表」:

python
import matplotlib.pyplot as plt

plt.rcParams["font.sans-serif"] = ["Noto Sans CJK SC", "DejaVu Sans"]
plt.rcParams["axes.unicode_minus"] = False

days = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
temps = [28, 31, 33, 30, 29, 32, 34]

plt.plot(days, temps)
plt.title("一周最高气温")
plt.xlabel("星期")
plt.ylabel("气温(°C)")
plt.show()

axes.unicode_minus = False 是配套设置:默认情况下负号会用一个特殊 Unicode 字符渲染,在多数中文字体里同样会变成方框;设为 False 后改用普通减号。图 1-3 是配置后的真实输出:

图 1-3 配置中文字体后正常显示

从本章起,所有示例都默认先做这两行配置(本书仓库自带 Noto Sans CJK SC 字体,_common 脚本已统一设置)。在你自己的电脑上,把字体名换成系统里实际存在的中文字体即可,例如 macOS 的 "PingFang SC"、Windows 的 "Microsoft YaHei"

1.5 一张图的骨架:Figure 与 Axes

不管多复杂的图,骨架都是同一个:一张 Figure,里面有至少一个 Axes

Figure(画布)是整张图的外壳,包含背景、标题栏、所有坐标系与图例;Axes(坐标系)是真正画数据、带坐标轴和刻度的那块区域。plt.plot 这类 pyplot 函数会「自动」在当前的 Figure 里创建/复用当前 Axes,所以前面的代码没提这两个概念也能画出来——但理解它们之后,你才能控制复杂的布局。

图 1-4 标注了这张图的各个部分:

图 1-4 一张图的骨架:Figure、Axes、标题、标签、刻度

各部分的名字与作用:

部分作用
Figure(画布)整张图的外壳,可以包含多个 Axes(第 9 章)
Axes(坐标系)画数据的区域,含坐标轴、刻度、网格
标题(title)说明这张图讲什么
x / y 轴标签说明横轴、纵轴各代表什么量、什么单位
刻度(tick)坐标轴上的数值记号与标注
数据线plot 画的折线/散点等

1.6 保存图片:savefig 初见

交互环境用 plt.show() 弹窗;要让图片脱离程序存在(写报告、发网页),用 plt.savefig(路径) 把当前图保存成文件:

python
import matplotlib.pyplot as plt

plt.rcParams["font.sans-serif"] = ["Noto Sans CJK SC", "DejaVu Sans"]
plt.rcParams["axes.unicode_minus"] = False

days = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
temps = [28, 31, 33, 30, 29, 32, 34]

plt.plot(days, temps, marker="o")
plt.title("一周最高气温")
plt.xlabel("星期")
plt.ylabel("气温(°C)")
plt.grid(True, alpha=0.3)
plt.savefig("week_temperature.png")
print("已保存")

真实输出:

已保存

并在当前目录生成 week_temperature.png,图 1-5 就是保存出的文件:

图 1-5 保存出的图片文件

savefig 支持 PNG、PDF、SVG、EPS 等格式,也支持控制分辨率与空白边距——这是第 10 章的完整主题,这里先会用即可。

1.7 读懂第一条报错

plt.plot 最经典的报错是x 与 y 长度不一致。假设 x 有 7 个元素、y 只有 5 个:

>>> import matplotlib.pyplot as plt
>>> plt.plot([1, 2, 3, 4, 5, 6, 7], [10, 12, 11, 14, 13])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
    ...
  File "matplotlib/axes/_axes.py", line 1792, in plot
    lines = [*self._get_lines(self, *args, data=data, **kwargs)]
  ...
ValueError: x and y must have same first dimension, but have shapes (7,) and (5,)

解读:

  • 错误类型是 ValueError(值错误)。
  • 关键信息在最后一行:x and y must have same first dimension, but have shapes (7,) and (5,)(x 与 y 的第一维必须相同,但形状分别是 (7,) 和 (5,))。折线图是逐点连线,x[i]y[i] 必须一一对应,长度必须一致。

读 Matplotlib 报错三步:

  1. 最后一行:错误类型 + 一句话说明。
  2. 中间的行:出错的是哪条语句、哪个文件(通常是 matplotlib/... 内部调用链)。
  3. 核对你传给函数的两个序列长度:用 print(len(x), len(y)) 确认。

动手实践

  1. 在项目根目录运行 uv run python 进入交互环境,验证 matplotlib.__version__
  2. plt.plot 画出你自己的一周睡眠时长(7 个数字),设置英文标题与轴标签,plt.show() 查看。
  3. 把第 2 步的标签换成中文,先不配置字体运行一次,观察方框;再按 1.4 配置字体后运行,对比前后。
  4. plt.savefig("sleep.png") 保存第 3 步的图,确认文件生成并可以打开。
  5. 故意让 plt.plot([1, 2, 3], [1, 2]) 运行,用「读报错三步」说出:错误类型是什么、两个序列的长度各是多少。

常见错误

错误写法现象原因
中文标签直接画标题/标签变成方框默认字体 DejaVu Sans 不含中文字形;需配置 font.sans-serif
负号显示成方框坐标轴负号异常需设置 axes.unicode_minus = False
plt.plot([1,2,3], [1,2])ValueError: x and y must have same first dimension两个序列长度必须一致
import matplotlib 后直接 plot(...)NameError: name 'plot' is not defined绘图函数在 pyplot 子模块,需 import matplotlib.pyplot as plt
忘写 plt.show()(交互环境)没有窗口弹出交互环境需显式调用 plt.show() 显示图形
在无图形界面的服务器上 plt.show()ImportError / 无反应服务器没有显示后端;改用 plt.savefig() 保存文件

章末练习

基础

  1. 写一段代码:画一条 5 个点的折线 [3, 1, 4, 2, 5],设置标题与两个轴标签,并保存为 line.png
  2. 在交互环境打印 matplotlib.__version__,确认安装。
  3. 用中文标题画一张图,并完成 1.4 的两行字体配置。

提高

  1. 解释 FigureAxes 的区别,并用一句话说明 plt.plot 是怎么「自动」找到画布和坐标系的。
  2. 画两张对比图:一张用 plt.plot(temps)(只传一个参数),一张用 plt.plot(days, temps),观察横坐标的区别,并解释 plot 单参数时的默认行为。

挑战

  1. 不运行代码,预测 plt.plot([1, 2, 3], [1, 2, 3, 4]) 会发生什么,再运行验证,并用「读报错三步」解释错误信息。
  2. plt.show() 换成 plt.savefig() 保存同一张图,再用 os.path.getsize("文件名.png") 查看文件大小;思考为什么同一张图保存两次大小可能不同。

章末自测

每题选择一个最佳答案。本书不附答案:完成后交由老师或 AI 老师批改讲解。

  1. Matplotlib 绘图最常用的导入写法是?
    • A. import matplotlib
    • B. import matplotlib.pyplot as plt
    • C. from matplotlib import *
    • D. import pyplot
  2. 下面哪个函数用于「弹出窗口显示图形」?
    • A. plt.save()
    • B. plt.show()
    • C. plt.plot()
    • D. plt.print()
  3. 整张图的外壳(可包含多个绘图区域)叫什么?
    • A. Axes
    • B. Figure
    • C. Canvas
    • D. Plot
  4. 真正绘制数据、带坐标轴与刻度的区域叫什么?
    • A. Figure
    • B. Axes
    • C. Grid
    • D. Legend
  5. 中文字体问题的解决方法是?
    • A. 把文字全部改成拼音
    • B. 配置 rcParams["font.sans-serif"] 为含中文字形的字体
    • C. 升级 Matplotlib
    • D. 改用 plotly
  6. axes.unicode_minus = False 的作用是?
    • A. 关闭所有文字
    • B. 让负号正常显示
    • C. 只显示正数
    • D. 加快绘图
  7. plt.plot([1, 2, 3], [1, 2]) 会?
    • A. 自动补齐为相同长度
    • B. 报 ValueError
    • C. 只画前两个点
    • D. 返回 None
  8. 把图保存为 PNG 文件,应使用?
    • A. plt.show()
    • B. plt.savefig("a.png")
    • C. plt.export("a.png")
    • D. plt.write("a.png")
  9. plt.plot([3, 1, 4, 1, 5]) 只传一个参数时,横坐标默认是?
    • A. 从 1 开始的整数
    • B. 从 0 开始的整数
    • C. 随机数
    • D. 空数组
  10. 关于 Matplotlib 的两种接口,正确的说法是?
    • A. pyplot 是面向对象接口
    • B. pyplot 是状态机风格,面向对象接口操作 Figure/Axes
    • C. 两者不能混用
    • D. 面向对象接口不能保存图片