在互联网上检索 Matplotlib 教程时,你往往会看到满篇的 plt.plot() 和 plt.title()。这种被称为“状态机接口”的写法虽然简短,但在处理复杂的工程图表时,极易导致标题错位、图例混乱。
作为未来的安全工程师,面对矿井灾害预警监控、岩石力学实验数据分析等多维度的信息展示要求,我们必须从一开始就建立起严谨的面向对象(Object-Oriented, OOP)绘图思维。本节将带你深入剖析 Matplotlib 最核心的两个对象:图形(Figure)与坐标系(Axes)。
1. 核心隐喻:画板与图纸#
在面向对象的绘图范式中,所有的图表都不是凭空出现的,而是由一个个具体的“对象”层层嵌套组合而成。为了便于理解,我们可以借用传统工程制图中的概念:
- Figure(图形 / 画布): 你可以把它想象成工程师桌面上的一块木制画板,或者最终导出的那一整张 PDF 纸。它是一个最顶层的容器,本身并不显示任何数据,只负责设定图表的整体大小、分辨率(DPI)以及背景颜色。
- Axes(坐标系): 你可以把它想象成用图钉钉在画板上的一张带刻度的工程方格纸。它是真正用于绘图的区域。一张画板(Figure)上可以只钉一张图纸,也可以钉上好几张图纸(多子图排版)。我们在图表上看到的所有折线、散点、网格、标题,全都是画在这个特定的 Axes 对象里的。
⚠️ 核心易错点提示:
Axes(坐标系)与Axis(坐标轴)在英文拼写上仅一字之差,但概念完全不同。Axes是一整幅包含上下左右边界的二维(或三维)空间容器;而Axis仅仅是构成这个容器边界的数轴(如水平的 x 轴和垂直的 y 轴)。
标准起手式:plt.subplots()#
在面向对象的风格中,我们绘图的第一步永远是“拿画板,铺图纸”。Matplotlib 提供了一个极其好用的“工厂函数” plt.subplots() 来一次性完成这个动作。
import matplotlib.pyplot as plt
# 创建一个图形对象 fig,以及附着在上面的坐标系对象 ax
fig, ax = plt.subplots(figsize=(8, 6))
在这行代码中:
plt.subplots()就像是一个流水线机器,它默认生产出一块画板和一张图纸,并将它们打包返回。- 我们利用 Python 的多变量赋值特性,用
fig接收了 Figure 对象,用ax接收了 Axes 对象。 figsize=(8, 6)参数指定了整个画板的物理尺寸(宽 8 英寸,高 6 英寸),这在撰写学术论文需要精确控制图表大小时非常有用。
操作坐标系对象(Axes)#
一旦获得了 ax 对象,我们的角色就变成了手握画笔的工程师,所有的绘制动作都是在向 ax 下达指令。
在面向对象语法中,设置图表属性的方法通常都带有 set_ 前缀,这与传统的 Pyplot 风格有显著区别。请牢记以下常用的对象方法:
-
画图指令:
ax.plot()(画折线)、ax.scatter()(画散点)、ax.bar()(画柱状图)。 -
设置文本:
-
ax.set_title():设置图表上方的标题。 -
ax.set_xlabel()/ax.set_ylabel():设置坐标轴标签。 -
设置数值范围:
-
ax.set_xlim()/ax.set_ylim():强制限定坐标轴的数据显示范围。 -
开启网格与图例:
-
ax.grid():开启背景网格线。 -
ax.legend():根据画线时指定的label生成图例。
综合工程案例:岩石单轴压缩应力-应变曲线#
为了将上述概念融会贯通,我们来绘制一张标准的岩石力学实验图表——单轴压缩过程中的应力-应变曲线。
在这个案例中,我们将运用上一节学到的跨平台中文字体防御机制与学术级 MathText 数学公式渲染,并且展示如何通过操纵对象的边框(Spine),打造出符合核心期刊审美的“L型”半开放坐标系(即隐藏顶部和右侧的边框)。
请仔细阅读并运行以下代码体会面向对象的优势:
import matplotlib.pyplot as plt
# --- 1. 学术与工程环境标准配置 ---
plt.rcParams["font.family"] = "serif"
plt.rcParams["font.serif"] = ["SimSun", "Songti SC", "Noto Serif SC"] # 跨平台中文防御列表
plt.rcParams["axes.unicode_minus"] = False
plt.rcParams["mathtext.fontset"] = "stix" # 完美渲染物理符号
# --- 2. 准备岩石力学实验数据 ---
# 应变 epsilon (10^-3) 和 正应力 sigma (MPa)
strain = [0, 1.2, 2.5, 3.8, 4.5, 5.0, 5.2]
stress = [0, 15, 38, 65, 82, 75, 60]
# --- 3. 创建对象:实例化图形与坐标系 ---
fig, ax = plt.subplots(figsize=(7, 5))
# --- 4. 面向对象绘图与细节设置 ---
# 绘制主曲线
ax.plot(strain, stress, color='#1f77b4', linewidth=2, marker='s', label="砂岩试样 A")
# 使用 set_ 系列方法设置文本(完美混排汉字与 LaTeX 物理符号)
ax.set_title(r"岩石单轴压缩实验曲线", fontsize=14, pad=15)
ax.set_xlabel(r"轴向应变 $\varepsilon$ ($10^{-3}$)", fontsize=12)
ax.set_ylabel(r"正应力 $\sigma$ ($\mathrm{MPa}$)", fontsize=12)
# --- 5. 进阶对象操作:边框 (Spine) 定制 ---
# 在高级学术图表中,通常会去掉右侧和顶部的封闭边框线
ax.spines['top'].set_visible(False) # 隐藏顶部边框
ax.spines['right'].set_visible(False) # 隐藏右侧边框
# 开启网格与图例
ax.grid(True, linestyle='--', alpha=0.6)
ax.legend(loc='upper left', frameon=False) # 图例放在左上角,并取消图例自带的边框
# --- 6. 渲染输出 ---
plt.show()
为什么这种思维如此重要?#
当你只画一条线时,面向对象的写法似乎比 plt.plot() 多写了几行代码。但试想一下:在实际的安全工程评价中,你可能需要在一张画板(Figure)上,同时贴上“瓦斯涌出量时序图”、“工作面应力分布图”、“微震能量频次图”三张图纸(Axes)。
如果使用传统的 plt.title(),程序根本不知道你要把标题加在哪怕一张图纸上,只能盲目地加在最后操作的那张图上,导致灾难性的错位。而使用面向对象的方法,由于每个坐标系都被赋予了明确的变量名(如 ax1, ax2, ax3),你可以精准地向 ax2 下达画图指令,向 ax3 下达修改边框指令。这种将复杂系统拆解为独立控制对象的计算思维,正是我们后续进行大规模数字化建模的基石。