Python 数据可视化 · 第 1 章

matplotlib 绘图入门 从画布、坐标系到第一张折线图

本章只做一件事:用 matplotlib 把一组数字画成图。看懂了 Figure、Axes 与 add_subplot 三者的关系,后面所有图表——柱形图、饼图、散点图——都只是换了画法,骨架完全一致。

对应 1.4 节 numpy + matplotlib 面向对象 / pyplot 两种写法
01

一张图的三层结构

matplotlib 把“画图”拆成了三个层次的对象。教材代码里的 fig 与 ax, 对应的正是最外层的画布与内部的坐标系。

Figure(画布,fig) Axes(坐标系,ax) fig = plt.figure() 创建占满窗口的整块画布 ax = fig.add_subplot(223) 在画布上划出一块坐标系区域 ax.plot(data) 把数据画进这块坐标系,而不是画布本身
图 1 Figure 是整块画布,Axes 是画布内的一块绘图区域,曲线属于 Axes
FIGURE

画布本体

一张完整的图片,可容纳多个坐标系,有尺寸与背景色。

AXES

坐标系

真正承载曲线、柱形、刻度和标题的区域,一张画布上可以有多个。

ARTIST

图形元素

曲线、点、文字等具体图形元素,都挂在某个 Axes 上。

02

固定四步:导入、准备、绘制、显示

第 1 章的两个示例都遵循同一顺序。先导入库,再准备数据,然后创建画布与坐标系,最后调用 plt.show() 显示。 任何一张 matplotlib 图表,都可以套进这个模板。

环境准备:若在 Jupyter 中首次使用,先安装依赖。教材用 %pip 魔法命令,保证包装进当前内核:
%pip install numpy --disable-pip-version-check
%pip install matplotlib --disable-pip-version-check
03

写法一:面向对象方式

先建画布 fig,再在画布上添加坐标系 ax,所有绘制动作都作用在 ax 上。 这是控制力最强、最适合多子图的方式。

PYTHON
import numpy as np
import matplotlib.pyplot as plt
data = np.array([1, 2, 3, 4, 5])        # 准备数据
fig = plt.figure()                      # 创建代表画布的 Figure 类的对象 fig
ax = fig.add_subplot(223)               # 在画布 fig 上添加坐标系风格的绘图区域 ax
ax.plot(data)                           # 绘制图表
plt.show()                              # 展示图表
面向对象方式绘制的折线图,图表位于 2×2 网格的左下角
运行结果:曲线出现在画布左下角(2×2 网格的第 3 格)

为什么图出现在左下角?

add_subplot(223) 的三个数字不是随便写的:前两位 2、2 表示把画布切成 2 行 2 列,第三位 3 表示使用 按行优先编号的第 3 格——即左下角。其余三格留白,所以图片看起来 “空出了一大块”。

若只想要一张铺满画布的图,把参数写成 111 即可:1 行 1 列第 1 格, 这与直接调用 plt.plot() 的默认效果一致。

04

动手:子图编号怎么对应位置

点击下面 2×2 网格中的任意一格,看 add_subplot 的参数如何随之变化。 编号规则是 先从左到右,再从上到下。

当前代码

ax = fig.add_subplot(223)

第 3 格:2 行 2 列中的左下角。这也是教材示例的位置,因此运行时图表偏左下。

05

写法二:pyplot 快速绘图

pyplot 模块会自动维护“当前画布”和“当前坐标系”,因此可以省略 fig 与 ax, 一行 plt.plot(data) 直接出图。适合快速验证与单图场景。

PYTHON
import numpy as np
import matplotlib.pyplot as plt     # 导入 pyplot 模块
data = np.array([1, 2, 3, 4, 5])     # 准备数据
plt.plot(data)                       # 在当前画布的绘图区域中绘制图表
plt.show()                           # 展示图表
pyplot 方式绘制的折线图,图表占满默认画布
运行结果:单个坐标系铺满画布,无留白

注意两次运行的差异:数据完全相同,画法完全相同,但前者图表只占左下 1/4 面积。 差别不在数据,而在 add_subplot 给出的位置参数。这正是本章要建立的空间感。

06

两种写法怎么选

对比项面向对象方式pyplot 方式
核心语句 fig = plt.figure()
ax = fig.add_subplot(223)
ax.plot(data)
plt.plot(data)
绘制对象 明确作用于某个 Axes,可分别控制每个子图 作用于“当前” Axes,由 pyplot 隐式维护
多子图支持 强,一张画布可放 2×2、2×3 等布局 需配合 plt.subplot(),控制较弱
代码量 略多,但结构清晰 最少,适合快速出图
适用场景 正式图表、多图对比、精细化调整 探索性分析、单图、教学演示
建议:初学阶段两种都写一遍,确认输出一致;进入第 3 章做复合图表时,统一改用面向对象方式, 便于对每个 Axes 单独设置标题、刻度和图例。
07

四个常见易错点

01

忘记 plt.show()。在脚本文件中运行若没有这句话,图只存在于内存,窗口不会弹出; 在 Jupyter 中末尾不写也能显示,但教材保留该句以保证脚本与笔记本行为一致。

02

默认画布是 640×480 像素。未设置 figsize 时,matplotlib 采用 6.4×4.8 英寸、 100 dpi,即 640×480。看到图偏小或偏方,属正常现象,可通过 figsize=(8, 5) 调整。

03

把 add_subplot 的参数当成坐标。223 是 2 行 2 列第 3 格, 不是 x=2、y=2、编号 3 的坐标。写成 231 则是 2 行 3 列的第 1 格,图形会变窄。

04

中文标题显示为方框。matplotlib 默认字体不含中文字形,需在绘图前设置 plt.rcParams['font.sans-serif'] = ['SimHei'],并关闭负号乱码 plt.rcParams['axes.unicode_minus'] = False。第 4 章会系统讲解字体设置。