python绘图库-matplotlib

一、安装与初识matplotlib

以下内容是自己入门matplotlib与简单使用matplotlib的记录。

Matplotlib 是 Python 中最核心、最老牌的数据可视化(绘图)基础库。 它就像是 Python 世界里的“数字画笔”,能够将 NumPy 数组或 Pandas 抽象的数据,转化为折线图、柱状图、散点图、3D 图等各种专业的静态、动态或交互式图表。它的设计理念深受 MATLAB 的影响(其核心子库 pyplot 的语法与 MATLAB 高度相似),由于其功能极其强大、底层控制极其精细,如今它已成为大模型特征分析、医疗数据工程以及科学计算领域中不可或缺的画图利器。

虽然 Matplotlib 的名字和日常使用中大多数的场景都集中在平面二维(2D)图表上,但它其实完全具备绘制三维(3D)图形的能力。Matplotlib 内置了一个叫做 mplot3d 的工具包,不需要额外安装任何第三方库,就能直接绘制出各种 3D 图表。


在Jupyter中可以直接显示使用Matplotlib绘制的图形,方便学习与查看效果。 官网:https://matplotlib.org/index.html 案例:https://matplotlib.org/gallery/index.html 官方所有示例源码:https://matplotlib.org/stable/tutorials/index.html 项目的github地址:https://github.com/matplotlib/matplotlib

1.1 安装matplotlib

本文以 python 3.11.9、`matplotlib 3.11.0 为环境来描述。以下内容中涉及所述的stable版本matplotlib就是指2026年6月11日发布的matplotlib v3.11,它为当前最新稳定版。

1
2
3
4
5
6
7
8
9
# 以在conda 中安装的Python 3.11.9环境为例
C:\Users>conda activate agent_py3119
(agent_py3119) C:\Users>python -V
Python 3.11.9
(agent_py3119) C:\Users>pip install matplotlib==3.11.0
# 此时安装了matplotlib 3.11.0
(agent_py3119) C:\Users>pip list | findstr matplotlib
matplotlib 3.11.0
matplotlib-inline 0.1.7

1.2 matplotlib基本介绍

1.2.1 第一张图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 导入matplotlib
import matplotlib.pyplot as plt
# 导入numpy库并取别名np
import numpy as np

# 创建图表
# 此方法依赖pyplot自动创建figure和axes
"""
意思: 在内存中暗中画好一张折线图。
拆解其中 np.arange(5): 生成一个包含 5 个数字的序列,即 [0, 1, 2, 3, 4]。
拆解其中 plt.plot(X, Y): 它的标准语法是传入 X 轴数据和 Y 轴数据。

这里传入了两个一模一样的序列,等同于:plt.plot([0,1,2,3,4], [0,1,2,3,4])。
Matplotlib 在后台把这 5 个坐标点 (0,0), (1,1), (2,2), (3,3), (4,4) 连接起来,形成一条斜线(其实是一条从左下角到右上角的 45 度的线段)。
"""
plt.plot(np.arange(5), np.arange(5))

# 显示
"""
意思: 把画好的图真正展示出来。
为什么需要此行代码: 前面的 plt.plot() 只是在电脑内存里悄悄把图画好了,并没有展示出来。此语句就是在一个画布上真正展示出绘制好的图
"""
plt.show()
Pasted image 20260611193021

1.2.2 %matplotlib inline

在 Jupyter Notebook(或 JupyterLab)中,%matplotlib inline 是一行魔法命令(Magic Command)。 它的作用是让画出来的图表直接嵌入在代码单元格(Cell)的正下方,而不是弹出一个独立的窗口。加上这条命令之后,在整个Jupyter Notebook文件中都不再需要使用plt.show()就能实现图的显示。

1
2
3
4
5
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
plt.plot(np.arange(5), np.arange(5))
#plt.show()

此代码段与上述代码段的效果完全相同,当然画的图也是一样的。

  • 之后所有关于展示图的 cell 都不再需要使用 plt.show()了吗?
    • 当在 Notebook 的某个cell中执行了 %matplotlib inline 之后,Jupyter 会接管 Matplotlib 的渲染后端。之后每当运行一个包含画图语句的 Cell(比如 ax.plot()plt.plot()),Jupyter 会在当前 Cell 运行结束的瞬间,自动在后台调用类似于 show() 的机制,把图表直接塞进这个 Cell 的正下方。
  • 之后的 cell 加上 plt.show()会有什么影响吗? 几乎没有任何负面影响。如果习惯性地在代码最后一行加上了 plt.show(),程序不会报错,也不会画出两张一模一样的图。它能正常工作,但会带来一个极其微小的“行为变化”:
    • 提前渲染并清空缓存 如果在一个 Cell 里写了很多行代码,plt.show()会在它所在的那一行立刻把图弹出来,而不是等整个 Cell 的所有代码全部跑完才弹出来。
  • 那么使用了魔法命令后到底是加还是不加plt.show()
    • 在 Jupyter 内部玩数据、做实验时: 建议不加。少写一行 plt.show()能让代码看起来更清爽。如果最后一行代码有文字输出,就在末尾随手加个分号 ; 即可。
    • 当打算把这段代码打包写成 .py 脚本时: 必须加上。因为 .py 脚本离开了 Jupyter 环境后,%matplotlib inline 这个魔法命令就不再生效,不写 plt.show()的话,在终端运行脚本时就无法弹出展示带有图表的窗口及图表了。

1.3 matplotlib中图形的组成部分

20260613171512030.png 此图来自 Matplotlib 官网,它清晰地展现了一个完整 Matplotlib 图表背后的所有组成构件和对应的代码 API。以下是对此图的介绍。

1.3.1 总体概述

  • 核心内容:此图全面展示了 Matplotlib 面向对象(Object-Oriented) 绘图的核心层级结构(个人感觉有点类似于HTML语言中各种容器元素的嵌套结构及对它们的操作),并标注了控制每个视觉元素的关键代码方法(如 ax.set_titleax.plot 等)。

  • 绘制步骤与核心逻辑

    1. 准备画布 (Figure):最外层首先创建一个最大的空白画布。
    2. 创建坐标系 (Axes):在画布上划分出一个或多个独立的绘图区域(即子图)。
    3. 绘制核心数据 (Line / Markers):在坐标系中通过数据点绘制出折线、散点等主体图形。
    4. 添加辅助修饰 (Title / Legend / Grid / Axis):最后加上标题、图例、网格和坐标轴标签,完成一幅逻辑严密、可读性高的图表。
  • 主要特点:结构高度模块化、层级清晰。Matplotlib 的设计思想是“一切皆对象”(有点像当初学习Java语言时学习的OOP编程思想),图中的每一个文字、线条、刻度都是一个独立的对象,都可以被精准地定制和修改。

1.3.2 主要元素介绍

根据图中的蓝色圆圈圈出的标注,图表的主要元素可以分为以下三大类:

1.3.2.1. 容器与框架层(基础舞台)

  • Figure (plt.figure):最外层的整个大画布。它是所有元素的最高层级容器,包含了图表的所有内容。
  • Axes (fig.subplots):具体的绘图区域(子图/坐标系)。一个 Figure 可以包含多个 Axes。它是画图的主战场,绝大部分的绘图操作(如画线、加网格)都是针对 Axes 进行的。
  • Spine (ax.spines):边框线。即包裹住绘图区域的四条四周边界线(上下左右)。

1.3.2.2. 数据表现层(图表核心)

  • Line (ax.plot):折线/曲线。用于表现连续趋势的数据线(如图中的蓝色和橙色曲线)。
  • Markers (ax.scatter):数据标记/散点。用于表现离散数据点的形状(如图中的紫色空心正方形)。在 Matplotlib 的底层逻辑中,“线(Line)”和“点(Marker)”其实是两个互相独立、甚至可以拆分的视觉元素。
  • matplotlib中并不止于这两种数据表现类型,还有其他类型如Bar (ax.plot)

1.3.2.3. 辅助说明层(图例与轴线)

  • Title (ax.set_title):图表标题。位于正上方,说明整张图画的是什么。

  • Legend (ax.legend):图例。用来解释不同颜色、形状的线条或散点分别代表什么数据(如图中的 Blue signalOrange signal)。

  • Grid (ax.grid):网格线。背后的虚线网格,方便眼睛快速对齐并读取数据坐标。

  • xAxis / yAxis (ax.xaxis / ax.yaxis):X 轴与 Y 轴本身。包含了轴线、刻度以及刻度标签的集合体。

  • xlabel / ylabel (ax.set_xlabel / ax.set_ylabel):坐标轴名称标签。说明 X 轴和 Y 轴分别代表什么物理含义。

  • Major/Minor tick (set_major_locator / set_minor_locator):主刻度与次刻度。长一点的是主刻度(如 0, 1, 2),短一点的是次刻度(如 0.25, 0.5)。

  • Major/Minor tick label (set_major_formatter / set_minor_formatter):刻度文字标签。即显式印在刻度旁边的数字或文字(如“3”或“3.25”)。

1.4 编码风格

1.4.1 风格说明

matplotlib支持两种编码风格:显式接口风格(也叫"object-oriented style“,简称"OO-style")与隐式接口风格(也叫"pyplot-style")。简介如下。

  • OO-style:显式地创建画布与子图,在它们的基础上调用各种控制数据或样式的方法。
  • pyplot-style:依赖于 pyplot 隐式地创建与管理画布与子图,使用 pyplot 函数进行盲盒式管理来画图。

在隐式风格中,Matplotlib 在幕后维护着一个“当前活动状态机”。当用户直接调用 plt.plot() 这种函数时,用户明明从来没有亲手创建过画布(Figure)和子图(Axes),但图却画出来了。这就是因为 pyplot 在自动进行以下隐式管理:

  • 自动创建:发现用户明确没建画布?它会自动在后台建一个 Figure,并在里面塞进一个默认的子图 Axes
  • 自动追踪(最核心的管理):如果用户一口气开了好几个窗口,或者画了好几个子图,pyplot 会在后台死死盯着:“哪一个画布是当前用户正在操作的?哪一个子图是当前活跃的?” **
  • 自动路由:比如当用户敲下 plt.title("我的图") 时,pyplot 管理系统会立刻把这个标题发送给 当前处于活跃状态的那张画布里的那个子图 。

个人认为,使用OO-style时,可以继承自己以前所学的面向对象编程思想来使用matplotlib画图,也更符合平常人思维习惯(明确责任主体,拿到句柄,使用句柄进行定点操作),当然OO-style是matplotlib是官方推荐的编码风格

这两种编程风格的差异与权衡参见官网解释:Matplotlib Application Interfaces (APIs)

1.4.2 the OO-style示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 2, 100) # Sample data.

# Note that even in the OO-style, we use `.pyplot.figure` to create the Figure.
# 创建画布与子图。
"""
figsize=(5, 2.7):其中创建了一张 “宽 5 英寸、高 2.7 英寸”(matplotlib中的默认单位是英寸) 的画布。英寸到像素的转换取决于一个叫 “DPI(每英寸像素点数,默认值是100) 的参数,所以会创建一个“宽500像素、高270像素” 的画布。
layout='constrained':自动优化防止标签重叠溢出。考虑到调整画布尺寸,有些元素(比如标题、坐标轴标签)可能会被挤到画布边缘甚至切掉。加上此参数值它可以帮忙 计算并优化子图与大画布边缘的留白,确保用户无论把 `figsize` 改成什么奇怪的比例,轴标签和标题都绝对不会溢出画布。
"""
fig, ax = plt.subplots(figsize=(5, 2.7), layout='constrained') #
ax.plot(x, x, label='linear') # Plot some data on the Axes.
ax.plot(x, x**2, label='quadratic') # Plot more data on the Axes...
ax.plot(x, x**3, label='cubic') # ... and some more.
ax.set_xlabel('x label') # Add an x-label to the Axes.
ax.set_ylabel('y label') # Add a y-label to the Axes.
ax.set_title("Simple Plot") # Add a title to the Axes.
ax.legend(); # Add a legend.
# plt.show() # 展示图形

1.4.3 the pyplot-style示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 2, 100) # Sample data.

plt.figure(figsize=(5, 2.7), layout='constrained')
plt.plot(x, x, label='linear') # Plot some data on the (implicit) Axes.
plt.plot(x, x**2, label='quadratic') # etc.
plt.plot(x, x**3, label='cubic')
plt.xlabel('x label')
plt.ylabel('y label')
plt.title("Simple Plot")
plt.legend();
# plt.show() # 展示图形

1.5 绘图模板

就像写作文、论文会有相关模板一样,为了便于记忆与掌握其编写模式,也可以为使用matplotlib画图创建一个干净、规范且高度精简的 Matplotlib 显式(面向对象风格) 绘图模板,方便初学者上手。 同时可以此基础上添加一些其他复杂的配置与操作。

精简注释版本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import matplotlib.pyplot as plt
import numpy as np

# ==============================================================================
# STEP 1: 准备数据 (Data Preparation)
# ==============================================================================
x = np.linspace(0, 2, 100)
y = x ** 2

# ==============================================================================
# STEP 2: 创建画布与坐标系/子图 (Figure & Axes)
# ==============================================================================
fig, ax = plt.subplots()

# ==============================================================================
# STEP 3: 绘制核心图表 (Plotting)
# ==============================================================================
ax.plot(x, y, label='Quadratic Curve', linewidth=2, linestyle='-')

# ==============================================================================
# STEP 4: 装饰与标注 (Customizing Labels, Title & Legend)
# ==============================================================================
ax.set_xlabel('X Axis Label')
ax.set_ylabel('Y Axis Label')
ax.set_title('Standard Template Simple Plot')
ax.legend(loc='upper left')

# ==============================================================================
# STEP 5: 输出与渲染 (Display & Save)
# ==============================================================================
plt.show() ;

详细注释版本如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import matplotlib.pyplot as plt
import numpy as np

# ==============================================================================
# STEP 0: 修改全局设置 (rcParams Settings)
# ==============================================================================
# 设置具体字体为黑体
plt.rcParams['font.sans-serif'] = ['SimHei']
# 解决负号 '-' 显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False

# ==============================================================================
# STEP 1: 准备数据 (Data Preparation)
# 官网参考: https://matplotlib.org/stable/users/explain/quick_start.html#types-of-inputs-to-plotting-functions
# ==============================================================================
x = np.linspace(0, 2, 100)
y = x ** 2

# ==============================================================================
# STEP 2: 创建画布与坐标系/子图 (Figure & Axes)
# 官网参考: https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.subplots.html
# ==============================================================================
fig, ax = plt.subplots()

# ==============================================================================
# STEP 3: 绘制核心图表 (Plotting)
# 官网参考: https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.plot.html
# ==============================================================================
# label 用于图例展示,linewidth 控制线粗,linestyle 控制线型
ax.plot(x, y, label='Quadratic Curve', linewidth=2, linestyle='-')

# ==============================================================================
# STEP 4: 装饰与标注 (Customizing Labels, Title & Legend)
# 轴标签参考: https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.set_xlabel.html
# 标题参考: https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.set_title.html
# 图例参考: https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.legend.html
# ==============================================================================
ax.set_xlabel('X Axis Label') # 设置 X 轴名称
ax.set_ylabel('Y Axis Label') # 设置 Y 轴名称
ax.set_title('Standard Template Simple Plot') # 设置图表正标题
ax.legend(loc='upper left') # 激活图例并固定在左上角

# ==============================================================================
# STEP 5: 输出与渲染 (Display & Save)
# 显示参考: https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.show.html
# 保存参考: https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.savefig.html
# ==============================================================================
# 如果需要保存图片到本地,取消下面这行的注释(必须在 plt.show() 之前调用)
# plt.savefig('my_plot.png', dpi=300)

plt.show() ; # 末尾加分号防止在 Jupyter 中打印内存地址文字

二、官方文档的介绍

在matplotlib官网文档页面的顶部有7个大菜单:Plot types、User guide、Tutorials、Examples、Reference、Contribute、Releases。 20260628125110519.png 它们的内容或作用分别大致如下:

(1) Plot types: 主要用来介绍 Matplotlib 中可以用来绘制哪些类型的图,并给出对应的示例代码。它是最实用的“抄代码圣地”,方便不熟悉某图或忘记函数名的用户直接视觉化检索,一键复制最小完备代码后快速修改出自己期望的效果。

(2) User guide: 是官方说明书。介绍了快速启动指南、核心解释性文献、设计哲学与重要术语、FAQ、以及安装方法等。它重点在于“阐述那些不随具体某行代码改变的宏观机制”(如色彩映射、画布与后端的交互原理、文字与字体的底层配置等)。

(3) Tutorials: 心法与语法指南。如果说 API 手册是告诉用户每个字怎么写的“字典”,那么这 5 个 Tutorial 就是教用户如何用这些字组合出高内聚、低耦合、符合Matplotlib大管家内部管理系统底层生命周期的“模范作文”,帮忙用户建立或掌握正确绘图世界观、规范高效的绘图习惯和方法。

(4) Examples: 官方的“海量实体代码仓库”。这里没有长篇大论的理论描述,而是提供了数以百计、针对具体物理元件和具体业务场景的独立运行脚本。无论是想给 text 加一个特殊边框,还是想看如何定制 axes 的网格,这里都准备好了开箱即用的工程案例。

(5) Reference: Matplotlib 官方最权威的“新华字典 / API 参考手册”。对包名、子包名、类型名(如各种 Artist 类)、各个方法的声明、调用签名、常用参数详细解说、以及返回值类型等进行了详尽描述,是编写代码时高频查阅的核心文档。

(6) Contribute: 针对开源社区开发者的“贡献与施工指南”。用来帮助世界各地的核心极客和代码贡献者掌握如何拉取源码、安装本地开发测试环境、编写和提交 Pull Request、进行回归测试、以及遵循社区的编码规范与准则。

(7) Releases: 官方的“编年史与进化轴”。详细记录了 Matplotlib 所有历史版本的发布时间、新特性的功能点、旧 API 的弃用(Deprecation)与重构公告、贡献者致谢、以及框架未来的长期发展规划。

三、Tutorials教程及Matplotlib中图的构成

3.1 Tutorials教程总体内容

虽然 Matplotlib 官网文档提供了极其丰富的内容,但面对这片汪洋大海,初学者极易陷入不知从何下手的迷茫境地。在正式学习与使用之前,建立起正确的“心智模型”至关重要。

打个比方,如果把学习 Matplotlib 比作修炼一门武功:

  • Reference(API 手册) 是“字典”,像素级地告诉读者每一个字(函数/参数)怎么写;
  • Examples(示例库) 是官方的“海量实体武器库”,虽然堆满了针对具体物理元件和具体业务场景的独立开箱脚本,却缺乏系统的穿引线;
  • User guide(用户指南) 则是长篇大论的“官方说明书”,内容过于细致繁杂,极易让人“乱花渐欲迷人眼”。

初学者真正需要的,应该是一份能够迅速概括核心概念、建立框架认知、且能复制即用的指南,从而为自己指出一条规范、高效操作 Matplotlib 的阳关大道——这就是 Tutorials 教程

Tutorials 教程是学习与使用 Matplotlib 的“心法和语法指南”。它不在细节参数上纠缠,而是站在架构的高度,帮忙用户建立起核心概念认知与框架认知,教用户如何将散落的 API 组合出高内聚、低耦合、完美契合 Matplotlib 内部管理系统底层生命周期的优秀代码。


官网的Tutorials教程包含5个子章节:Pyplot tutorial、Coding shortcuts、Image tutorial、The Lifecycle of a Plot、Artist tutorial。 20260628171259066.png

它们的内容或作用分别大致如下:

  • (1) Pyplot tutorial: 探索性数据分析的”快刀指南“。
      • 它是干什么的:专门教用户如何使用 matplotlib.pyplot 这个面向过程的状态机接口。
    • 为什么独立存在:正如官网所述,它是为了照顾从 MATLAB 转过来的用户,以及需要快速看一眼数据趋势(EDA)的初学者。它教的是最不用动脑子、最隐式、代码量最小的画图方式。
  • (2) Coding shortcuts: 规范与高效“编码指南”。
      • 它是干什么的:教用户如何用更高阶、更现代的 Python 语法来精简绘图代码。
    • 为什么独立存在:它包含了很多让用户少写几行代码的“核心秘籍”。比如这样的代码: ax.set(xlabel="...", ylabel="...", title="...")。不要一行行调用set_xxx了,而是用一个 .set() 复合调用直接使用关键字参数!它的目的就是为了提升用户的工程效率,减少冗余代码。
  • (3) Image tutorial: 计算机视觉与多维数据的”数字调色盘“。
      • 它是干什么的:专门教用户如何利用 NumPy 数组(NDArray) 来处理、缩放、并渲染像素级的图像数据。
    • 为什么独立存在:AI 领域的图像通常是二维矩阵(灰度图)或三维矩阵(RGB)。这个教程不是为了教用户画折线,而是为了教用户如何把矩阵像素化输出、如何通过 cmap(颜色映射表)和 clim(色彩对比度范围)来做视觉增强。它是专门留给 CV(计算机视觉)工程师和矩阵计算研究员的特供指南。
  • (4) The Lifecycle of a Plot: 工业级工程落地的“剧本复盘”。
      • 它是干什么的:用一个极具实战意义的真实业务数据,从零开始,带用户经历:创建画布 \(\rightarrow\) 绘制初稿 \(\rightarrow\) 样式微调 \(\rightarrow\) 全局配置调整 \(\rightarrow\) 导出生产级图片(PDF/PNG)的完整生命周期。
    • 为什么独立存在:API 手册是散落的积木,而这里提供的是标准工业流水线样板房。它手把手教用户如何在一个脚本中,优雅地控制 Matplotlib 大管家内部管理系统的生命周期。
  • (5) Artist tutorial: 彻底打通底层架构的“任督二脉”。
      • 它是干什么的:向用户展示 Matplotlib 大管家内部管理系统的底牌,深度剖析 FigureAxesAxis 的包含树状网络,以及 ax.linesax.patches 这些图元容器。
    • 为什么独立存在:帮用户建立起一套“万物皆绘图对象(Artist)”的宏观世界观。读懂了它,用户使用 Matplotlib 时,可以从“隐式调包”走向“显式架构”(如通过ax.patches 列表里去逆向操控某一个柱子的颜色或加数字标签),并洞悉大管家的“绘制心跳”(其中描述了每一个图元对象如何调用底层 Renderer(渲染器)将自己绘制到 Canvas(画布)上的机制)。它是用户告别堆砌式代码、真正走向“底层掌控者与高阶定制库作者”的通关必经之路。

考虑到”Artist tutorial“的用途与重要性,下面将专门对其中内容展开描述。

3.2 Artist tutorial(包含讲述图的构成)

3.2.1 matplotlib中API构架分层

Matplotlib官方文档Artist tutorial将其API构架分为三层:matplotlib.backend_bases.FigureCanvas(笔者在官方完整API文档没有找到此类,但找到了matplotlib.backend_bases.FigureCanvasBase,它代表画布的基类,猜测应该是Artist tutorial中误写了。此章节中将使用matplotlib.backend_bases.FigureCanvasBase)、matplotlib.backend_bases.Renderermatplotlib.artist.Artist

官方对它们的概述如下:

  •  matplotlib.backend_bases.FigureCanvasBase 画布,是绘制图形的区域。
  •  matplotlib.backend_bases.Renderer 渲染器,是知道如何在画布上绘制的对象。
  •  matplotlib.artist.Artist 绘图对象或叫图元,是知道如何使用渲染器在画布上绘制图像的对象。

如果将其以图文形式展示并稍加解释,大致如下: matplotlib.backend_bases.FigureCanvasBasematplotlib.backend_bases.Renderer负责处理绘图过程、展示与资源管理的全部底层细节,matplotlib.artist.Artist 则处理所有高级结构(用户直接调用它,所以它是用户与前两者的媒介),例如表示和布局图形、文本和线条。根据官网文档显示,用户绝大多数时间都是在与matplotlib.artist.Artist打交道。

Artist(绘图对象/图元)可以细分为两大类:primitives(基础图元/基元) 与 containers(容器对象/容器)。primitives代表那些绘制在画布上的基本图形对象,如:Line2D、Rectangle、Text 与 AxesImage 等等;containers则代表放置primitives的地方,如:Axis、Axes 与 Figure。 从概念包含归属关系(非API类型继承关系)上,Artist 细分后的代表性事物如下图: Artist绘图对象层次结构

绘图的标准化流程:首先创建一个画布对象,然后通过画布对象创建出一个或多个坐标系(也叫子图),最后通过坐标系的相关帮助性方法(即matplotlib API中为坐标系提供的相关方法)创建出所需要的基础图元、设置其属性(如颜色、位置、透明度等),最后渲染展示整个绘图效果。

1
2
3
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(2, 1, 1) # two rows, one column, first plot

3.2.2 坐标系Axes与Artist的属性

最常用API-Axes

在使用matplotlib进行绘图的过程中,Axes 是matplotlib API中最重要的类型,且是最经常使用且绝大多数时间都在使用的类型。这是因为坐标系是绝大部分绘图对象附着或放置之处。

Axes提供了许多特定的帮助函数(其实就是类型中包含的方法)来绘制常见的基础图元,比如经常用的plot()、text()、hist()、imshow()方法分别绘制了Line2D、Text、Rectangle、AxesImage 这些基础图元对象。Axes 提供的这些方法获取用户提供的数据,利用这些数据创建基础图元对象,并把它们添加到特定的容器中,最后在需要时渲染出整个画布及其中所有内容。 用户只需要给Axes的方法提供参数数据即,这背后的一系列动作都由matplotlib内部管理机制自动完成,这套机制经常被称为matplotlib大管家。Matplotlib 大管家在本质上是一个由“脚本状态机层(matplotlib.pyplot 模块)”发号施令,通过“面向对象图元层(Artist API)”组织和调度万物,最终交由“后端引擎(Backend)”落地渲染的统一协调管理机制。

默认情况下,坐标系区域在横纵方向分别占据画布大致77%左右长度,剩余的画布面积被转换成了四周的留白,留白区域可以用来放置坐标系标题、坐标轴刻度、刻度标签等内容。但坐标系区域可以通过修改默认参数值来调整:

1
2
3
fig2 = plt.figure()
# 参数分别是x轴起点、y轴起点、宽度、长度(整个画布的宽高都算作1,起点与跨距用小数来表示)
ax2 = fig2.add_axes((0.15, 0.1, 0.7, 0.3))

在下面这个示例中,首先创建画布,然后通过画布对象创建坐标系对象。最后当执行ax.plot创建一个Line2D对象并把它附着在坐标系上,并返回这个Line2D对象。这个创建出来的Line2D对象其实可以通过ax.lines来直接获取,ax.lines的类型是列表,每个向坐标系添加的Line2D对象都会被添加到ax.lines末尾。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import matplotlib.pyplot as plt
import numpy as np
t = np.arange(0.0, 1.0, 0.01)
s = np.sin(2*np.pi*t)

fig = plt.figure() #创建画布
ax = fig.add_subplot() #创建坐标系(子图),默认就是一个1*1的坐标系
# 绘制一个 Line2D 对象即一个线条,返回值line就是这个Line2D 对象
line, = ax.plot(t, s, color='blue', lw=2) #x轴数据、y轴数据、线条颜色、线条宽度linewidth

"""
此时,line 与 ax.lines[0] 其实是等同的
print(line == ax.lines[0]) #返回True
"""

# plt.show(); # 此时如果再加上这行代码,即可展示绘制的图

此外,Axes除了提供了方法绘制核心绘图区域的基础图元,还提供了方法来配置与装饰x轴、y轴的刻度、刻度标签与坐标轴名称。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import matplotlib.pyplot as plt
import numpy as np
# 此示例中,笔者为了显式展示相关效果,为某些区域设置了一些颜色或宽度,导致美观度可能下降

# 创建画布
fig = plt.figure()
# 设置画布fig的填充色为粉色、边框颜色为红色、框架宽度为4
fig.set(facecolor='pink', edgecolor='red', linewidth=4)
# 微调画布,将子图(Axes)的“上边界”设定在整张画布物理高度的 80% 位置。
fig.subplots_adjust(top=0.8)
# 添加2行1列共2个子图。并返回第1个子图ax1(从上到下、从左往右数,最开始的子图索引是1)
ax1 = fig.add_subplot(211)
# 设置子图ax1的y轴名称
ax1.set_ylabel('Voltage [V]')
# 设置子图ax1的标题
ax1.set_title('A sine wave')

# 指定起点、终点,按照固定步长产生一个数据序列
t = np.arange(0.0, 1.0, 0.01)
# 构造一个函数 s = sin(2 * pi * t)
s = np.sin(2*np.pi*t)
# 在子图ax1中使用plot方法绘制一条折线段
line, = ax1.plot(t, s, color='blue', lw=2)

# 设定随机种子值保证随机数复现
np.random.seed(19680801)

# 先前准备了一个画布,但只初始化并返回了第一个坐标系,第2个坐标系对应的画布区域是空的
# 现在初始化并返回第2个坐标系
ax2 = fig.add_axes((0.15, 0.1, 0.7, 0.3))
# 在子图ax2中绘制了一个由50根小柱子组成的直方图,并返回三个底层艺术单元的引用
# n:数组,每一个柱子里落入的数据点的绝对数量。
# bins:数组,50个柱子边缘刻度边界值(50个柱子对应有51个边界值)
# patches:列表,是包含了 50 个 Rectangle(矩形基础图元)对象的列表
n, bins, patches = ax2.hist(np.random.randn(1000), 50,
facecolor='yellow', edgecolor='yellow')

# 设置子图ax2的x轴名称
ax2.set_xlabel('Time [s]')
# 设置子图ax2对应的坐标系矩形(ax.patch)的填充色
ax2.set(facecolor='gray')

# 展示画布上的绘图效果,并做资源清理
plt.show()

Artist的属性与getter/setter

在matplotlib中,画布中的所有内容都是由matplotlib大管家派出的绘图对象即Artist绘制出来的,每个绘图对象都有一长串属性,可以通过它们来修改绘图对象的视觉效果。

比如画布本身就包含一个代表其画布那个矩形的属性(fig.patch),这个矩形的大小与画布大小一样,用户可以拿到fig.patch这个属性后就可以对那个矩形进行设置(比如设置填充色、透明度、是否可见等);类似地,坐标系本身也包含一个矩形区域,它就是由所有坐标轴围起来的那个区域(在二维直角坐标系中,它是一个矩形区域;在极坐标系中它是一个圆形或扇形区域。但不管是直角坐标系还是极坐标系,坐标系中对应的那块区域总是用 ax.patch 来获取),拿到ax.patch就可以坐标系区域的视觉效果进行配置。

对于每一个绘图对象Artist都有如下属性:

属性名 属性描述
alpha 透明度。整型或浮点数,范围在[0,1],默认为None表示完全不透明,越小越透明,0表示完全透明
animated 是否动画加速开关。布尔类型,True时表示开启,开启后,大管家在绘制动态图时会使用 Blitting 技术(只刷新运动的组件,不刷新静止的背景),能给动态图带来极大的流畅度提升。
axes 坐标系/子图。有些绘图对象Artist的此属性可能为None,表示它不属于任何一个具体的子图坐标系。如fig.suptitle("我是全局总标题").axes为None,即全局的画布总标题的axes的属性值为None。
clip_box 矩形裁剪区域。定义了该 Artist 允许被看到的矩形物理边界。
clip_on 是否开启裁剪开关。布尔类型,True时表示开启,此时组件超出子图边框时会把它剪掉。
clip_path 路径裁剪区域。它是任意形状的路径,默认为None,表示不指定路径裁剪区域即显示全部内容。指定时,表示只有落在指定路径路径几何边界内部的像素,才允许被渲染器渲染出来,其余部分将被隐藏。
contains 检测鼠标点击坐标点是否在某组件内部,需要用户自定义函数自行实现判断逻辑、并与点击事件绑定。
figure 组件所属的画布。某些绘图对象的figure属性值可以None,表示它还没有被添加具体的某个画布中,比如当用户调用构造函数matplotlib.patches.Circle(...)直接创建一个Circle实例时,此时它的figure属性值一般为None。
label 组件的文字标签。默认是空字符串。
picker 控制该组件能否被鼠标“点中”。
transform 组件空间定位解析器。就是说组件以何种参考对象来将代表空间位置的抽象“数字”变成屏幕上具体的“像素”点位置,这种参考变换之间要有一个变换矩阵。有三个核心的变换矩阵:ax.transData(默认值)、ax.transAxes、fig.transFigure
visible 组件是否可见。布尔值类型,默认值一般为True,表示可见。
zorder 图层顺序。数字类型,它决定谁盖在谁上面(画作的“前后图层”),数值越大图层越靠前。
rasterized 是否强行把矢量图强行变成高清像素位图。布尔类型,默认False表示关闭,保持纯矢量输出。

matplotlib API中为上述每个属性提供了一个gettersetter方法,分别表示获取属性值、设置属性值。同时也支持在绘图对象上调用set方法同时设置多个属性值。 matplotlib API中也提供了matplotlib.artist.getp(...)来一次性获取某绘图对象Artist的所有属性及值。另外因为matplotlib源码中注释的高规范可读性,其为每个类型(面向对象术语)提供了docstrings,所以通过使用help(xxx)也能查看指定绘图对象xxx的所有属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 坐标系对象ax2的title属性值是一个'matplotlib.text.Text'实例,它是` matplotlib.artist.Artist` 的子类,就是说ax2.title也是一个Artist实例。ax2.title也有alpha属性,可调用get_alpha()
print(ax2.title.get_alpha())
# 再调用set_alpha()
ax2.title.set_alpha(ax2.title.get_alpha() * 5)
# 同时设置ax2.title的多个属性
ax2.title.set(alpha=ax2.title.get_alpha() * 5, color='red')

# ax2就是一个Artist实例,对其使用matplotlib.artist.getp 方法获取其所有属性
matplotlib.artist.getp(ax2)
# ax2.title 也是一个Artist实例,也可对其使用matplotlib.artist.getp 方法获取其所有属性
matplotlib.artist.getp(ax2.title)
# 调用python原生自带的help方法,输出ax2.title对应类型` matplotlib.artist.Artist`的 docstrings,其中就包含它的所有属性properties
help(ax2)

3.2.3 Artist可细分为primitives与containers

前面已经提到:Artist(绘图对象/图元)可以再细分为两大类:primitives(基础图元/基元) 与 containers(容器对象/容器)。primitives代表那些绘制在画布上的基本图形对象,如:Line2DRectangleTextAxesImage 等等;containers则代表放置primitives的地方,如:AxisAxesFigure

以下分别对primitives(基础图元/基元)与containers(容器对象/容器)的核心代表进行简述,阅读后可以后它们有个初步的认识。它们的核心代表对应的那些各种图的完整解释与绘制示例将在此教程的最后单独用一个章节给出。

基础图元primitives

primitives 代表那些最终绘制在画布上的标准图形实体对象,是具体展示数据的实体。在实际应用中,它以各种形式支撑着数据的可视化呈现:无论是展示数据变化趋势的线条(折线图的底层实体)、对比数据大小或分布范围的面片(柱状图/直方图的底层矩形)、还是用来展现海量离散数据的集合图元(散点图的底层大表),亦或是对图表进行说明的文字与范围标识的圆形、多边形,在后台全是一个个具体的 primitives 实例。常见的分类与核心代表如图所示。 primitives的细分与代表

(1)线条流派“Line Elements”

类似于霓虹灯人偶舞蹈中的人偶,每一节/全身只有单一颜色的那种演员,它们在舞台上只有骨架、没有肉体。 它们是由无数个点连接而成的“极细钢丝”,可以改变它的长度、粗细(linewidth)和颜色,但因为它们内部没有所谓填充面积的概念,所以无从谈起给它们内部涂抹颜色(不支持 facecolor)。

  • (1.1)Line2D(平面线条):最经典最常见的线条。平时绘制的折线图、曲线图,甚至坐标轴上那一排排细小刻度线,底层都是用它一根根拉出来的。
  • (1.2)Line3D(3D 立体线条):在xy平面的基础上进一步扩展了能力,能够上下移动、“会飞”的线条。
(2)面片流派“Patch Elements”

不再是霓虹灯人偶舞蹈中的人偶那样每一节/全身只有单一颜色,此类演员不仅有骨架,而且长了肉体(闭合面积)。 凡是有闭合面积的圈(演员的身体关节是一个闭合面积的形状),就会有外圈颜色与内部面积颜色的概念。它们全都继承自 Patch类型 ,适合用来涂色和圈定范围。

  • (2.1)Rectangle(矩形):画布的底色(fig.patch)、子图的背景(ax.patch),还是在画柱状图(bar)和直方图(hist)时那一排排并列的柱子,本质上全是一块块长宽不同的矩形方砖。
  • (2.2)Circle/Ellipse(圆形/椭圆):在数据分析里,如果想在密密麻麻的特征图、散点图上,把某几个最核心的异常数据点“狠狠地圈起来”引起读者注意,掏出它来准没错。有个很形象的比喻:有点像是舞台上的探照灯。
  • (2.3)Polygon(多边形):给出一串有序的坐标点,然后像连线游戏一样(比如连接养鱼时圈中水域面积时用的多个浮漂),把最后一个点和第一个点死死连在一起,连出一个闭合的、形状奇奇怪怪的面积区域。常用于高亮显示置信区间。
  • (2.4)Wedge(扇形/楔形):画饼图(ax.pie())时,那些被切成不同大小、用来代表百分比的“扇形区域”,在底层,每个“扇形区域”就是一个Wedge实例。另外,极坐标系的圆盘也是Wedge
(3)文本流派“Text Elements”

大管家把所有的文字都封装成了统一的 Text 实例。为了防止字体的颜色和背景不协调(如二者的颜色相同或相近,导致文字无法正常展示或不明显),每个 Text 实例在底层都暗中拥有自带了一个背景板(一个Rectangle 边框),可以随时给文字加阴影、设置合适样式的背景等。

  • Text(通用文本):字符类的组件都是Text。如,子图标题、 \(X/Y\) 轴的标签,以及在图表里随手贴的批注文字,在后台全是一个个 Text 实例。
(4)图像流派“Images”

是数字与像互映射大师,可以处理庞大的矩阵数据。具体就是把一堆乱七八糟的矩阵二维数字(比如医疗 MRI 断层扫描数据、热力图矩阵),根据指定的映射规则“参数cmap”,把不同大小的数字映射成不同颜色的像素格子。

  • AxesImage(图像像素矩阵) :调用 ax.imshow() 时返回的就是 AxesImage 实例。在机器学习中看混淆矩阵、在医疗行业看特征图,都是用它把抽象的权重数字变成像素图像。
(5)集合流派“Collection”

是专门用来处理独立分散几何个体的工具。图像流派“Images”与集合流派“Collection”都是为了应付海量数据而生的具体primitives组件,但它们还是有显著区别的:Images 渲染的是“连续的像素格子”,而 Collection 渲染的是“独立的几何个体”,这些独立的几何个体不一定是连续的。 在工业级实际生产应用中,要在图里画 10 万个散点时,如果创建 10 万个独立的 Circle 实例,内存和 CPU 当场就会卡瘫痪(即使有显卡也没有被使用到的机会)。matplotlib为了应对与处理此场景问题,开发了 Collection 。它把几万个点的坐标和颜色直接打包,由CPU在内存中合并成一整张大表后,直接传递给显卡。显卡显卡最擅长处理这种批量的结构化数据,只要计算一次,就能把几万个点一次性渲染出来(当然普通的使用场景下,几百个散点时不需要使用到显卡也行)。

  • Collections(图元集合 / 散点军团):是大规模散点的救命神器。调用 ax.scatter() 绘制密密麻麻的散点图时,返回的其实就是它(PathCollection)。在处理海量患者医疗数据分布时,它绝对会使用到的性能降维利器。

容器对象containers

containers则代表放置primitives的地方,最常见的容器对象包括:FigureAxesAxiscontainers的细分与代表 如果说 primitives 是在舞台上表演的各个具体演员,那么 containers 就是搭建舞台的各级主办方与舞台框架。它们自己通常不负责展示核心数据,但负责提供物理坐标系、撑起整张画布,给基础图元们提供附着与展示的空间。

(1)Figure“画布”

它是舞台的“投资人 / 整张地皮”。代表整个绘图窗口或者整张物理打印纸,是整张图画的“总管家”。通过它,可以控制整张图的物理尺寸(figsize)、背景色,给整个绘图窗口取一个全局总标题,或者裁切与保存出最终的图片文件。

画布是绘图对象中最顶级的对象容器。常说的一个画布窗口,其实就是一个matplotlib.figure.Figure实例,它包含了整个图表里的一切组件。画布有很多常用的属性,这些属性分别包含着画布中经常用到的primitivescontainers,常见的如下:

画布属性名 代表的意义 相关说明
axes 坐标系(Axes)列表 (1)通过add_subplot(...)add_axes(...)方法向画布中添加的坐标糸实例将添加到axes列表末尾。(2)动态删除画布某子图坐标系,要先获取对应的坐标系,然后通过Axes.remove()方法,不要直接通过fig.axes.remove(某具体子图坐标系实例)这样的python原生列表中删除元素的方法删除,这样会引起matplotlib大管家内数据的混乱与不一致。matplotlib中几乎都是这样的规律“要通过matplotlib提供的标准API去添加或删除指定的绘图对象,以保证大管家内部数据的统一与一致。"
patch 坐标系背景,是一个(Rectangle)对象 拿到矩形对象可以直接修改它的属性如填充色:fig.patch.set_facecolor('#1e272e')
legends 是画布级别的图例(Legend)列表(不同级别的对象容器的legends不存在包含关系,而是相关隔离的。所以通过ax.legend()产生的图例是属于某个具体子图的,不在此范围) 不在子图里量的图例,而是专属于画布的图例。
lines 是画布级别的线条(Line2D)列表。一般只会向坐标系中添加线条,然后通过Axes.lines获取) 使用时的推荐做法:(1)通过fig.lines.extend(...)方法向fig.lines添加线条时,最好给线条对象加上参数transform=fig.transFigure即使用画布相对空间。(2)或者就通过调用画布提供专门用来添加线条的方法:fig.text()、fig.legend()
patches 是画布级别的面片(Patch)列表 。一般只会向坐标系中添加面片实例如矩形、椭圆等,然后通过Axes.patches获取) -
texts 是画布级别的文本(Text)列表 fig.suptitle(...)设置画布标题、fig.text(...)设置一个普通文本对象,都会在fig.textx添加一个元素。
(2)Axes“子图/坐标系”

它是舞台上的“独立分会场“。在使用 Matplotlib 进行绘图时,与绝大多数人打交道最多的容器。只要在这个坐标系里调用 ax.plot()ax.bar(),坐标系Axes就会自动把对应的折线图与柱状图绘制出来,并使其附着在自己的空间内。

一个画布上可以分布多个 Axes(多个 Axes在画布上的分布规律有点像九宫格,但相邻的格子可以合并为一个 Axes)。每个 Axes拥有自己独立的网格线、独立的图例,互不干扰。

每个 Axes坐标系容器又包含以下两个常见的子容器:XAxisYAxis,它们分别表示X坐标轴与Y坐标轴:

  • XAxis(X 坐标轴):归属于当前子图舞台的“横向规矩尺”。负责看管子图横向的数据边界。上面的横向轴线、刻度线、刻度标签以及轴标题,底层实现上全部由它管理。
  • YAxis(Y 坐标轴):归属于当前子图舞台的“纵向规矩尺”。负责看管子图纵向的数据边界。上面的纵向轴线、刻度线、刻度标签以及轴标题,底层实现上全部由它管理。

Axes“子图/坐标系”(底层是一个matplotlib.axes.Axes实例)是整个Matplotlib绘图世界的中心。它包含了整个画布中绝大多数绘图对象Artist的实际装载与管理。同时,Axes“子图/坐标系”提供了大量的辅助方法用于创建与添加绘图对象,又提供了一些属性从而可以便捷对Axes“子图/坐标系”中所包含的绘图对象进行访问。

  • Axes“子图/坐标系”提供的常用辅助方法:
Axes 的辅助方法 创建与返回的绘图对象 创建的绘图对象存储位置
annotate - 文本箭头注释 Annotation ax.texts
bar - 柱状图 Rectangle ax.patches
errorbar - 误差棒图 Line2D and Rectangle ax.lines and ax.patches
fill - 区域填充图 / 面积图 Polygon ax.patches
hist - 直方图 Rectangle ax.patches
imshow - 图像数据展示 / 热力图与像素矩阵贴图 AxesImage ax.images
legend - 子图图例 Legend ax.get_legend()
plot - 折线图 Line2D ax.lines
scatter - 散点图 / 气泡图 PolyCollection ax.collections
text - 纯文本标签 / 独立艺术字弹幕 Text ax.texts
  • Axes“子图/坐标系”提供的常用属性方法:
Axes 的属性名 属性代表的意义 相关操作或注意事项
artists 记录通过 ax.add_artist() 添加且无法归类的特殊绘图对象。折线、柱状图、圆、文字都有自己的专属存放位置(lines, patches, texts),它们不会出现在 ax.artists 里。 如果想遍历子图中的一切组件(包括图元演员与坐标轴等子容器),使用ax.get_children()
patch 代表Axes背景矩形的Rectangle实例 可以通过它直接修改子图舞台的背景色或边框,例如:ax.patch.set_facecolor('lightgray')
collections 记录各种“集合图元(Collection)”的列表,主要用来批量装载海量同类图元以优化性能。例如:ax.scatter() 产生的散点集合、ax.errorbar() 批量打包的误差线段集合、以及由高层 API(如 3D 表面图)自动生成的批量多边形集合(PolyCollection)。 集合对象是为了优化内存而设计的。如果用 scatter 画了 1000 个点,它们不会产生 1000 个 patch,而是以 1 个集合的形式保存 ax.collections 里。
images 记录子图内所有“二维像素贴图(Image)”的列表。
lines 记录子图内所有“二维线条(Line2D)”的列表。所有通过 ax.plot() 绘制出来的折线、趋势线、均值辅助线,都会在这里登记入册。 凡是通过 ax.plot() 刷出来的折线会在这里登记。注意: ax.errorbar() 产生的误差图的主线和中心点也会记录在这里。
patches 记录子图内部所有“二维几何面片(Patch)”的列表。包含通过 ax.bar() 画的柱状图矩形、ax.hist() 的直方图砖块、以及手工添加的圆/椭圆(Circle/Ellipse)、多边形(Polygon)等。记录子图内部所有独立的“二维几何面片(Patch)”。包含通过 ax.bar() 画的柱状图矩形、ax.hist() 的直方图砖块,以及手工单点添加的圆(Circle)、独立多边形(Polygon 柱状图的每一根柱子都是一个独立的 Rectangle 实例。如果想批量给柱子染色,可以通过遍历 ax.patches 逐个调用 .set_facecolor()
texts 记录子图内部所有“文本标签(Text)”的列表。包含通过 ax.text() 手工打的艺术字、ax.annotate() 做的箭头引线标注,以及各个数据点的文本标签。 注意: 子图的轴标题(Title)虽然也是 Text 实例,但它有自己的独立专属指针 ax.title,通常不通过遍历 ax.texts 来修改标题。
xaxis 代表当前子图舞台的“横向规矩尺”(一个独立的 XAxis 子容器实例)。
yaxis 代表当前子图舞台的“纵向规矩尺”(一个独立的 YAxis 子容器实例)。
(3)Axis“坐标轴”

它是大管家抽象出来的“坐标轴基类”,是 XAxis (X坐标轴)和 YAxis(Y坐标轴) 共同的“父类”。它本身不分横纵,纯粹在底层负责定义一条标准的规矩轴应该怎么装载轴线、刻度、刻度标签和轴标题。

坐标轴(底层是一个matplotlib.axis.Axis实例)负责处理刻度、网格、刻度标签和坐标轴标签相关操作。比如它可以配置X轴上刻度是显示在轴线的上面还是下面(甚至上下都显示,且配置成两套不同的刻度),类似地也可以配置Y轴上刻度是显示在轴线的左边还是右边(甚至左右都显示,且配置成两套不同的刻度)。此处坐标轴还存储了原始数据(绘制图表时传递进来来的原始数据,它决定了坐标轴的data intervals 即数据区间,代表原始数据的绝对边界)与视野范围(叫view intervals 即视窗区间,它代表了图表窗口当前肉眼可见的、呈现出来的数学边界。在对图表进行缩放与移动时,就需要调整view intervals)。最后,坐标轴还可以设置定位器(Locator)与格式化器(Formatter),它们分别决定刻度的位置与刻度对应的值是如何映射成为对应的显式字符串的。 对于定位器的理解,举个例子就是:是按照“每隔 10 点点一个(等间距线性)”标记刻度,和“按对数 \(10^x\) 级数递增”来标记刻度(此时对于原始数据10、100、1000,它们在X轴上分别对应的刻度值是1、2、3)。对于格式化器,可以简单理解为一个映射函数,它将刻度值原始通过特定的映射函数映射成指定格式的字符串。

Axis也提供了一些属性以便用户能够便捷获取其中存储的一些绘图对象。拿到之后,一般是不推荐对它们通过python原生方法进行增加、删除或修改的,而应该通过matpotlib提供的封装API来操作。比如说修改坐标轴的标题、刻度、刻度标签等,虽然可以分别通过如下方式来实现:axis.label = xxx、强行往 axis.majorTicks 列表里 append 新的 Tick、修改 axis.majorTicks[i].text 的字符串,而应该调用对应的封装好的方法:ax.set_xlabel() / ax.set_ylabel()axis.set_major_locator(具体的Locator)或ax.set_xticks([位置列表])、axis.set_major_formatter(具体的Formatter)或ax.set_xticklabels([文字列表])。

如果要设置刻度与网格的样式(如颜色、粗细、方向),应该要使用ax.tick_params(),具体细节见tick_params

四、Matplotlib中对图的操作

2.1 rcParams全局配置参数

2.1.1 画图示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# 导包
import matplotlib.pyplot as plt
import numpy as np

# ==============================================================================
# STEP 1: 准备数据
# 官网参考: https://matplotlib.org/stable/users/explain/quick_start.html#types-of-inputs
# ==============================================================================
x = np.arange(-5, 5, 0.1)
y = np.sin(x)

# ==============================================================================
# STEP 2: 创建画布与坐标系/子图
# 官网参考: https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.subplots.html
# ==============================================================================
# 使用 layout='constrained' 确保移动坐标轴后,刻度标签不会溢出大画布
# fig, ax = plt.subplots(figsize=(6, 5), layout='constrained')
fig, ax = plt.subplots()

# ==============================================================================
# STEP 3: 调整边框位置,使零点坐标在 (0,0) 处。此操作可选,调整零点坐标到 (0,0) 处只为了更符合我们的画图习惯
# 官网参考: https://matplotlib.org/stable/api/spines_api.html#matplotlib.spines.Spine.set_position
# ==============================================================================
# 1. 将上方和右方的边框线设置为透明/隐形
ax.spines['top'].set_color('none')
ax.spines['right'].set_color('none')

# 2. 将下边框(X轴)和左边框(Y轴)强制移动到数据坐标的 0 点位置
ax.spines['bottom'].set_position(('data', 0))
ax.spines['left'].set_position(('data', 0))

# ==============================================================================
# STEP 4: 绘制核心图表
# 官网参考: https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.plot.html
# ==============================================================================
ax.plot(x, y, label='sin(x)', linewidth=2, linestyle='-')

# ==============================================================================
# STEP 5: 装饰与标注
# 轴标签参考: https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.set_xlabel.html
# 标题参考: https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.set_title.html
# 图例参考: https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.legend.html
# 文本参考:https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.text.html
# ==============================================================================
# 1. X轴、Y轴标签传入空字符串(默认就是空字符串)
ax.set_xlabel('')
ax.set_ylabel('')

# 2. 手动在第一象限指定位置“钉”上文本
# x=5.0, y=0.05 -> 正好在 X 轴最右端的上方
# ha='right' (右对齐) 确保文字不超出右边界,va='bottom' (底部对齐) 确保它悬浮在轴线上方
ax.text(5.5, 0.05, 'X Axis Label', ha='right', va='bottom', fontsize=10)

# x=0.1, y=1.0 -> 正好在 Y 轴最顶端的右侧
# ha='left' (左对齐) 确保文字往右边长,va='top' (顶部对齐) 让它跟顶端刻度基本平齐
ax.text(0.1, 1.1, 'Y Axis Label', ha='left', va='top', fontsize=10, rotation=0)

ax.set_title("Sine Wave", fontsize=14, pad=20) # 适当加点 pad 别让标题和Y轴标签撞车
ax.legend(loc='upper right') # 激活图例并固定在右上角

# ==============================================================================
# STEP 6: 输出与渲染
# 显示参考: https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.show.html
# ==============================================================================
plt.show() ; # 末尾加分号防止在 Jupyter 中打印内存地址文字

20260616113129162.png 此图就是上述代码绘制出来的。其中各行代码已经添加了注释,且其具体用法与解释也已经标出matplotlib官网对应的api解释页面的链接。 还有一点需要注意到,我并没有显式指出线条颜色、线条宽度、画布背景色等参数,它就已经有了默认值,这就是 rcParams全局参数在起作用。

2.1.2 rcParams介绍

  • 基本介绍
    • rcParams 全称是 Runtime Configuration Parameters(运行时配置参数)。在 Matplotlib 中,rcParams 是整个库的“中央控制大厅”或“全局配置字典”。

    • 当用户没有在代码(比如 plt.figure())里显式指定颜色、字体大小、线条粗细时,Matplotlib 就会像翻阅“员工守则”一样去翻阅 rcParams,看看里面默认规定了什么,然后使用这些默认配置参数值画图。

    • 关于rcParams全局参数的所有默认值,可以在此官网页面查看:matplotlib-configuration-rcparam。此外,已经安装到本地的matplotlib可以在此文件中查看其默认值:{python环境}\Lib\site-packages\matplotlib\mpl-data\matplotlibrc。个人认为,如果没有特殊要求,就不用特意记忆(配置实在太多难以记忆)与关注,有需要时就查阅文档找到对应的配置名,手动设置新值以覆盖默认值。


  • 它在代码里长什么样?
    • 在底层,rcParams 其实就是一个继承自 Python 原生字典(dict)的对象。它里面用“键值对(Key-Value)”存储了成百上千条控制画图外观的默认规则。

    • 例如,官方文档里写的那句: default: rcParams["figure.facecolor"] (default: 'white') 在 Python 后台翻译过来就是:

1
2
3
4
5
import matplotlib as mpl

# 查看当前的全局配置中画布背景色默认是什么
print(mpl.rcParams["figure.facecolor"]) # 输出: 'white'
...

  • 核心作用
    • 免去重复写参数的麻烦。如果要在同一个脚本里画 10 张图,并且希望它们的背景统统都是灰色、线条都是虚线。如果不用 rcParams,就必须在每个 plt.figure(facecolor='gray') 里都写一遍。 有了 rcParams,只需要在脚本最开头改一次“全局出厂设置”,后面所有的绘图都会自动继承。
    • 让 Matplotlib 支持中文。在用 Matplotlib 画图时,要让图表中正常显示中文,就要修改rcParams 里的默认字体集(配置名:'font.sans-serif',默认值是一个列表)为'SimHei'。
    • 支撑“一键换肤”功能。使用此功能可以一键切换成 ggplotseaborn 甚至“暗黑模式”的炫酷换肤功能。这个换肤功能的本质,就是 Matplotlib 官方写好了一个个配置文件,当调用它时,它会批量、打包地把全新的参数覆盖进 rcParams 字典里。
1
2
3
4
5
6
7
8
9
10
import matplotlib as mpl
"""
mpl.rcParams['font.family'],表示全局默认的字体大类。它的默认值是:['sans-serif'],表示所有图表文本都使用 无衬线字体(sans-serif) 大类

mpl.rcParams['font.sans-serif'],表示具体的字体备选推荐列表。它的默认值跟字体大类有关,字体大类是默认值时,它的值是:['DejaVu Sans', 'Bitstream Vera Sans', 'Computer Modern Sans Serif', 'Lucida Grande', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', 'sans-serif'],也是一个列表。
它是一份从左到右的“待选名单”。当要在图表里写字时,它会先在操作系统里找 `DejaVu Sans`,如果找到了就用它画;如果系统里没有(比如 Windows 默认通常没有),它就会划掉,接着找第二个 `Bitstream Vera Sans`……以此类推,直到找到电脑里真正安装了的字体为止。
但是这份默认的“待选名单”中全部是西文字体,不支持中文。要支持中文就要使用:['SimHei'],字体大类仍使用默认的 ['sans-serif'] 即可。
"""
mpl.rcParams['font.sans-serif'] = ['SimHei']
...

  • 配置是从哪里被加载进来的? 当用户启动 Python 环境并 import matplotlib 的那一刹那,系统会按照以下优先级顺序去寻找并读取配置,最终合并进内存中的 rcParams 字典:
    • 当前代码运行期修改(最高优先级):即在代码里写的 mpl.rcParams[...] = ...
    • 当前工作目录下的文件:如果当前文件夹下有一个名为 matplotlibrc 的文件,系统会读取它。
    • 用户个人家目录下的文件:存放在 C:\Users\你的用户名\.matplotlib\matplotlibrc(或者是 .config/matplotlib/matplotlibrc)。
    • Matplotlib 安装包自带的出厂设置:也就是{python环境}\Lib\site-packages\matplotlib\mpl-data\matplotlibrc 这个纯文本文件。官方文档里写的那些默认全局配置就是写在这个源文件里的(比如figure.facecolor: white)。

2.2 pyplot.plot常用调用形式

官网文档:matplotlib.pyplot.plot.html 它是一个用来绘制拆线图的方法。官方源码提供的matplotlib.pyplot.plot 函数的声明如下:

1
matplotlib.pyplot.plot(*args, scalex=True, scaley=True, data=None, **kwargs)

其中第一个参数args是可变位置参数背包,看了后不明所以,光看声明的话,看了跟没看一样。所以matplotlib官网提供了几个常用的调用签名(相当于是给出了第一个参数args的一些具体例子):

1
2
3
4
5
6
#(1)调用签名1:这是最常用的调用形式(推荐!规范、优雅的 OO 风格写法)
plot([x], y, [fmt], *, data=None, **kwargs)
#(2)调用签名2:这种调用形式有点像是对“调用签名形式(1)”的扩展,它允许用户在单行代码里,一口气画出N条不同的线,即把多条线揉在一行写,会导致代码变得极其冗长、难以阅读。还是推荐分成N个语句来写,就是说推荐拆成N个“调用签名形式(1)”对应的语句。
plot([x], y, [fmt], [x2], y2, [fmt2], ..., **kwargs)
#(3)调用签名3:使用带标签的数据。它是matplotlib在老api(上述两种)的基础上,为了兼容python数据分析生态爆发导致Pandas流行而推出的兼容的调用签名
plot('xlabel', 'ylabel', data=obj)

下面分别对上述3种常见调用签名进行说明与举例(其中重点是调用签名1与调用签名3)。

2.2.1 调用签名1

1
2
#(1)调用签名1:这是最常用的调用形式(推荐!规范、优雅的 OO 风格写法)
plot([x], y, [fmt], *, data=None, **kwargs)

说明:

  • 其中第一个参数[x]:表示x轴上的数据,是可选的,可传可不传。当不传它时,因为y是必传的,系统会根据y的值来推断确认参数x的值,使用的方法叫做“序列的自然索引(Index)”。此处不给出此方法的严格定义,它在此处大概的意思与效果“就是根据y的值调用 NumPy 的 np.arange() 机制来为 X 轴量身定制一组“自然索引(Index)”,参数x的具体信息或确认规则如下:
    • 元素个数:严格与 y 的元素个数保持 1:1 完全一致。
    • 元素类型:默认生成标准的 64 位整型(numpy.int64)。
    • 起点值:永远从 0 开始。
    • 步长:固定为 1
    • 终点值:终点值为 N - 1(其中 \(N\)y 的元素个数)。在数学区间上,它就是一个左闭右开的集合 \([0, N)\)
  • 参数y:表示y轴上的数据,它必须要传。就是说当只传入一个坐标轴上的数据时,系统默认就认为传入了y坐标轴上的数据。
  • 是 Format String(格式字符串) 的缩写。它是一个极度精简的“快捷皮肤代码”,用几个极度精简的字符同时指定线条的特征如颜色、线型和标记点形状
  • *(星号占位符):这是一个 Python 语法强制限止符。意味着星号后面的所有参数,用户在调用时必须写出参数名,即必须使用关键字参数。
  • data参数:在使用此调用签名1这种用户签名时,这个参数不需要传值。传值了就变成了调用签名3 ,此时需要对应改变第1、2参数的值。
  • kwargs(关键字参数):它是一个大杂烩。在[fmt] 里没法用一两个字符表达的、精细的外观控制参数,就通过 kwargs 来控制,这些控制参数有很多,比如线条宽度(linewidth)和图例名字(label),具体见:matplotlib-pyplot-plot文档

举例:

1
2
3
4
5
6
7
8
9
10
import matplotlib.pyplot as plt

y = range(1,4)

#x值根据y推断出来:为[0,1,...,N-1] 。其中N为y的长度3
fig, ax = plt.subplots()

ax.plot(y)

plt.show();
Pasted image 20260614210138

2.2.2 调用签名2

根据分析,个人认为,这种调用签名形式不推荐。此处记录下,以便后续看到此用法也要认识。

1
2
#(2)调用签名2:这种调用形式有点像是对“调用签名形式(1)”的扩展,它允许用户在单行代码里,一口气画出N条不同的线,即把多条线揉在一行写,会导致代码变得极其冗长、难以阅读。还是推荐分成N个语句来写,就是说推荐拆成N个“调用签名形式(1)”对应的语句。
plot([x], y, [fmt], [x2], y2, [fmt2], ..., **kwargs)

它允许用户在单行代码里,一口气画出N条不同的线,即把多条线通过一个语句画出来,会导致代码变得极其冗长、难以阅读。一旦报错,很难一眼看出是第几条线的数据出了问题。所以推荐分成N个语句来写,就是说推荐拆成N个“调用签名1形式”对应的语句。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 导入 matplotlib 库中的 pyplot 模块,并简写为 plt,用于后续的图表绘制与展示
import matplotlib.pyplot as plt

# 导入 NumPy 科学计算库,并简写为 np,用于高效生成高密度的矩阵数据
import numpy as np

# 使用 np.arange(start, stop, step) 生成一个等差数列的一维数组作为 X 轴坐标
# 起点为 1,终点为 2.05(左闭右开,实际不包含 2.05,所以最大到 2.00),步长为 0.05
# 这行代码一共生成了 21 个高密度的采样点:[1.0, 1.05, 1.1, ..., 1.95, 2.0]
x = np.arange(1, 2.05, 0.05)

# 根据 X 轴的数组,利用 NumPy 的广播机制,点对点地计算出三组对应的 Y 轴坐标数据
y1 = x # 第一条线的数据:y = x(斜率为 1 的直线)
y2 = 2*x # 第二条线的数据:y = 2x(斜率为 2 的直线)
y3 = 3*x # 第三条线的数据:y = 3x(斜率为 3 的直线)

# 调用 plt.subplots() 创建一个画布(fig)和一个子图坐标系(ax)
# 这是 Matplotlib 最推荐的面向对象写法,后续的所有画图动作都将在 ax 上展开
fig, ax = plt.subplots()

# 在子图 ax 上绘制折线图,采用的是官方第二种多线签名模式:plot(x1, y1, x2, y2, x3, y3)
# 传入参数必须满足“偶数个、成对出现”的铁律,这里一口气画出了三条独立的直线
ax.plot(x, y1, x, y2, x, y3)

# 全局渲染指令,负责把内存中构建好的完整图表在屏幕上弹窗或在 Notebook 中展现实体图像
plt.show()

拆分成N个“调用签名1形式”对应的语句,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 导入 matplotlib 库中的 pyplot 模块,并简写为 plt,用于图表的创建、控制和最终显示
import matplotlib.pyplot as plt

# 导入 NumPy 科学计算库,并简写为 np,用于高效生成和处理高密度的多维数组数据
import numpy as np

# 使用 np.arange(start, stop, step) 创建一个等差数列作为三条线的公共 X 轴坐标
# 起点为 1,终点为 2.05(左闭右开区间,实际不包含 2.05,最高到 2.00),步长为 0.05
# 这行代码会在内存中生成包含 21 个高密度数据点的数组:[1.0, 1.05, 1.1, ..., 1.95, 2.0]
x = np.arange(1, 2.05, 0.05)

# 结合 X 轴数组,利用 NumPy 的数组广播机制,分别计算出三组对应的 Y 轴数据
y1 = x # 第一条线的数据公式:y = x(斜率为 1 的直线数据)
y2 = 2*x # 第二条线的数据公式:y = 2x(斜率为 2 的直线数据)
y3 = 3*x # 第三条线的数据公式:y = 3x(斜率为 3 的直线数据)

# 调用 subplots() 函数创建一个画布(fig)和该画布上的一个子图坐标系(ax)
# 这是面向对象风格(OO-style)的核心,后续所有图形的绘制和微调都将作用在 ax 对象上
fig, ax = plt.subplots()

# 在当前的子图坐标系 ax 上分别绘制三条独立的折线
# 每一行严格遵循 plot(x, y) 形式,传入一组明确的横纵坐标,由系统内部自动完成连线渲染
ax.plot(x, y1) # 绘制第一条直线(默认会使用系统自带的第一种主题颜色,通常是蓝色)
ax.plot(x, y2) # 绘制第二条直线(默认会使用系统自带的第二种主题颜色,通常是橙色)
ax.plot(x, y3) # 绘制第三条直线(默认会使用系统自带的第三种主题颜色,通常是绿色)

# 全局绘图渲染指令,负责将内存中 ax 坐标系里已经画好的三条线真刀真枪地渲染出来
# 它会唤起系统的图形窗口或者在 Jupyter Notebook 中直接输出最终的实体可视化图像
plt.show()
Pasted image 20260614210256

2.2.3 调用签名3

按照上述已有知识进行画图,必须先把数据从字典里解包出来,写成 ax.plot(my_dict['x']、my_dict['y'])之类的形式以分别给x轴、y轴传递数据。 随着 Python 数据分析生态的爆发,Pandas DataFrame 变成了大家的家常便饭。为了兼容老用户的习惯,同时不增加新的函数名(比如再造一个 plot_dataframe() 之类的函数,就会让 API 变得臃肿),官方决定对原有的 plot 进行了“兼容性改造”:

  • 如果不传 data,plot方法像个传统的数学计算器,去接收数字序列(就是 调用签名1调用签名2 两种形式)。

  • 如果传了 data,plot方法就切换成大管家式的兼容性模式,允许用户用字符串列名来从data中获取对应列的数据(这就是 调用签名3)。

  • 调用签名3的形式如下:

1
2
3
4
5
6
"""
data 参数支持所有可索引对象。例如,它可以是字符串dict、数组 pandas.DataFrame或结构化的 NumPy 数组。

'xlabel'与'ylabel'是data对象中的可索引键
"""
plot('xlabel', 'ylabel', data=obj)
  • 调用签名3的示例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import matplotlib.pyplot as plt
import pandas as pd

# 模拟一个从 CSV 或数据库中读取出来的 Pandas DataFrame 表格
df = pd.DataFrame({
'timestamp': [10, 20, 30, 40, 50],
'temperature': [22.5, 23.1, 21.8, 24.0, 23.5],
'humidity': [60, 55, 58, 62, 61]
})

# 创建画布与子图
fig, ax = plt.subplots()

# 传入相同的 data 数据源,通过更换字符串列名,轻松在一张图上画两条线
# 第一条线:横轴是时间,纵轴是温度,快捷线型为蓝色实线 'b-'
ax.plot('timestamp', 'temperature', 'b-', data=df, label='Temperature (°C)')

# 第二条线:横轴依然是时间,纵轴切换为湿度,快捷线型为绿色虚线 'g--'
ax.plot('timestamp', 'humidity', 'g--', data=df, label='Humidity (%)')

ax.set_xlabel('Time (s)')
ax.set_title('Sensor Data Summary')
ax.legend()

plt.show()
Pasted image 20260614213404

2.3 容器与框架层常用操作

2.3.1 画布操作

官网文档链接:matplotlib.pyplot.figure.html 创建一个新画布或激活一个已存在画布的方法声明如下:

1
matplotlib.pyplot.figure(num=None, figsize=None, dpi=None, *, facecolor=None, edgecolor=None, frameon=True, FigureClass=<class 'matplotlib.figure.Figure'>, clear=False, **kwargs)

其中主要或常用的参数如下:

  • num=None:画布序号,整数/字符串/Figure实例/SubFigure实例。这个序号在matplotlib后台管理的所有的画布中是唯一的,就是说每个画布都有一个唯一的序号。个人认为一般不用设置此参数,而是交由系统自动管理。如果传递了此参数,则它的机制如下:

    • 如果不传递此参数,则是新创建画布,系统会自动为新创建的画布自动指定一个在整个系统中递增的且唯一的整数序号,默认从1开始往上增加,寻找当前未被占用的最小正整数。
    • 如果传递的num值是一个已存在画布的序号,则此序号对应的画布就被激活并返回此画布对象;如果传递的num值不代表任何已存在的画布,则一个新画布被创建、激活并返回,且此画布的序号就为此num值。
    • 如果传递的num参数值是一个字符串,也是新创建画布,传递的字符串就是画布标签和窗口标题,这个字符串本身就是这个画布的唯一序号(标识)。
    • 如果传递的是Figure实例,当此实例已经被pyplot所追踪管理到,则此画布被激活、返回;如果此实例还没有被pyplot所追踪管理到,则它将被追踪管理到,并被激活、返回。
    • 如果传递的是SubFigure实例,则它的父画布被激活、返回。
  • figsize=None:画布尺寸,参数形式与类型是(float, float) or (float, float, str),不显式传递时使用默认值(6.4, 4.8)。具体情况如下:

    • 如果传递形式是(float, float),则它们分别代表画布的宽、高,单位是英寸。
    • 如果传递形式是(float, float, str),则它们分别代表画布的宽、高、单位,单位只能是"inch"/"cm"/"px"之一,它们分别代表英寸/厘米/像素。
    • 宽度或高度可以使用None,此时为None参数就会使用其默认值。即如果宽度为None即(None, height)则使用(6.4, height),如果高度为None即(width, None)则使用(width, 4.8),如果两者同时为None即(None, None)则使用(6.4, 4.8)。
  • facecolor=None:画布背景颜色。默认值是rcParams["figure.facecolor"],而此全局配置的默认值是'white'。

  • layout=None:画布内元素的布局机制。如果使用默认值None,则系统此时会读取 rcParams["figure.layout_engine"](出厂默认为 'none'),不使用任何布局算法,可以把此参数设置为matplotlib已提供的布局机制之一,也可使用自定义布局机制,主要用它定位绘图元素的布局机制,旨在避免坐标轴装饰元素(标签、刻度等)重叠。可选的值有:{'constrained', 'compressed', 'tight', 'none', LayoutEngine, None}官方推荐在一般情况下直接使用'constrained',使用此参数值时,系统会自动调整坐标系尺寸以避免坐标轴装饰元素重叠,能够处理复杂的子图布局与颜色块如图例;'LayoutEngine'就是自定义布局机制。

  • clear=False:当传递一个已存在的 num(无论是整数还是字符串)来激活老画布时,如果 clear=True,老画布会被清空重练,激活后老画布后然后返回此画布;如果 clear=False(默认值),老画布会被原封不动地唤醒并返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
### 画布学习:创建两个画布 
import matplotlib.pyplot as plt
import numpy as np

# ==============================================================================
# STEP 1: 准备数据 (Data Preparation)
# ==============================================================================
x1 = np.linspace(0, 2, 100)
y1 = x1 ** 2
y2 = np.arange(1, 5, 1)

# ==============================================================================
# STEP 2: 创建画布与坐标系/子图 (Figure & Axes)
# ==============================================================================
fig1, ax1 = plt.subplots(num=1, figsize=(6, 4.8, "in"), facecolor="w", layout="constrained")
fig2, ax2 = plt.subplots(num=2, figsize=(6.4, 5.2), facecolor="gray", layout="compressed")

# ==============================================================================
# STEP 3: 绘制核心图表 (Plotting)
# ==============================================================================
ax1.plot(x1, y1, label='Quadratic Curve', linewidth=2, linestyle='-')
ax2.plot(y2, label='Line', linewidth=2, linestyle='--')

# ==============================================================================
# STEP 4: 装饰与标注 (Customizing Labels, Title & Legend)
# ==============================================================================
ax1.set_xlabel('X1 Axis Label')
ax1.set_ylabel('Y1 Axis Label')
ax1.set_title('Standard Template Simple Plot1')
ax1.legend(loc='upper left')

# ==============================================================================
# STEP 5: 输出与渲染 (Display & Save)
# ==============================================================================
plt.show() ;

363320

2.3.2 子图操作

2.3.2.1 在画布中创建多个均匀子图

官方文档:matplotlib-pyplot-subplots。此方法常在画布网格中各子图均匀分布时使用(比如 4 张图均分画布)。方法声明如下:

1
matplotlib.pyplot.subplots(nrows=1, ncols=1, *, sharex=False, sharey=False, squeeze=True, width_ratios=None, height_ratios=None, subplot_kw=None, gridspec_kw=None, **fig_kw)

matplotlib中创建多子图时,会将整个画布划分成m行n列的网格,每个子图可能占据其中1到多个网格。

此方法常用参数解释(如果没有出现在调用签名中,那就是kwargs关键字参数):

  • rows:整数,表示画布分成几行。
  • ncols:整数,表示画布分成几列。

示例1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import matplotlib.pyplot as plt
import numpy as np

# ==============================================================================
# STEP 1: 准备数据 (Data Preparation)
# ==============================================================================
x = np.linspace(0, 2, 100)
y = x ** 2

# ==============================================================================
# STEP 2: 创建画布与坐标系/子图 (Figure & Axes)
# ==============================================================================
# 相当于plt.subplots(1,1),画布中只有一行一列,即一个子图
fig, ax = plt.subplots()

# ==============================================================================
# STEP 3: 绘制核心图表 (Plotting)
# ==============================================================================
ax.plot(x, y, label='Quadratic Curve', linewidth=2, linestyle='-')

# ==============================================================================
# STEP 4: 装饰与标注 (Customizing Labels, Title & Legend)
# ==============================================================================
ax.set_xlabel('X Axis Label')
ax.set_ylabel('Y Axis Label')
ax.set_title('Standard Template Simple Plot')
ax.legend(loc='upper left')

# ==============================================================================
# STEP 5: 输出与渲染 (Display & Save)
# ==============================================================================
plt.show() ;

示例2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import matplotlib.pyplot as plt
import numpy as np

# ==============================================================================
# STEP 0: 修改全局设置 (rcParams Settings)
# ==============================================================================
# 设置具体字体为黑体
plt.rcParams['font.sans-serif'] = ['SimHei']
# 解决负号 '-' 显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False

# STEP 1: 准备数据
x = np.linspace(0, 2 * np.pi, 100)

# STEP 2: 一步到位创建 2x2 矩阵网格
fig, axs = plt.subplots(nrows=2, ncols=2, figsize=(10, 8), layout='constrained')
# print(type(axs)) #此处 axs 的类型是numpy.ndarray

# ==============================================================================
# 1. 操控 [0, 0] 位置 —— 左上角格子
# ==============================================================================
axs[0, 0].plot(x, np.sin(x), color='tab:blue', linewidth=2)
axs[0, 0].set_title('子图 [0, 0]: 正弦波')
axs[0, 0].grid(True, linestyle='--', alpha=0.6)

# ==============================================================================
# 2. 操控 [0, 1] 位置 —— 右上角格子
# ==============================================================================
axs[0, 1].plot(x, np.cos(x), color='tab:orange', linewidth=2)
axs[0, 1].set_title('子图 [0, 1]: 余弦波')

# ==============================================================================
# 3. 操控 [1, 0] 位置 —— 左下角格子
# ==============================================================================
axs[1, 0].plot(x, np.sin(2 * x), color='tab:green', linewidth=2)
axs[1, 0].set_title('子图 [1, 0]: 高频正弦波')

# ==============================================================================
# 4. 操控 [1, 1] 位置 —— 右下角格子
# ==============================================================================
axs[1, 1].plot(x, np.sin(x / 2), color='tab:red', linewidth=2)
axs[1, 1].set_title('子图 [1, 1]: 低频正弦波')

# STEP 4: 统一微调全局大标题
fig.suptitle('均匀矩阵网格多子图(2x2)', fontsize=16, weight='bold')

# STEP 5: 渲染输出
plt.show();


2.3.2.2 向画布中逐渐添加子图

2.3.2.2.1 figure-add-subplot(推荐)

官方文档:matplotlib-figure-figure-add-subplot。方法声明如下:

1
Figure.add_subplot(*args, **kwargs)

作用是在图中添加一个元素,作为子图布局的一部分。

此方法常在画布网格中各子图不均匀分布时使用。常用调用签名如下:

1
add_subplot(nrows, ncols, index, **kwargs)

其中常用参数解释如下:

  • nrows, ncols, index:3个整数或一个SubplotSpec对象,表示表格画布有几行几列个网格、添加的子图的索引是多少。默认值是(1, 1, 1)。实际传递整数时也有两种形式,如下。

    • 形如fig.add_subplot(nrows, ncols, index),表示画布被分成了nrows行、ncols列 的网格,当前向画布中添加的子图所对应的网格的索引(添加的子图可能跨越多个网格。网格索引从1开始,从上到下、从左往右每隔一个网格索引递增1)。推荐此形式。

    • 直接将nrows, ncols, index实际上对应的3个数字合并成一个数字,比如fig.add_subplot(235),其实等价于fig.add_subplot(2,3,5)。此形式只能在网格总数不多于9个时使用,因为10无法在上述参数中准确表达。

    • 传递一个SubplotSpec对象时,暂时不关注。

  • projection:kwargs关键字参数可传递的实际参数,表示添加的子图所使用坐标系类型。有如下列表中有元素可选:{None, 'aitoff', 'hammer', 'lambert', 'mollweide', 'polar', 'rectilinear'},可不传递使用默认值None,当前与传递'rectilinear'(直角坐标系)一样的效果,另外'polar'表示极坐标系。

  • sharex, sharey:kwargs关键字参数可传递的实际参数,分别表示添加的子图与这两个参数值共享x坐标或y坐标,值类型是坐标系。设置后将与共享坐标轴的轴具有相同的范围、刻度和比例。

  • label:kwargs关键字参数可传递的实际参数,表示添加的子图的名称,值类型是字符串。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np

# ==============================================================================
# STEP 0: 修改全局设置 (rcParams Settings)
# ==============================================================================
# 设置具体字体为黑体
mpl.rcParams['font.sans-serif'] = ['SimHei']
# 解决负号 '-' 显示为方块的问题
mpl.rcParams['axes.unicode_minus'] = False

# 准备模拟数据
t = np.linspace(0, 2 * np.pi, 200)
y_sin = np.sin(t)
y_cos = np.cos(t)

# STEP 1: 创建顶级画布
fig = plt.figure(figsize=(10, 8), layout="constrained")

# ==============================================================================
# 示例 1: 跨格子的子图 (利用文档提到的元组 index 机制)
# 假装切分成 2 行 2 列的网格,但让第一个子图占据第一行的左右两个格子 (1, 2)
# ==============================================================================
ax1 = fig.add_subplot(2, 2, (1, 2))
ax1.plot(t, y_sin, color="tab:blue", linewidth=2)
ax1.set_title("子图 1: 宽的顶部子图 (跨越索引1与2)")

# ==============================================================================
# 示例 2: 紧凑 3 位数简写形式 (对应文档 235 逻辑)
# 在刚才 2 行 2 列的底座下,第 2 行的左边格子就是第 3 个位置
# ==============================================================================
ax2 = fig.add_subplot(223) # 等价于 (2, 2, 3)
ax2.plot(t, y_cos, color="tab:orange", linewidth=2)
ax2.set_title("子图 2: 左下子图 (三位数缩写法)")

# ==============================================================================
# 示例 3: 更改投影机制 (对应文档 projection='polar' 逻辑)
# 在 2 行 2 列网格中,第 4 个位置(右下角)创建一个极坐标系
# ==============================================================================
ax3 = fig.add_subplot(2, 2, 4, projection='polar')
ax3.plot(t, t, color="tab:green", linewidth=2) # 绘制阿基米德螺旋线
ax3.set_title("子图 3: 右下子图 (极坐标子图)", pad=15)

# STEP 6: 显示画布
plt.show();

官网还有很多官方示例:examples-using-matplotlib-figure-figure-add-subplot

2.3.2.2.2 pyplot-subplot

官方文档:matplotlib-pyplot-subplot。方法声明如下:

1
matplotlib.pyplot.subplot(*args, **kwargs)

作用是在图中添加一个元素,作为子图布局的一部分。

此方法常在画布网格中各子图不均匀分布时使用。常用调用签名如下:

1
subplot(nrows, ncols, index, **kwargs)

其中常用参数解释如下:

  • nrows, ncols, index:3个整数或一个SubplotSpec对象,表示表格画布有几行几列个网格、添加的子图的索引是多少。默认值是(1, 1, 1)。索引就把的子图如果不存在则新创建并返回、如果已存在直接激活并返回。实际传递整数时也有两种形式,如下。

    • 形如fig.add_subplot(nrows, ncols, index),表示画布被分成了nrows行、ncols列 的网格,当前向画布中添加的子图所对应的网格的索引(添加的子图可能跨越多个网格。网格索引从1开始,从上到下、从左往右每隔一个网格索引递增1)。推荐此形式。

    • 直接将nrows, ncols, index实际上对应的3个数字合并成一个数字,比如fig.add_subplot(235),其实等价于fig.add_subplot(2,3,5)。此形式只能在网格总数不多于9个时使用,因为10无法在上述参数中准确表达。

    • 传递一个SubplotSpec对象时,暂时不关注。

  • projection:kwargs关键字参数可传递的实际参数,表示添加的子图所使用坐标系类型。有如下列表中有元素可选:{None, 'aitoff', 'hammer', 'lambert', 'mollweide', 'polar', 'rectilinear'},可不传递使用默认值None,当前与传递'rectilinear'(直角坐标系)一样的效果,另外'polar'表示极坐标系。

  • sharex, sharey:kwargs关键字参数可传递的实际参数,分别表示添加的子图与这两个参数值共享x坐标或y坐标,值类型是坐标系。设置后将与共享坐标轴的轴具有相同的范围、刻度和比例。

  • label:kwargs关键字参数可传递的实际参数,表示添加的子图的名称,值类型是字符串。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np

# ==============================================================================
# STEP 0: 修改全局设置 (rcParams Settings)
# ==============================================================================
# 设置具体字体为黑体
mpl.rcParams['font.sans-serif'] = ['SimHei']
# 解决负号 '-' 显示为方块的问题
mpl.rcParams['axes.unicode_minus'] = False

# 准备模拟数据
t = np.linspace(0, 2 * np.pi, 200)
y_sin = np.sin(t)
y_cos = np.cos(t)

# STEP 1: 创建顶级画布
fig = plt.figure(figsize=(10, 8), layout="constrained")

# ==============================================================================
# 示例 1: 跨格子的子图 (利用文档提到的元组 index 机制)
# 假装切分成 2 行 2 列的网格,但让第一个子图占据第一行的左右两个格子 (1, 2)
# ==============================================================================
# ax1 = fig.add_subplot(2, 2, (1, 2))
# ax1 = plt.subplot(2, 2, (1, 2), figure=fig) 写法会报错。 官网文档显示此处可以传递figure参数,但实际调用时报错提示“TypeError: add_subplot() got an unexpected keyword argument 'figure'”
# 所以此处不传递figure参数,系统会把子图自动添加到最后创建的画布上
ax1 = plt.subplot(2, 2, (1, 2))
ax1.plot(t, y_sin, color="tab:blue", linewidth=2)
ax1.set_title("子图 1: 宽的顶部子图 (跨越索引1与2)")

# ==============================================================================
# 示例 2: 紧凑 3 位数简写形式 (对应文档 235 逻辑)
# 在刚才 2 行 2 列的底座下,第 2 行的左边格子就是第 3 个位置
# ==============================================================================
# ax2 = fig.add_subplot(223) # 等价于 (2, 2, 3)
ax2 = plt.subplot(2, 2, 3)
ax2.plot(t, y_cos, color="tab:orange", linewidth=2)
ax2.set_title("子图 2: 左下子图 (三位数缩写法)")

# ==============================================================================
# 示例 3: 更改投影机制 (对应文档 projection='polar' 逻辑)
# 在 2 行 2 列网格中,第 4 个位置(右下角)创建一个极坐标系
# ==============================================================================
# ax3 = fig.add_subplot(2, 2, 4, projection='polar')
ax3 = plt.subplot(2, 2, 4, projection='polar')
ax3.plot(t, t, color="tab:green", linewidth=2) # 绘制阿基米德螺旋线
ax3.set_title("子图 3: 右下子图 (极坐标子图)", pad=15)

# STEP 6: 显示画布
plt.show();

画出来的图跟[[#2.3.2.2.1 figure-add-subplot]] 中示例代码绘制出来的图一样。

2.3.3 边框线操作(坐标轴设置)

关于边框线类型Spine的官网文档:module-matplotlib.spines。但在实际使用过程中,一般不会通过Spine类直接创建实例,而是通过坐标系对象来获取,如下(重点查看“STEP 3: 调整边框”部分内容)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# 导包
import matplotlib.pyplot as plt
import numpy as np

# ==============================================================================
# STEP 1: 准备数据
# ==============================================================================
x = np.arange(-5, 5, 0.1)
y = np.sin(x)

# ==============================================================================
# STEP 2: 创建画布与坐标系/子图
# ==============================================================================
# 使用 layout='constrained' 确保移动坐标轴后,刻度标签不会溢出大画布
# fig, ax = plt.subplots(figsize=(6, 5), layout='constrained')
fig, ax = plt.subplots()

# ==============================================================================
# STEP 3: 调整边框位置,使零点坐标在 (0,0) 处。此操作可选,调整零点坐标到 (0,0) 处只为了更符合我们的画图习惯
# ==============================================================================
# 1. 将上方和右方的边框线分别设置为红色、透明/隐形
ax.spines['top'].set(color='red') # 等价于 ax.spines['top'].set_color('red')
ax.spines['top'].set(linewidth=2)
ax.spines['top'].set(linestyle='--')
ax.spines['right'].set_color('none')

# 2. 将下边框(X轴)和左边框(Y轴)强制移动到数据坐标的 0 点位置
ax.spines['bottom'].set_position(('data', 0))
ax.spines['left'].set_position(('data', 0))

# ==============================================================================
# STEP 4: 绘制核心图表
# ==============================================================================
ax.plot(x, y, label='sin(x)', linewidth=2, linestyle='-')

# ==============================================================================
# STEP 5: 装饰与标注
# ==============================================================================
# 1. X轴、Y轴标签传入空字符串(默认就是空字符串)
ax.set_xlabel('')
ax.set_ylabel('')

# 2. 手动在第一象限指定位置“钉”上文本
# x=5.0, y=0.05 -> 正好在 X 轴最右端的上方
# ha='right' (右对齐) 确保文字不超出右边界,va='bottom' (底部对齐) 确保它悬浮在轴线上方
ax.text(5.5, 0.05, 'X Axis Label', ha='right', va='bottom', fontsize=10)

# x=0.1, y=1.0 -> 正好在 Y 轴最顶端的右侧
# ha='left' (左对齐) 确保文字往右边长,va='top' (顶部对齐) 让它跟顶端刻度基本平齐
ax.text(0.1, 1.1, 'Y Axis Label', ha='left', va='top', fontsize=10, rotation=0)

ax.set_title("Sine Wave", fontsize=14, pad=20) # 适当加点 pad 别让标题和Y轴标签撞车
ax.legend(loc='upper right') # 激活图例并固定在右上角

# ==============================================================================
# STEP 6: 输出与渲染
# ==============================================================================
plt.show() ; # 末尾加分号防止在 Jupyter 中打印内存地址文字

将上述"STEP 3: 调整边框"单独拿出来解释:

1
2
3
4
5
6
7
8
9
10
11
12
13
...
# ==============================================================================
# STEP 3: 调整边框。使零点坐标在 (0,0) 处。此操作可选,调整零点坐标到 (0,0) 处只为了更符合我们的画图习惯
# 官网参考: https://matplotlib.org/stable/api/spines_api.html#matplotlib.spines.Spine.set_position
# ==============================================================================
# 1. 将上方和右方的边框线分别设置为红色、透明/隐形
ax.spines['top'].set(color='red') # 等价于 ax.spines['top'].set_color('red')
ax.spines['right'].set_color('none')

# 2. 将下边框(X轴)和左边框(Y轴)强制移动到数据坐标的 0 点位置
ax.spines['bottom'].set_position(('data', 0))
ax.spines['left'].set_position(('data', 0))
...

这里只讨论直角坐标系。一个坐标系共有上下左右4个边框线,它们在坐标系对象的是通过一个字典保存的,获取它们的方法分别就是:ax.spines['top']、ax.spines['bottom']、ax.spines['left']、ax.spines['right']

拿到具体边框线对象后,就通过set方法或set_xxx方法来修改某属性的默认值。比如要修改上边框线的颜色为红色可以这样写:

1
2
3
ax.spines['top'].set(color='red')
# 或者下面这样写。就是有一个属性名xxx就有一个对应的set_xxx方法
ax.spines['top'].set_color('red')

边框线的set方法解释及所有支持的属性见官网文档:matplotlib.spines.Spine.set。 常见的属性名及其描述如下:

Property Description
alpha 表示不透明度,越小越透明。类型是浮点数或None,范围在[0.0,1.0],为None时表示完全不透明,
color 边框线颜色
linewidth 边框线的粗细(宽度),浮点数或整数,单位磅
linestyle 设置边框线的线型,字符串如 '-', '--', ':'
visible 控制该条边框线是否显示。值为布尔值: True/False
position 边框位置

2.4 数据表现层常用操作

这里的数据表现层就是描述图中的线条与标记点,就是使用matplotlib.pyplot.plot(...)在某坐标系中绘制图的操作。前面也已经解释过其调用签名,参见”[[#2.2 pyplot.plot常用调用形式]]“章节,此章节在将此基础上展开重点再描述一些绘制图时一些参数参数及操作。

此章节以下内容都基于前述”调用签名1”形式来进行描述。回忆下其形式如下:

1
2
#(1)调用签名1:这是最常用的调用形式(推荐!规范、优雅的 OO风格(Object-Oriented 风格) 写法)
plot([x], y, [fmt], *, data=None, **kwargs)

前面在“[[#2.2.1 调用签名1]]”章节中已经对此方法中的部分关键参数或常用参数进行了简单解释,以下重点讲解这两个参数:fmt、kwargs。

2.4.1 pyplot.plot方法的fmt参数

它是 Format String(格式字符串) 的缩写。它是一个简写组合,用来快速设置线条基本属性,只包含标记点形状、线条的线型 和 线条颜色。而且fmt参数中设置的线条基本属性,其实也可以被kwargs关键字参数进行设置。

fmt参数值是标记点形状、线条的线型 和 线条颜色 三者简写的组合(最多三个属性值都可以传递,也可以只传递一个或两个属性值,甚至不传)。

1
2
3
4
fmt = '[marker][line][color]'
# [marker] 标记点形状
# [line] 线条的线型
# [color] 线条颜色

官方推荐按照这种顺序组合来传递fmt参数值,其他顺序组合可能引发不确定的结果。


以下这三种属性支持的属性值或常见属性值。

  • marker支持的常用值 Markers支持如下常用值,而且还有其他可用值,见官网。
character description
'.' point marker
',' pixel marker
'o' circle marker
'v' triangle_down marker
'^' triangle_up marker
'<' triangle_left marker
'>' triangle_right marker
  • line支持值
character description
'-' solid line style
'--' dashed line style
'-.' dash-dot line style
':' dotted line style
  • color支持的常用值 不支持中文,如果fmt中只设置颜色可以使用英文颜色全称或十六进制字符串如'#008000'。
character color
'b' blue
'g' green
'r' red
'c' cyan
'm' magenta
'y' yellow
'k' black
'w' white

  • 使用示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np

# ==============================================================================
# STEP 0: 修改全局设置 (rcParams Settings)
# ==============================================================================
# 设置具体字体为黑体
# mpl.rcParams['font.sans-serif'] = ['SimHei']
# 解决负号 '-' 显示为方块的问题
mpl.rcParams['axes.unicode_minus'] = False

# ==============================================================================
# STEP 1: 准备数据 (Data Preparation)
# ==============================================================================
x1 = np.arange(-4, 4, 0.1)
y1 = np.cos(x1)

# ==============================================================================
# STEP 2: 创建画布与坐标系/子图 (Figure & Axes)
# ==============================================================================
fig1, ax1 = plt.subplots(figsize=(6, 4.8, "in"), facecolor="w", layout="constrained")

# ==============================================================================
# STEP 3: 绘制核心图表 (Plotting)
# ==============================================================================
# (1)此处fmt参数值为'o-r',表示marker:o、line:-、color:r,即标记点形状为圆、线条线型为实线、线条颜色为红色
# (2)此处演示了 fmt 参数中已经设置了线型为实线,然后kwargs参数中又重复设置了线型为虚线。最后以kwargs参数中设置的值为准,但执行时会告警提示冗余设置
ax1.plot(x1, y1, 'o-r', label='Cosin Function', linewidth=1, linestyle='--')

# ==============================================================================
# STEP 4: 装饰与标注 (Customizing Labels, Title & Legend)
# ==============================================================================
ax1.set_xlabel('X1 Axis Label')
ax1.set_ylabel('Y1 Axis Label')
ax1.set_title('Cosin Function Plot1')
ax1.legend(loc='upper left')

# ==============================================================================
# STEP 5: 输出与渲染 (Display & Save)
# ==============================================================================
plt.show() ;

2.4.2 pyplot.plot方法的kwargs参数

它关键字参数,是一个大杂烩。在[fmt] 参数里没法用一两个字符表达的、精细的外观控制参数,就通过 kwargs 来控制,这些控制参数有很多,比如线条宽度(linewidth)和图例名字(label),具体见:matplotlib-pyplot-plot文档章节中的“Other Parameters:”部分内容。

以下示例给出了一些常用的特征设置参数(具体plot方法调用时的参数及解释):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#### plot 函数的 kwargs 参数 学习
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np

# ==============================================================================
# STEP 0: 修改全局设置 (rcParams Settings)
# ==============================================================================
# 设置具体字体为黑体
mpl.rcParams['font.sans-serif'] = ['SimHei']
# 解决负号 '-' 显示为方块的问题
mpl.rcParams['axes.unicode_minus'] = False

# ==============================================================================
# STEP 1: 准备数据 (Data Preparation)
# ==============================================================================
x1 = np.arange(-4, 4, 0.1)
y1 = np.cos(x1)

# ==============================================================================
# STEP 2: 创建画布与坐标系/子图 (Figure & Axes)
# ==============================================================================
fig1, ax1 = plt.subplots(figsize=(6, 4.8, "in"), facecolor="w", layout="constrained")

# ==============================================================================
# STEP 3: 绘制核心图表 (Plotting)
# ==============================================================================
# 只使用kwargs参数
ax1.plot(x1, y1, # x/y轴的数据
label='Cosin Function', #线条的名称
linewidth=1, #线条的宽度
alpha=0.5, #同时控制线条与标记点的不透明度,值是[0.0,1.0]之间的浮点数,越小越透明,0时绝对透明即隐形
marker='o', #标记点的形状为圆
linestyle='--', #线条的线型为虚线
color='r', #线条的颜色为红色
#标记点其实大概由现部分组成,里面的圆与圆外面的包裹边缘,它们都有宽度(markersize、markeredgewidth颜色(markerfacecolor、markeredgecolor)。
markerfacecolor='blue', #标记点的里面的圆点的填充色
markeredgecolor='yellow', #标记点里面圆点外面包裹的边缘的颜色
markeredgewidth=2, #标记点里面圆点外面包裹的边缘的宽度
#fillstyle='right', ##标记点里面圆点中只有右半边有颜色(markerfacecolor),左半边中空
)

# ==============================================================================
# STEP 4: 装饰与标注 (Customizing Labels, Title & Legend)
# ==============================================================================
ax1.set_xlabel('X 坐标轴')
ax1.set_ylabel('Y 坐标轴')
ax1.set_title('余弦函数图')
ax1.legend(loc='upper left')

# ==============================================================================
# STEP 5: 输出与渲染 (Display & Save)
# ==============================================================================
plt.show() ;

官网提供了很多示例:examples-using-matplotlib-pyplot-plot

2.5 辅助说明层常用操作

2.5.1 ax.title设置

官网文档链接:matplotlib.axes.Axes.set_title。 每个坐标系都可以设置title,matplotlib中关于此方法的声明如下:

1
Axes.set_title(label, fontdict=None, loc=None, pad=None, *, y=None, **kwargs)

其中主要或常用的参数如下:

  • label:坐标系的标题名称,类型是字符串。
  • loc:坐标系的标题名称显示的位置,类型是字符串,有3个可选值:'center'/'left'/'right',默认是'center'表示title显示在坐标系的上方正中间位置。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import itertools
import matplotlib.pyplot as plt

fontsizes = itertools.cycle([8, 16, 24, 32])

def example_plot(ax):
ax.plot([1, 2])
ax.set_xlabel('x-label', fontsize=next(fontsizes))
ax.set_ylabel('y-label', fontsize=next(fontsizes))
# 将坐标系的标题设置为“Title”,字体大小是24,位置是坐标系的上方靠左
ax.set_title('Title', fontsize=next(fontsizes), loc='left')

fig, ax = plt.subplots(layout="constrained")
example_plot(ax)
plt.show()

570 官方示例文档:examples-using-matplotlib-axes-axes-set-title

2.5.2 ax.legend图例设置

官网文档链接:matplotlib.axes.Axes.legend.html。 每个坐标系的图例默认会有一个配置,但也可以通过坐标系的legend方法来设置其配置信息如位置。matplotlib中关于此方法的声明如下:

1
Axes.legend(*args, **kwargs)

常用调用签名如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 方式1. 自动检测图例中需要显示的元素
"""
此方式下不传递任何参数,添加到图例中的元素是自动被决定的,系统自动获取当前坐标系中所有图元信息。
此时图例中的显示的名称来自绘制图元时设置的label参数值,或者图元.set_label("label名称") 设置的参数值。如下示例1
"""
legend()

# 方式2. 明确列出被说明的plot及对应的图例名称
"""
为了更加明确地控制哪个图元使用什么图例名称,可以将图元与图例分别用可迭代容器(如list)封装起来,调用legend方法时一对对解包出来使用。如下示例2
"""
legend(handles, labels)

# 方式3. 明确列出被说明的线条(跟 方式2 类似)
"""
与方式3类似,只是这种情况已经将图元的名字在绘制图元时通过label属性确定了,所以可以不再重复传递而是直接从图元中获取。最后只需要给legend方法传递一批用可迭代容器封装起来的图元即可。如下示例3
"""
legend(handles=handles)

# 方式4. 为已存在的plot实例指定标签图例名称(不推荐)
"""
如果将多个字符串用可迭代容器封装过起来传递给legend方法,就可以为坐标系已有的所有图元设置图例名称。
这种方式不被官方推荐,因为此时图元与图元名称之间的对应关系是通过图元被创建出来的顺序来隐匿表达,而这种顺序在复杂情况会产生混乱。
如示例4
"""
legend(labels)

这4种具体的方式是原方法声明的4种应用实例。它们都有如下常用参数:

  • loc:图例在坐标系中的位置,类型可以字符串/整型数字/二元组坐标。字符串形式的参数可以是以下值,字符串及对应的位置如图(再加上一个'best',此参数在图元比较多、数据复杂时需计算开销较大,那时不建议使用):
    • 如果传递整数,matplotlib为上述10个位置再加上'right'(向后兼容,实际与'center right'同义),各分配了一个对应的整数,范围是[0,10]。详情看官网文档。
    • 如果传递是坐标,坐标的单位默认是 “轴域比例坐标”,就是说(0, 0)代表坐标轴大框架的左下角、(1, 1)代表坐标轴大框架的右上角、(0.5, 0.5)代表整个坐标轴框架的正中心。其他类推。
  • labelcolor:图例中文字的颜色。
  • fontsize:整个图例的大小。
  • facecolor:图例背景颜色。

以下示例1、2、3、4分别对应上述方式1、2、3、4。 示例1(重点看“STEP 3: 绘制核心图表 (Plotting)”):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np

# ==============================================================================
# STEP 0: 修改全局设置 (rcParams Settings)
# ==============================================================================
# 设置具体字体为黑体
mpl.rcParams['font.sans-serif'] = ['SimHei']
# 解决负号 '-' 显示为方块的问题
mpl.rcParams['axes.unicode_minus'] = False

# ==============================================================================
# STEP 1: 准备数据 (Data Preparation)
t = np.linspace(0, 2 * np.pi, 200)
y_sin = np.sin(t)
y_cos = np.cos(t)
# ==============================================================================

# ==============================================================================
# STEP 2: 创建画布与坐标系/子图 (Figure & Axes)
# ==============================================================================
fig, ax1 = plt.subplots(layout="constrained")
# ==============================================================================

# STEP 3: 绘制核心图表 (Plotting)
# ==============================================================================
ax1.plot(t, y_sin, label='Inline Label:Sin Function', color="tab:red", linewidth=2)
ax1.plot([1, 2, 3, 4, 5, 6], label='Inline Label:Straight Line')
"""
上述一行代码等价于如下两行代码:
line = ax1.plot([1, 2, 3, 4, 5, 6])
line[0].set_label('Inline Label:Straight Line')
"""
ax1.legend() # 没有这个语句的话,默认不会显示图例

# ==============================================================================
# STEP 4: 装饰与标注 (Customizing Labels, Title & Legend)
# ==============================================================================
ax1.set_title("子图 1")

# ==============================================================================
# STEP 5: 输出与渲染 (Display & Save)
# ==============================================================================
plt.show();

示例2:

1
2
3
4
5
# STEP 3: 绘制核心图表 (Plotting)
# ==============================================================================
line1, = ax1.plot(t, y_sin, label='Inline Label:Sin Function', color="tab:red", linewidth=2)
line2, = ax1.plot([1, 2, 3, 4, 5, 6], label='Inline Label:Straight Line')
ax1.legend([line1, line2], ['line1 label', 'line2 label']) # 没有这个语句的话,默认不会显示图例

示例3:

1
2
3
4
5
6
# STEP 3: 绘制核心图表 (Plotting)
# ==============================================================================
line1, = ax1.plot(t, y_sin, label='Inline Label:Sin Function', color="tab:red", linewidth=2)
line2, = ax1.plot([1, 2, 3, 4, 5, 6], label='Inline Label:Straight Line')
# 一定要使用关键字参数handles,因为如果只传递一个列表时只会被当作图元的label,解析时会出错
ax1.legend(handles = [line1, line2]) # 没有这个语句的话,默认不会显示图例

示例4:

1
2
3
4
5
6
7
8
# STEP 3: 绘制核心图表 (Plotting)
# ==============================================================================
line1, = ax1.plot(t, y_sin, label='Inline Label:Sin Function', color="tab:red", linewidth=2)
line2, = ax1.plot([1, 2, 3, 4, 5, 6], label='Inline Label:Straight Line')
# 如果列表中元素个数比图元个数少,那么后续的图元就会没有图例
ax1.legend(['line1 label', 'line2 label'], loc='upper left', fontsize=10, labelcolor='green', facecolor='gray')
# 没有这个语句的话,默认不会显示图例
# 示例4 绘制效果如下图

2.5.3 ax.grid网格设置

官网文档链接:matplotlib-axes-axes-grid。 每个坐标系的图例默认会有一个配置,但也可以通过坐标系的legend方法来设置其配置信息如位置。matplotlib中关于此方法的声明如下:

1
Axes.grid(visible=None, which='major', axis='both', **kwargs)

此方法常用参数如下:

  • visible:是否显示网格,可以是布尔值或None,可选,默认是None。具体解释如下:

    • 如果使用布尔值True或False,分别是强制把网格显示出来或关闭掉,不管子图中之前是否已经显示了网格。

    • 如果使用None,就像电器最基础的功能只有开关两种选项,这个参数就是这在开关之间切换。就是说如果当前子图没有显示网格,使用此参数调用grid方法网格就会显示出来;反过来,如果当前子图已经显示网格了,使用此参数调用grid方法网格就会关闭掉或说隐藏起来。不推荐:可读性不高、容易产生歧义

    • 还有一个特别的点需要注意,当传递了kwargs关键字参数后,无论visible参数传递了True或False还是None,此时认为用户的真实意图肯定是“想看它”,那么总是显示网格。

  • which:在坐标轴哪个刻度级别显示网格,类型是字符串,可用的值有:'major'/'minor'/'both',它们是坐标轴刻度系统(Ticks)的核心概念。可选,默认值是'major'。此处这3个参数值分别代表在主刻度、次刻度、主次刻度 上显示网格。

  • axis:控制哪个“方向”的网格,可使用的值有'x/y/both',默认是'both'。这个参数决定了网格线到底是要横着画、竖着画、还是铺满。'x'表示只控制垂直于 X 轴的网格线(也就是一条条竖线),'y'表示只控制垂直于 Y 轴的网格线(也就是一条条横线),'both'则表示同时控制所有方向的网格线。

  • kwargs:关键字参数,它是一个大杂烩。在前面参数里没没定义或使用的控制参数,就通过 kwargs 来控制。比如color、linestyle等,具体见官网文档。


示例1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import matplotlib.pyplot as plt
import numpy as np

# ==============================================================================
# STEP 0: 修改全局设置 (rcParams Settings)
# ==============================================================================
# 设置具体字体为黑体
plt.rcParams['font.sans-serif'] = ['SimHei']
# 解决负号 '-' 显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False

# 创建画布与坐标系/子图
fig, ax = plt.subplots(figsize=(7, 4))

# 绘制核心图表
ax.plot(np.linspace(0, 10, 100), np.exp(np.linspace(0, 2.3, 100)), color='tab:blue')

# ==============================================================================
# 手动启用次刻度
# ==============================================================================
ax.minorticks_on()

# ==============================================================================
# 精细化设置网格
# ==============================================================================
# 1. 开启:主刻度的 Y 轴网格(水平横线),颜色为#7f8c8d
ax.grid(visible=True, which='major', axis='y', color='#7f8c8d', linestyle='-', linewidth=1)

# 2. 辅助:次刻度的 X 轴网格(垂直竖线),颜色为#bdc3c7(极淡的虚线)且宽度也比y轴的网格线的小
ax.grid(visible=True, which='both', axis='x', color='#bdc3c7', linestyle=':', linewidth=0.6)

plt.show()

官网示例参见:examples-using-matplotlib-axes-axes-grid

2.5.4 xlabel/ylabel坐标轴名称标签

官网文档链接:matplotlib-axes-axes-set-xlabelmatplotlib-axes-axes-set-ylabel

xlabel/ylabel坐标轴名称标签,就是x/y坐标轴的名称。最直白的参考就是初中时学习直角坐标系时,在x/y坐标轴旁边分别标记的 x与y 字样,这就是为x/y坐标轴分别取的名称。

matplotlib中为直角坐标系中的x/y轴设置名字及相关属性的方法与用法是非常相似地,此处以x坐标轴的相关方法来进行说明。matplotlib中关于此方法的声明如下:

1
Axes.set_xlabel(xlabel, fontdict=None, labelpad=None, *, loc=None, **kwargs)

其中常用参数如下:

  • xlabel:x坐标轴的名称,类型是字符串。就是上面所说的 x 字样。
  • fontdict:全称是 Font Dictionary(字体字典)。它是一个 Python 字典(dict),专门用来打包控制文本外观的各种属性(如字体名称、大小、颜色、粗细、倾斜度等)。目的是在编写规范的、可重复使用的绘图脚本以绘制 AI 实验中的各种训练曲线图时,可以利用它们,让标题、X轴标签、Y轴标签的字体风格保持高度统一。任何由 Matplotlib 官方 matplotlib.text.Text 类所支持的属性(官网链接:matplotlib.text.Text)都可以在此字典中使用。
  • labelpad:轴标签(Label)与坐标轴大框架(包括刻度线和刻度数字)之间的距离,单位是磅,可以是浮点数或None,当为默认的None时取全局参数值4.0磅。根据官网文档,其计算公式可以为:最终标签位置 = 坐标轴外边缘(Axes Bounding Box) + 刻度线长度}+ 刻度数字所占宽度 + labelpad。当觉得轴标签(即轴名称)离坐标轴、刻度、刻度标签太近了,那就调大;太远了则调小到0,甚至可以是负数(负数情况,通常在隐藏了刻度线后,用来做极简排版)。
  • loc:x坐标轴名称标签在x坐标轴那条线段的位置,可用值有:'left'/'center'/'right',分别表示在xx坐标轴那条线段的左边/中间/右边位置。对于y坐标轴,可用值就变成了:'bottom'/'center'/'top'。
  • kwargs:关键字参数,它是一个大杂烩。在前面参数里没没定义或使用的控制参数甚至声明了的参数,可以通过 kwargs 来控制(如果kwargs中出现了与fontdict中相同的参数,kwargs的优先级更高)。比如color、alpha等,任何由 Matplotlib 官方 matplotlib.text.Text 类所支持的属性(官网链接:matplotlib.text.Text)都可以在此处使用。

示例1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
### 轴标签
import matplotlib.pyplot as plt

# 修改全局设置以支持中文
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

fig, ax = plt.subplots(figsize=(6, 4), layout="constrained")
ax.plot([0, 1], [10000, 50000]) # Y轴是很大的五位数,就是说宽度可能比较大

# 统一定义一个样式模板字典
my_label_font = {
'fontsize': 14,
'color': 'darkblue',
'weight': 'bold',
'family': 'sans-serif'
}

# X轴标签:向外推开 25 磅
ax.set_xlabel('横轴 (Epochs)', labelpad=25, color='red', alpha=0.5, loc='center')

# Y轴标签:设置为 0 磅,它会紧紧贴着那排y轴的刻度文字标签
ax.set_ylabel('纵轴 (Loss)', labelpad=0)

ax.set_title("labelpad 间距修改演示")
plt.show()

官网示例参见:examples-using-matplotlib-axes-axes-set-xlabel

2.5.5 主刻度与次刻度及刻度标签

2.5.5.1 Axes.set_xticks方法

官网文档链接:matplotlib-axes-axes-set-xticks

xlabel/ylabel坐标轴名称标签,就是x/y坐标轴的名称。最直白的参考就是初中时学习直角坐标系时,在x/y坐标轴旁边分别标记的 x与y 字样,这就是为x/y坐标轴分别取的名称。

matplotlib中为直角坐标系中的x/y轴设置名字及相关属性的方法与用法是非常相似地,此处以x坐标轴的相关方法来进行说明。matplotlib中关于此方法的声明如下:

1
Axes.set_xticks(ticks, labels=None, *, minor=False, **kwargs)

其中常用参数如下:

  • ticks:代表x轴上要显式刻画出来的刻度位置,由一维数组类数据(比如列表 listnp.arange 生成的数组等)表示,就是说它决定了 X 轴上哪些数字的地方将伸出那些刻度“小短线”。有两种注意事项:
    • 如果传入一个空列表 ax.set_xticks([]),其效果是把 X 轴的所有刻度线和刻度标签删除,让 X 轴变成一条光秃秃的“光杆”。
    • 调用这个方法后,Matplotlib 内部的轴定位器会被替换为 FixedLocator(固定定位器),那么后面 Matplotlib 内部因为不再维护一个默认的轴定位器,那么它将不会再帮忙自动缩放或重新调整刻度密度了,而只依赖 FixedLocator的显式声明与操作。
  • labels:前面ticks参数显式声明的每个刻度对应的刻度标签,可选。如果未传递参数,那么Matplotlib将把ticks参数的各个刻度值对应的字符串作为刻度对应的标签;如果传递了此参数,类型是一个字符串列表,且长度与ticks参数的长度必须一样。
    • 与ticks类似地,如果传递了参数,Matplotlib 内部的轴刻度标签格式化器会被替换为 FixedFormatter(固定轴刻度标签格式化器),那么后面 Matplotlib 内部因为不再维护一个默认的轴刻度标签格式化器,那么它将不会再帮忙自动设置刻度对应的刻度标签了,而只依赖 FixedFormatter的显式声明与操作。
  • minor:是否只设置主刻度及其刻度标签,类型是布尔型,默认是False。当为False时,设置的 tickslabels 只对主刻度生效;当为True时,设置的 tickslabels 只对次刻度生效;当为
  • kwargs:关键字参数,它是一个大杂烩。在前面参数里没没定义或使用的控制参数甚至声明了的参数,可以通过 kwargs 来控制(如果kwargs中出现了与fontdict中相同的参数,kwargs的优先级更高)。比如color、alpha等,任何由 Matplotlib 官方 matplotlib.text.Text 类所支持的属性(官网链接:matplotlib.text.Text)都可以在此处使用。

示例1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
### 刻度
import matplotlib.pyplot as plt
import numpy as np

# ==============================================================================
# STEP 0: 修改全局设置 (rcParams Settings)
# ==============================================================================
# 设置具体字体为黑体
plt.rcParams['font.sans-serif'] = ['SimHei']
# 解决负号 '-' 显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False

fig, ax = plt.subplots()

ax.plot([0, 50, 100], [1, 2, 3], label='阶段 A')
ax.plot([0, 50, 100, 500], [10, 50, 1000, 5000], label='爆炸的阶段 B')

# 动态获取当前的 X 轴真实物理下限、上限
x_min, x_max = ax.get_xlim() # 此时系统会自动检查到:x_max 现在是 500 多(包含了一点自动留白)

# 利用拿到的真实最大值,动态生成均匀分布的刻度
# 比如在 0 到 真实的 max 之间均匀刻3个刻度线
dynamic_ticks = np.linspace(0, x_max, 3)

# 给动态算好的坐标指定特定的刻度标签,并设置颜色与字体大小
ax.set_xticks(dynamic_ticks, labels=['起点/0', f"半程/{x_max/2}", f"最新终点/{x_max}"], color='red', size=20)
ax.set_title('x轴刻度定制、y轴刻度自动生成')
ax.legend()

plt.show()

2.5.5.2 Axes.set_xlim方法

官网文档链接:matplotlib-axes-axes-set-xlim

设置x轴的显示范围。xlim/ylim分别代表x轴显示范围/y轴显示范围,它们只代表x/y坐标轴上画面可见的“物理几何范围”,本身不包含任何刻度的样式、颜色、字体等“相关属性”。

matplotlib中为直角坐标系中的x/y轴设置显示范围的方法与用法是非常相似地,此处以x坐标轴的相关方法来进行说明。matplotlib中关于此方法的声明如下:

1
Axes.set_xlim(left=None, right=None, *, emit=True, auto=False, xmin=None, xmax=None)

其中常用参数如下:

  • left:x坐标轴上最小刻度即左边界,可选,如果传入了只接受数字。默认值是None,表示保持现有左边界不动,让系统自动计算与适应。

    • 如果传入了一个类型为数字的二元元组,则matplotlib会自动将第一个元素解析为left,第二个元素解析为right。
  • right:x坐标轴上最大刻度即右边界,可选,如果传入了只接受数字,默认值是None,表示保持现有右边界不动,让系统自动计算与适应。

  • emit:是否通知观察者(Observers)视窗范围改变了。可用值是布尔值类型。默认值为True。在多个子图需要“双轴联动滚动”(比如在看 A 图的第 10~50 帧,B 图也必须同步切换到 10~50 帧)时,emit=True 会向系统发送一个信号。如果设置为 False,系统只会悄悄把当前子图的视窗改了,但不声张从而切断与其他联动子图的同步通知。

  • auto:是否把 X 轴的自动缩放(Autoscaling)保持开启。可用值是布尔值类型或None,默认值是False。

    • 使用None时,表示保持当前的自动缩放状态不改变。
    • True值时,表示保留系统在此轴上刻度表示范围的自动缩放权力。如果后面追加了更大的新数据,依然允许系统自动把轴刻度范围自动适应拉宽。
    • False值(默认值)时,表示取消系统在此轴上刻度表示范围的自动缩放权力。
  • xmin:等价于 left参数。但不能与 left参数同时使用,否则报错。

  • xmax:等价于 right参数。但不能与 right参数同时使用,否则报错。


示例1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
### xlim与ylim
import matplotlib.pyplot as plt
import numpy as np

# STEP 1: 修改全局设置以支持中文
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# 创建两个上下排列的子图,模拟需要双轴联动的监控面板
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(7, 5), layout="constrained")

# 数据准备
x_data = np.linspace(0, 500, 100)
y_data = np.sin(x_data / 20)

# 绘制核心图表
ax1.plot(x_data, y_data, color='blue', label="图 1 (普通裁剪)")
ax2.plot(x_data, y_data, color='orange', label="图 2 (静默裁剪 + 保留自动缩放)")

# 1. 正常裁剪图 1 的视窗
ax1.set_xlim(left=0, right=100)
# 此时:图 1 范围死死锁在 [0, 100],且默认 auto=False,以后再加新数据画面也不会自动撑大。

# 2. 多参数高级定制图 2 的视窗
# [left, right] 规定了当前视野范围
# [emit=False] 让图 2 的范围改变变成“一件悄悄发生的事”,不向系统发送重绘和通知信号(阻止逆向触发某些联动回调)
# [auto=True] 告知matplotlib系统:虽然现在指定了x轴刻度范围 0-100,但如果后续如果追加新数据,依然允许系统自动把刻度范围拉宽
ax2.set_xlim(left=0, right=100, emit=False, auto=True)

# ==============================================================================
# 验证 auto=True 的效果:往两个子图同时追加一个超出当前刻度范围的数据点 (X = 600)
# ==============================================================================
ax1.plot([600], [0], 'ro') # 图 1 增加红点
ax2.plot([600], [0], 'ro') # 图 2 增加红点,结果是图2中x轴刻度范围自动适应拉宽,延伸到了x=600左右

ax1.set_title("图 1:auto=False(默认),红点在视野外看不见")
ax2.set_title("图 2:auto=True,系统发现新红点后,自动把x轴刻度范围自动适应拉宽到600左右")

# 显示网格
ax1.grid()
ax2.grid()

# 显示图例
ax1.legend()
ax2.legend(loc='upper right', fontsize=7)

plt.show()

官网示例参见:examples-using-matplotlib-axes-axes-set-xticks

2.6 其他常用操作或注意事项

2.6.1 支持中文

Matplotlib 任何文本的最终渲染字体,是由plt.rcParams['font.family'](字体大类/族)和对应的备选列表(如 plt.rcParams['font.sans-serif'])共同决定的。

Matplotlib 默认的字体大类保存在plt.rcParams['font.family']中,它的默认值是:['sans-serif'],表示所有图表文本都使用 无衬线字体(sans-serif) 大类。

在字体大类下面还有具体的字体,这些才是决定图表中各个文字所使用的字体。这个配置保存在plt.rcParams['font.sans-serif'],它是一个列表,表示具体的字体备选推荐列表。它的默认值跟字体大类有关,字体大类是默认值['sans-serif']时,具体的字体备选推荐列表的值是:['DejaVu Sans', 'Bitstream Vera Sans', 'Computer Modern Sans Serif', 'Lucida Grande', 'Verdana', 'Geneva', 'Lucid', 'Arial', 'Helvetica', 'Avant Garde', 'sans-serif']。

具体的字体备选推荐列表的值,其实代表一份从左到右的“待选名单”。当要在图表里写字时,matplotlib会先在操作系统里找 DejaVu Sans,如果找到了就用它画;如果系统里没有(比如 Windows 默认通常没有),它就会被pass,接着找第二个 Bitstream Vera Sans……以此类推,直到找到电脑里真正安装了的字体为止。 但是这份默认的“待选名单”中全部是西文字体,不支持中文。要支持中文就要使用:['SimHei'],字体大类仍使用默认的 ['sans-serif'] 即可。

具体的设置代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
### 方式1(推荐)
import matplotlib.pyplot as plt
import numpy as np

# 设置具体字体为黑体
plt.rcParams['font.sans-serif'] = ['SimHei']
# 解决负号 '-' 显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False



### 方式2
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np

# 设置具体字体为黑体
mpl.rcParams['font.sans-serif'] = ['SimHei']
# 解决负号 '-' 显示为方块的问题
mpl.rcParams['axes.unicode_minus'] = False

这两种写法在实际运行效果上是完全等价的,都能完美解决中文乱码和负号变成方块的问题。 它们唯一的区别在于“调用的入口路径(API 层级)”不同。用一句话总结就是:plt.rcParams 只是 mpl.rcParams 的一个快捷方式(指针)。

推荐使用方式1中的用法,理由如下:

  • 保持代码简洁,避免冗余导入:
    • 日常作图时,绝大多数操作都是通过 import matplotlib.pyplot as plt 来完成,极少直接用到顶层的 matplotlib 核心库。
    • 用第二种写法的话,用户必须被迫多写一行 import matplotlib as mpl。这在代码上属于引入了不必要的模块。
  • 符合业界主流审美:
    • 翻阅 GitHub 上的主流开源 AI 项目、Kaggle 顶尖 Notebook 或者是 Datawhale 等开源社区的教程脚本,大家几乎约定俗成地在使用 plt.rcParams。遵循这个习惯能让自己编写脚本在团队协作或沟通时读起来步伐更协调、更具“极客范”。

2.6.2 plt.show()注意事项

2.6.2.1 是带有“释放清理”性质的终结函数

plt.show() 并不是单纯的“把图画出来展示给用户”,它在将图形渲染到屏幕后,会在内存中把当前整个画布(Figure)对象彻底销毁并清空。

比如用户在执行了 plt.show() 之后,还想继续调用 plt.savefig('my_plot.png') 来保存图片,将会得到一张完全空白的图片。又比如用户在执行了 plt.show() 之后,还想修改其中子图中的相关内容比如修改子图标题,此时再去调用 ax.grid()plt.show();,并不会看到期望的效果,如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import matplotlib.pyplot as plt

# 设置具体字体为黑体
plt.rcParams['font.sans-serif'] = ['SimHei']

# 解决负号 '-' 显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False

fig, ax = plt.subplots()

# 1. 此时默认是没有网格的
ax.grid()# 触发“切换”:从无到有,网格显示了!
ax.set_title("交互模式:网格已自动开启!")

plt.show();

# 2. 紧接着再调用一次,没有传任何其他参数
ax.grid()# 再次触发“切换”:从有到无,网格又消失了!
ax.set_title("交互模式:网格已自动关闭!")

plt.show();

这段代码虽然不会报错,但其并不会展示用户期望的效果。用户并不能看到弹出窗口中子图网格消失、标题变化,相反从第2个ax.grid()语句之后的代码不能展示其期望的效果。

真正的流程大致如下:

  • 执行了第一个plt.show()语句前,matplotlib系统一直在操作内存中的相关python对象,一直在积蓄实力。

  • 执行了第一个plt.show()语句后,程序会弹出一个窗口显示画布上内容。

  • 在用户关闭窗口的那一瞬间,matplotlib系统会认为“这张图的使命已经完成”。为了释放内存,它会在后台把这张画布等内容从当前的活跃管理队列中彻底注销并清空。虽然 figax 这两个变量在 Python 内存中依然保留着残余的地址(因此后续代码不会触发 NameErrorAttributeError),但它们已经变成了与物理渲染引擎彻底断开连接的“僵尸对象”。

  • 后续用户尝试继续对画布及其中内容进行操作此时,都只是在对一个个单独存在、未与matplotlib后端引擎建立联系的python对象进行操作,matplotlib后端引擎不会收到这些新指令,后续再怎么调用 plt.show() 也绝对无法弹窗或刷新任何内容。

2.6.2.2 是阻塞式(Blocking)函数

在标准的 Python 脚本(如 .py 文件)中运行到 plt.show() 时,程序会立刻卡住(阻塞)。只有当用户用鼠标手动关闭弹出的那个 GUI 绘图视窗后,后续的代码才会继续向下执行。

如果在跑一个需要连续训练并画图的自动化循环(比如每 10 个 Epoch 画一次 Loss),直接写 plt.show() 会让训练在第一个 10 轮就死死卡住,直到手动去点那个关闭按钮。

在需要非阻塞的自动化脚本中,通常会使用 plt.ion()(Interaction On:开启交互模式)配合 plt.pause(1),让它显示当前的绘图效果后继续往后执行代码与刷新画布。。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import matplotlib.pyplot as plt

# ==============================================================================
# STEP 0: 修改全局设置 (rcParams Settings)
# ==============================================================================
# 设置具体字体为黑体
plt.rcParams['font.sans-serif'] = ['SimHei']
# 解决负号 '-' 显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False

plt.ion() # Interaction On。开启交互模式,图表会实时更新,不需要阻塞等待
fig, ax = plt.subplots(figsize=(6, 4))
ax.plot([1, 2, 3], [4, 5, 6], color="tab:blue", linewidth=2)

ax.grid(True) # 开启网格线
# 关键函数,暂停 3 秒并强制刷新图表窗口。在交互模式下,只有调用 pause/show 才会真正渲染画面
ax.set_title("网格开启中 (3秒)...")
plt.pause(3) #

ax.grid(False) # 关闭网格线
ax.set_title("网格已关闭!")

plt.ioff() # Interaction Off。关闭交互模式
plt.show() # 显示最新图表

此代码可实现如下功能:在不关闭、不销毁绘图窗口的前提下,实现图表状态的动态闪变切换。 具体视觉流程如下:

  • 前3秒:窗口自动弹出,展示一条蓝色折线,此时网格开启,标题显示“网格开启中 (3秒)...”。

  • 第3秒时:窗口不关闭,画面在直接发生突变——网格消失,标题闪变成“网格已关闭!”。

  • 3秒之后:图表定格在“无网格”的最新状态并进入阻塞挂起,等待用户手动点击右上角关闭窗口。

2.6.2.3 在不同运行环境(IDE vs Jupyter)下的表现不同

  • 在标准 .py 脚本或终端里:
    • 必须显式调用:如果不写 plt.show(),程序运行完后桌面上什么都不会发生,画布在后台默默地被创建与销毁。
  • 在 Jupyter Notebook / JupyterLab 中(开启了 %matplotlib inline 后):
    • 自动拦截并提前渲染:这是一个非常特殊的“魔术机制”。一旦代码块(Cell)执行完毕,Jupyter 的后台钩子会自动抓取当前的 Figure 对象,就算没写 plt.show(),它也会强行在单元格下方把图显示出来。

    • 此时 plt.show() 的作用:在 Jupyter 里写它,更多地是告诉matplotlib后端引擎“代码要执行结束了、显示出最后的绘图效果并释放相关资源”,起到释放清理的作用。 Jupyter 在执行完 plt.show() 后就立即在此 cell 后面显示绘图效果,如果之后还有代码,还会继续执行,但因为 plt.show() 是带有“释放清理”性质的终结函数,所以后续的操作看不到效果。

2.6.2.4 尽量不要“多次调用”

在同一个 .py 脚本或 jupyter notebook 文件中,尽量只在代码的最末尾调用一次 plt.show()

五、matplotlib绘制常用图表

4.1 常用二维图表

4.2 常用三维图表


python绘图库-matplotlib
https://jiangsanyin.github.io/2026/07/12/python绘图库-matplotlib/
作者
sanyinjiang
发布于
2026年7月12日
许可协议