数值 Python: 绘图与可视化

Python 科学计算环境中的可视化环境充满活力且多样化,它为各种可视化需求提供了充足的选择。这里,我们将重点探讨使用 Matplotlib 库来探索可视化。

在 Python 的科学计算环境中,有许多高质量的可视化库。最流行的通用可视化库是 Matplotlib,其主要特点是生成静态高质量的二维和三维图像。许多库都专注于可视化领域。其中一些突出的有 BokehPlotly 它们都主要关注交互性和网络访问。Seaborn 是一个基于Matplotlib库的高级绘图库,用于统计数据分析。Mayavi 使用古老的 VTK 进行高质量三维可视化。Paraview 也是基于 VTK 的。在 3D 可视化领域,还有一些新玩家,如 VisPy,这是一个基于 OpenGL 的 2D 和 3D 可视化库,与基于浏览器的环境(IPython 等)有良好的交互性和连接性。

Python 科学计算环境中的可视化环境充满活力且多样化,它为各种可视化需求提供了充足的选择。这里,我们将重点探讨使用 Matplotlib 库来探索可视化。

导入 Matplotlib

与大多数 Python 库不同,Matplotlib 为库提供了多套 API 入口。具体来说,提供了有状态 API 和面向对象 API,这两种 API 都由模块 matplotlib.pyplot 提供。强烈建议只使用面向对象的方法,本文的其余部分将仅关注 Matplotlib 的这一部分。要使用面向对象 Matplotlib API,我们首先需要导入它的 Python 模块。接下来,我们将假设 Matplotlib 是使用以下约定导入的:

1
2
3
4
5
6
7
In [2]: %matplotlib qt5

In [3]: import matplotlib as mpl

In [4]: import matplotlib.pyplot as plt

In [5]: from mpl_toolkits.mplot3d.axes3d import Axes3D

第一行假设我们在 IPython 环境中工作,具体地说是在 IPython notebook 或 IPython QtConsole 中。IPython 魔法命令 %matplotlib inline 将 Matplotlib 配置为使用 inline 后端(qt、wx、gtk等),这将使图像直接显示在终端,而不是新的窗口。语句 import matplotlib as mpl 导入 Matplotlib 主模块,语句 import matplotlib.pyplot as plt 用于便捷地地访问子模块 matplotlib.pyplot,该模块提供用于创建新图形实例的函数。

入门

在深入探讨如何使用 Matplotlib 创建图形的细节之前,我们先从一个简单的例子开始。我们将介绍 Matplotlib 库的一些基本原理,以便了解如何使用库生成图形。

Matplotlib 中的图形是根据 Figure 实例和 figure 中的一个或多个 Axes 实例构建而成的。Figure 实例为绘图提供了一个画布区域,而 Axes 实例则提供了坐标系,用于分配给画布的特定区域。如图所示:

一个 Figure 可以包含多个 Axes 实例,Axes 实例可以手动分配到 figure 画布的任意区域。也可以使用 Matplotlib 提供的布局管理器将 Axes 实例自动添加到 figure 画布。Axes 实例提供了一个坐标系统,可用于绘制各种不同图形样式的数据,包括线图、散点图、柱状图以及许多其他样式。另外,Axes实例还用于确定如何显示坐标轴,如是否显示轴标签、刻度线和刻度线标签等。事实上,使用 Matplotlib 的面向对象 API 时,调整图形外观所需的大多数函数都是 Axes 类的方法。

下面展示了一个简单的 Matplotlib 入门示例。假设需要在 $x \in [-5, 2]$ 上绘制 $y(x) = x^3 + 5x^2 + 10$ 的一阶及二阶导,我们可以这样:

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
In [7]: x = np.linspace(-5, 2, 100)

In [8]: y1 = x**3 + 5*x**2 + 100

In [9]: y2 = 3*x**2 + 10*x

In [10]: y3 = 6*x + 10

In [18]: fig, ax = plt.subplots()

In [19]: ax.plot(x, y1, color="blue", label="$y(x)$")
Out[19]: [<matplotlib.lines.Line2D at 0x1e33da69cc0>]

In [20]: ax.plot(x, y2, color="red", label="$y^{'}(x)$")
Out[20]: [<matplotlib.lines.Line2D at 0x1e33da7ffd0>]

In [21]: ax.plot(x, y3, color="green", label="$y{''}(x)$")
Out[21]: [<matplotlib.lines.Line2D at 0x1e339d099e8>]

In [22]: ax.set_xlabel("x")
Out[22]: Text(0.5,14.625,'x')

In [23]: ax.set_ylabel("y")
Out[23]: Text(22,0.5,'y')

In [24]: ax.legend()
Out[24]: <matplotlib.legend.Legend at 0x1e339cc3fd0>

在这里,我们使用 plt.subplots 函数来生成 Figure 和 Axes 实例。该函数用于在新创建的 figure 实例中创建 Axes 实例网格,这里它仅仅用作在一次函数调用中同时创建 Figure 和 Axes 实例的便捷方法。一旦 Axes 实例可用,其余所有步骤都涉及调用此 Axes 实例的方法。要创建实际的图,需要使用 ax.plot,它将第一个和第二个参数(NumPy 数组和数值数据)作为图的 x 和 y 值,并绘制出一条连接这些数据点的直线。还使用可选的 colorlabel 关键字参数来指定每一行的颜色,并为图例(legend)中使用的每一行分配一个文本标签。这几行代码足以生成我们需要的图形,但最好还应该为 x 和 y 轴设置标签,并在适当的情况下为我们绘制的曲线添加图例。轴标签使用 ax.set_xlabelax.set_ylabel 方法进行设置,这些方法将带有相应标签的文本字符串作为参数;使用 ax.legend 方法添加图例,这里由于在绘制曲线时我们使用了 label 关键字参数,所以不需要任何参数。

这些是使用 Matplotlib 创建图表所需的典型步骤。虽然上图完整且功能完善,但其外观方面还有许多改进的空间。

交互与非交互模式

Matplotlib 库旨在与许多不同的环境和平台配合使用。因此,库不仅包含用于生成图形的例程,还包含对在不同图形环境中显示图形的支持。为此,Matplotlib 提供了用于生成不同格式图形(如 PNG、PDF、Postscript和SVG)的后端,为在不同平台的 GUI 上展示图形可以使用不同的部件工具箱(Qt、GTK、wxWidgets 以及用于 Mac OS X 的 Cocoa)。

可以在 Matplotlib 资源文件中选择使用哪种后端,也可以使用 mpl.use 函数指定,在导入 matplotlib 之后必须立即调用 mpl.use,然后再导入 matplotlib.pyplot 模块。例如,要选择 Qt4Agg 后端,我们可以:

1
2
3
import matplotlib as mpl
mpl.use('qt4agg')
import matplotlib.pyplot as plt

用于显示 Matplotlib 图形的 GUI 对于与 Python 脚本文件或 IPython 控制台交互非常有用,它允许交互式地(如:缩放和平移等)查看图形。当使用在 GUI 中显示图形的交互式后端时,需要调用函数 plt.show 以使窗口出现在屏幕上。默认情况下,plt.show 调用程序将挂起,直到窗口关闭。为了获得更多的互动,我们可以通过调用函数 plt.ion 来激活交互模式(interactive mode)。这指示 Matplotlib 接管 GUI 事件循环,在创建图形时立即显示在窗口,并将控制流返回给 Python 或 IPython 解释器。要使图形的变动生效,需要使用函数 plt.draw 发出重绘命令。使用函数 plt.ioff 来关闭交互模式,使用函数 mpl.is_interactive 来检查 Matplotlib 是处于交互模式还是非交互模式。

虽然交互式 GUI 具有其独特的优势,但在使用 IPython Notebook 或 Qtconsole 时,显示直接嵌入 notebook 中的 Matplotlib 生成图形通常更方便。此行为是使用 IPython 命令 %matplotlib inline 激活的,该命令激活为 IPython 提供支持的“内联(inline)后端”。这会将 Matplotlib 配置为使用非交互式后端生成图形,然后将其显示为 IPython Notebook 中的静态图像。IPython “内联后端” 也可以使用 IPython的 %config 命令进行微调。例如,我们可以使用 InlineBackend.figure_format 选项(对于 Mac OS X 用户 %config InlineBackend.figure_format='retina' 是另一个有用的选项,它能提升 Matplotlib 图形在 Retina 屏上的质量)生成图形的输出格式:

1
2
In [8]: %matplotlib inline
In [9]: %config InlineBackend.figure_format='svg'

当使用 IPython 内联后端时,不需要使用 plt.showplt.draw,因为 IPython 富显示系统负责触发渲染和图形显示。
在这里,将假定代码示例在 IPython notebook 中执行,因此对函数 plt.show 的调用不在代码示例中。使用交互式后端时,需要在每个示例的末尾添加此函数调用。

Figure

正如前一节介绍的那样,Matplotlib 中使用 Figure 对象来表示图形。除了提供一个可以放置 Axes 实例的画布外,Figure 对象还提供了对图形执行操作的方法,它有几个属性可用于配置图形的属性。

Figure 对象可以使用函数 plt.figure 来创建,可以使用几个可选的关键字参数来设置图形属性。特别地,它接受 figsize 关键字参数,该参数传递一个元组 (width, height) 用于指定图形画布的宽度和高度(以英尺为单位)。用于指定图形画布颜色的 facecolor 关键字参数也很有用。

一旦创建了 Figure,就可以使用 add_axes 方法创建一个新的 Axes 实例并将其分配给图形画布上的一个区域。add_axes 需要一个强制性参数,该参数是一个包含左下角坐标和图形画布坐标系中轴宽度和高度的列表,其格式为 (left, bottom, width, height)。Axes 对象的坐标以及宽度和高度用总画布宽度和高度的分数表示。例如,完全填充画布的 Axes 对象对应于 (0, 0, 1, 1),这不会为轴标签和刻度留出空间。更实用的是 (0.1, 0.1, 0.8, 0.8),这对应于占据画布宽度和高度的 80% 的居中 Axes 实例。add_axes 方法有大量的关键字参数来设置新 Axes 实例的属性。这里值得强调的一个关键字参数是 axisbg,我们可以使用其分配 Axes 对象的背景颜色。而与 plt.figurefacecolor 参数一起使用,使我们可以选择画布和 Axes 实例覆盖区域的颜色。

使用从 plt.figurefig.add_axes 中获得的 FigureAxes 对象,我们就可以准备开始使用 Axes 对象的方法绘制数据了。一旦创建了所需的图形,Figure 对象中就有更多的方法在图形创建工作流程中变得很重要。要设置整体图形标题,可以使用 suptitle (以字符串作为参数)。为了将图形保存到文件中,我们可以使用 savefig 方法。默认情况下,输出文件格式将根据 filename 参数的文件扩展名确定,但我们也可以使用 format 参数明确指定格式。可用的输出格式取决于使用哪个 Matplotlib 后端,通常有 PNG、PDF、EPS 和 SVG 格式。生成的图像的分辨率可以通过 dpi 参数设置。DPI 代表“每英寸点数(dots per inch)”,由于图形尺寸是使用 figsize 参数以英寸为单位指定的,因此将这些数字相乘得出输出图像的大小(以像素为单位)。savefig 方法也接受一些与 plt.figure 函数的参数类似的参数(如 facecolor)。需要注意的是,即使 facecolor 参数与 plt.figure 已经一起使用,在使用 savefig 时还需指定它,以应用于生成的图像文件。最后,在 savefig 中使用 transparent=True 参数可以使图形画布变得透明。

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
In [21]: fig = plt.figure(figsize=(8, 2.5), facecolor="#f1f1f1")

In [22]: ax = fig.add_axes((left, bottom, width, height), facecolor="#e1e1e1")

In [23]: x = np.linspace(-2, 2, 1000)

In [24]: y1 = np.cos(40 * x)

In [25]: y2 = np.exp(-x**2)

In [26]: ax.plot(x, y1*y2)
Out[26]: [<matplotlib.lines.Line2D at 0x193133be3c8>]

In [27]: ax.plot(x, y2, 'g')
Out[27]: [<matplotlib.lines.Line2D at 0x193133bea58>]

In [28]: ax.plot(x, -y2, 'g')
Out[28]: [<matplotlib.lines.Line2D at 0x193133bec50>]

In [29]: ax.set_xlabel('x')
Out[29]: Text(0.5,0,'x')

In [30]: ax.set_ylabel('y')
Out[30]: Text(0,0.5,'y')

In [31]: fig.savefig("graph.png", dpi=100, facecolor="#f1f1f1")

Axes

上一节介绍的 Figure 对象提供了 Matplotlib 图形的骨干,但所有有趣的内容都是在 Axes 实例内部或周围组织的。Axes 对象是 Matplotlib 库大多数绘图活动的核心。它提供了坐标系,它还包含用于确定轴标签位置的 axis 对象。绘制不同类型图形的函数也是 Axes 类的方法。

对于大多数常见的用例,在图形画布中明确指定 Axes 实例的坐标是很繁琐的。在图中使用多个 Axes 实例面板时尤其如此。 Matplotlib 提供了几个不同的 Axes 布局管理器,它们根据不同的策略创建和放置 Axes 实例。后面我们将详细介绍如何使用这种布局管理器。为了方便展示接下来的示例,我们在此简要介绍一下布局管理器——plt.subplots。之前,我们已经使用过这个函数。plt.subplots 函数能够通常指定其第一个和第二个参数来用网格填充图形,也可以使用 nrowsncols 参数填充图形,它会使用给定数量的行和列填充图形。

1
fig, axes = plt.subplots(nrows=3, ncols=2)

这里,函数 plt.subplots 返回一个元组 (fig, axes),其中 fig 是一个图,axes 是一个大小为 (ncols, nrows) NumPy 的数组。

plt.subplots 函数还包含两个特殊的关键字参数 fig_kwsubplot_kw,它们分别是在创建 Figure 和 Axes 实例时使用的带有关键字参数的字典。这允许我们用 plt.subplots 完全控制 Figure 和 Axes 对象的属性。

绘图类型

数据的有效可视化需要各种各样的图形技术。Matplotlib 将许多类型的绘图技术用作 Axes 对象的方法。常用的 2D 绘图函数如下图所示。

线条属性

最基本的绘图类型是简单的线图。它可以用来描述单变量函数的图形,或者将数据绘制为控制变量的函数。在线图中,我们经常需要配置图中线条的属性。如:线条宽度、线条颜色、线条样式(实线、虚线、点线等)。在 Matplotlib 中,我们将这些属性作为关键字参数传给绘图的方法(plot、step、bar 等)。 许多绘图方法都有自己的具体参数,但大多数绘图方法都共享基本属性,如颜色和线宽。下表总结了这些基本属性和相应的关键字参数。

参数 可选的值 描述
color 可以是颜色名称的字符串如 red、blue 等,或形如 #aabbcc 的 RGB 颜色代码 颜色
alpha 在 0.0(完全透明)到 1.0(完全不透明)之间的浮点数 透明度
linewidth, lw 浮点数 线宽
linestyle, ls ‘_’——实线
‘__’——虚线
‘:’——点线
‘:-‘——点划线
线条的风格
marker ‘+’,’o’, ‘*’——十字形,圆形,星形
‘s’——方形
‘.’——小点
‘1, 2, 3, 4, …’——不同角度的三角形符号
每个数据点,无论它是否与相邻数据点连接,都可以用此参数指定标记的符号
markersize 浮点数 标记的大小
markerfacecolor 颜色规范 填充标记的颜色
markeredgewidth 浮点数 标记边缘的线宽
markeredgecolor 颜色规范 标记边缘的颜色
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
In [27]: linewidths = [0.5, 1.0, 2.0, 4.0]
...: for n, linewidth in enumerate(linewidths):
...: axes[0].plot(x, y + n, color="blue", linewidth=linewidth)
...: axes_settings(fig, axes[0], "linewidth", len(linewidths))
...: # Line style
...: linestyles = ['-', '-.', ':']
...: for n, linestyle in enumerate(linestyles):
...: axes[1].plot(x, y + n, color="blue", lw=2, linestyle=linestyle)
...: # custom dash style
...: line, = axes[1].plot(x, y + 3, color="blue", lw=2)
...: length1, gap1, length2, gap2 = 10, 7, 20, 7
...: line.set_dashes([length1, gap1, length2, gap2])
...: axes_settings(fig, axes[1], "linetypes", len(linestyles) + 1)
...: # marker types
...: markers = ['+', 'o', '*', 's', '.', '1', '2', '3', '4']
...: for n, marker in enumerate(markers):
...: # lw = shorthand for linewidth, ls = shorthand for linestyle
...: axes[2].plot(x, y + n, color="blue", lw=2, ls='', marker=marker)
...: axes_settings(fig, axes[2], "markers", len(markers))
...: # marker size and color
...: markersizecolors = [(4, "white"), (8, "red"), (12, "yellow"), (16, "lightgreen")]
...: for n, (markersize, markerfacecolor) in enumerate(markersizecolors):
...: axes[3].plot(x, y + n, color="blue", lw=1, ls='-', marker='o',
...: markersize=markersize, markerfacecolor=markerfacecolor,
...: markeredgewidth=2)
...: axes_settings(fig, axes[3], "marker size/color", len(markersizecolors))
...:
...: 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
# a symbolic variable for x, and a numerical array with specific values of x
sym_x = sympy.Symbol("x")
x = np.linspace(-2 * np.pi, 2 * np.pi, 100)
def sin_expansion(x, n):
"""
Evaluate the nth order Talyor series expansion
of sin(x) for the numerical values in the array x.
"""
return sympy.lambdify(sym_x, sympy.sin(sym_x).series(n=n+1).removeO(),
'numpy')(x)

fig, ax = plt.subplots()

ax.plot(x, np.sin(x), linewidth=4, color="red", label='exact')
colors = ["blue", "black"]
linestyles = [':', '-.', '--']

for idx, n in enumerate(range(1, 12, 2)):
ax.plot(x, sin_expansion(x, n), color=colors[idx // 3],
linestyle=linestyles[idx % 3], linewidth=3,
label="order %d approx." % n)

ax.set_ylim(-1.1, 1.1)
ax.set_xlim(-1.5*np.pi, 1.5*np.pi)
# place a legend outsize of the Axes
ax.legend(bbox_to_anchor=(1.02, 1), loc=2, borderaxespad=0.0)
# make room for the legend to the right of the Axes
fig.subplots_adjust(right=.75)
plt.show()

图例

具有多行的图形通常可以从图例中受益,图例在图形中的某处显示标签。正如我们在前面的示例中看到的,图例可以使用 legend 方法添加到 Matplotlib 图形中的 Axes 实例。在上一节的示例中,我们使用了 loc 参数,它允许我们指定添加图例的位置: loc = 1 表示右上角,loc = 2 表示左上角,loc = 3 表示左下角,loc = 4 表示右下角,如下图所示。

在上一节的示例中,我们还使用了 bbox_to_anchor,它使图例能够放置在图形画布中的任意位置。bbox_to_anchor 参数形式为 (x, y),其中 x 和 y 是 Axes 对象中的画布坐标。即,点 (0, 0) 对应于左下角,点 (1, 1) 对应于右上角。请注意,在这种情况下,x 和 y 可以小于 0 大于 1,这表示图例可以放置在 Axes 区域之外。

默认情况下,图例中的所有线都以垂直排列方式显示。使用 ncols 参数,可以将图例标签拆分为多个列,如下图所示。

文本格式与注释

文本标签、标题和注释是大多数图形中的重要组成部分,对用于呈现这些文本的字体类型和字体大小具有完全的控制是生成高质量图形的基本要求。matplotlib 提供了几种配置字体属性的方法。默认值可在 Matplotlib 资源文件中设置,session范围的配置可在 mpl.rcparams 字典中设置。此字典是 Matplotlib 资源文件的缓存,在重新启动 Python 解释器并再次导入 Matplotlib 之前,对此字典中的参数所做的更改均有效。与文本显示方式相关的参数包括 font.familyfont.szie

提示:使用 print(mpl.rcparams) 可以获取可能的配置参数及其当前值的列表。更新参数就像为字典 mpl.rcparams 相应项分配新值一样简单(如 mpl.rcparams['savefig.dpi']=100)。参阅 mpl.rc 函数(可用于更新 mpl.rcparams 字典)和 mpl.rcdefaults (恢复为默认值)。

也可以通过将一组标准关键字参数传递给(在图形中创建文本标签的)函数,来逐个设置文本属性。大多数以某种方式处理文本标签的 Matplotlib 函数都接受下表中总结的关键字参数。

参数 描述
fontsize 字体大小(以磅为单位)
family 字体类型
backgroundcolor 文本标签背景的颜色
color 字体颜色
alpha 字体颜色的透明度
rotation 文本标签的旋转角度

在科学计算环境的可视化中,能够在文本标签中呈现数学符号和表达式是非常重要的。Matplotlib 通过文本标签中的 LaTeX 标记对此提供了极好的支持—— Matplotlib 中的任何文本标签都可以包含 LaTeX,方法是将其包含在 $ 符号中。默认情况下,Matplotlib 使用内部 LaTeX 渲染,它支持 LaTeX 语言的子集。但是,通过将配置参数 mpl.rcParams["text. usetex"]=True,还可以使用外部功能齐全的 LaTeX 引擎(如果你的系统上提供)。

在 Python 的字符串中嵌入 LaTeX 代码时,有一个常见的障碍: Python 使用 \ 作为转义字符,而在 LaTeX 中,它用于表示命令的开始。为了防止 Python 解释器转义包含 LaTeX 表达式的字符串,可以使用 r 前缀,如 r"$\int f(x) dx$"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fig, ax = plt.subplots(figsize=(12, 3))
ax.set_yticks([])
ax.set_xticks([])
ax.set_xlim(-0.5, 3.5)
ax.set_ylim(-0.05, 0.25)
ax.axhline(0)
# text label
ax.text(0, 0.1, "Text label", fontsize=14, family="serif")
# annotation
ax.plot(1, 0, "o")
ax.annotate("Annotation",
fontsize=14, family="serif",
xy=(1, 0), xycoords="data",
xytext=(+20, +50), textcoords="offset points",
arrowprops=dict(arrowstyle="->", connectionstyle="arc3, rad=.5"))
# equation
ax.text(2, 0.1, r"Equation: $i\hbar\partial_t \Psi = \hat{H}\Psi$",
fontsize=14, family="serif")
plt.show()

Axis 属性

现在,仍需配置和微调的图形的最后一个主要方面是 Axis 实例。二维图形有两个 Axis 对象:水平 x 轴和垂直 y 轴。每个轴可以相对于属性单独配置。在本节中,我们将详细介绍如何控制图形的这些方面。

轴标签与标题

axis 标签可以说是 axis 最重要的属性,几乎在所有情况下都需要设置。我们可以使用 set_xlabelset_ylabel 方法设置坐标轴标签。此外,可选的 labelpad 参数指定从轴到标签的间距,此填充有时是必要的,可以避免轴标签和轴刻度标签之间的重叠。set_xlabelset_ylabel 方法还采用其他参数来设置文本属性,如颜色(color)、字体大小(fontsize)和字体名称(fontname)等。

1
2
3
4
5
6
7
8
9
10
x = np.linspace(0, 50, 500)
y = np.sin(x) * np.exp(-x/10)
fig, ax = plt.subplots(figsize=(8, 2), subplot_kw={'facecolor': "#ebf5ff"})
ax.plot(x, y, lw=2)
ax.set_xlabel("x", labelpad=5, fontsize=18, fontname='serif', color="blue")
ax.set_ylabel("f(x)", labelpad=15, fontsize=18, fontname='serif', color="blue")
ax.set_title("axis labels and title example", fontsize=16,
fontname='serif', color="blue")
plt.show()

除了 x 轴和 y 轴上的标签之外,我们还可以使用 set_title 方法设置轴对象的标题。此方法的参数与 set_xlabelset_ylabel 的参数基本相同,但 loc 参数除外,loc 参数可指定为 ‘left’、’centered’、’right’,指示标题应左对齐、居中或右对齐。

轴范围

默认情况下,Matplotlib 的 x 轴和 y 轴的范围将根据 Axes 对象中绘制的数据自动调整。在许多情况下,这些默认范围已足够,但在某些情况下,可能需要显式设置轴范围。在这种情况下,我们可以使用 axis 对象的 set_xlimset_ylim 方法。这两种方法都有两个参数,分别指定下限和上限。set_xlimset_ylim 的另一种实现是 axis 方法,它接受字符串参数 ‘tight’ 和 ‘equal’。

也可以通过将 x 和/或 y 轴的 axis 参数设置为 ‘x’、’y’或 ‘both’,并使用 autoscale 方法,将 True 和 False 作为第一个参数传递,选择性地打开和关闭自动缩放。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
x = np.linspace(0, 30, 500)
y = np.sin(x) * np.exp(-x/10)
fig, axes = plt.subplots(1, 3, figsize=(9, 3), subplot_kw={'facecolor': "#ebf5ff"})
axes[0].plot(x, y, lw=2)
axes[0].set_xlim(-5, 35)
axes[0].set_ylim(-1, 1)
axes[0].set_title("set_xlim / set_y_lim")
axes[1].plot(x, y, lw=2)
axes[1].axis('tight')
axes[1].set_title("axis('tight')")
axes[2].plot(x, y, lw=2)
axes[2].axis('equal')
axes[2].set_title("axis('equal')")
plt.show()

轴刻度、刻度标签与网格

仍需配置的最后的 axis 基本属性是轴刻度的放置以及相应刻度标签的放置和格式。轴刻度是图形整体外观的重要组成部分,在准备出版和生产高质量图形时,经常需要对轴刻度进行详细控制。matplotlib 模块提供了一个通用的和可扩展的刻度管理系统,它提供了对刻度位置的完全控制。matplotlib 区分主要刻度和次要刻度。默认情况下,每个主刻度都有相应的标签,主刻度之间的距离可以进一步用没有标签的次刻度标记,必须显式打开此功能。

当进行刻度的配置时,最常见的设计目标是确定带有标签的主刻度放置的位置。mpl.ticker 模块为不同的放置策略提供了类。如,mpl.ticker.MaxNLocator 可用于设置刻度的最大值,mpl.ticker.MultipleLocator 可用于在给定基数的倍数处设置刻度,mpl.ticker.FixedLocator 可用于将刻度放置在指定的坐标处。要更改 ticker 策略,可以在 Axes.xaxisAxes.yaxis 中使用 set_major_locatorset_minor_locator方法。这些方法接受在 mpl.ticker 中定义的 ticker 类的实例,或者从这些类派生的自定义类。

显式指定刻度位置时,我们还可以使用 set_xtmicksset_yticks 方法,这两种方法接受放置主要刻度的坐标列表。在这种情况下,还可以使用 set_xticklabelsset_yticklabels 为每个刻度设置自定义标签,接受字符串列表用作相应刻度的标签。如果可能,最好使用通用刻度放置策略,例如 mpl.ticker.MaxNLocator,因为如果坐标范围发生变化,它们会动态调整,而使用 set_xtmicksset_yticks 的显式刻度放置则需要手动更改代码。然而,当必须控制刻度的精确位置时,set_xtmicksset_yticks 是方便的方法。

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
x = np.linspace(-2 * np.pi, 2 * np.pi, 500)
y = np.sin(x) * np.exp(-x**2/20)
fig, axes = plt.subplots(1, 4, figsize=(12, 3))
axes[0].plot(x, y, lw=2)
axes[0].set_title("default ticks")
axes[1].plot(x, y, lw=2)
axes[1].set_title("set_xticks")
axes[1].set_yticks([-1, 0, 1])
axes[1].set_xticks([-5, 0, 5])
axes[2].plot(x, y, lw=2)
axes[2].set_title("set_major_locator")
axes[2].xaxis.set_major_locator(mpl.ticker.MaxNLocator(4))
axes[2].yaxis.set_major_locator(mpl.ticker.FixedLocator([-1, 0, 1]))
axes[2].xaxis.set_minor_locator(mpl.ticker.MaxNLocator(8))
axes[2].yaxis.set_minor_locator(mpl.ticker.MaxNLocator(8))
axes[3].plot(x, y, lw=2)
axes[3].set_title("set_xticklabels")
axes[3].set_yticks([-1, 0, 1])
axes[3].set_xticks([-2 * np.pi, -np.pi, 0, np.pi, 2 * np.pi])
axes[3].set_xticklabels(['$-2\pi$', '$-\pi$', 0, r'$\pi$', r'$2\pi$'])
x_minor_ticker = mpl.ticker.FixedLocator([-3 * np.pi / 2, -np.pi/2, 0,
np.pi/2, 3 * np.pi/2])
axes[3].xaxis.set_minor_locator(x_minor_ticker)
axes[3].yaxis.set_minor_locator(mpl.ticker.MaxNLocator(4))
plt.show()

图形中经常使用的设计元素是网格线,它有助于从图形中直观地读取值。网格和网格线与轴刻度密切相关,因为它们以相同的坐标值绘制,因此本质上是跨越图形的刻度的延伸。在 Matplotlib 中,可以使用 axes 对象的 grid 方法打开轴网格。grid 方法采用可选的关键字参数,用于控制网格的外观。与 Matplotlib 中的许多绘图函数一样,网格方法接受参数 color、linestyle 和 linewidth 指定网格线的属性。此外,它还接受 which 和 axis,可以分别为其赋值 major、minor 和 both,以及 x、y 和 both,用于指示给定样式将应用于哪个轴上的刻度。如果网格线需要多种不同的样式,则可以使用 which 和 axis 对网格进行多个调用。

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
x = np.linspace(-2 * np.pi, 2 * np.pi, 500)
y = np.sin(x) * np.exp(-x**2/20)
fig, axes = plt.subplots(1, 3, figsize=(12, 4))
x_major_ticker = mpl.ticker.MultipleLocator(4)
x_minor_ticker = mpl.ticker.MultipleLocator(1)
y_major_ticker = mpl.ticker.MultipleLocator(0.5)
y_minor_ticker = mpl.ticker.MultipleLocator(0.25)

for ax in axes:
ax.plot(x, y, lw=2)
ax.xaxis.set_major_locator(x_major_ticker)
ax.yaxis.set_major_locator(y_major_ticker)
ax.xaxis.set_minor_locator(x_minor_ticker)
ax.yaxis.set_minor_locator(y_minor_ticker)

axes[0].set_title("default grid")
axes[0].grid()

axes[1].set_title("major/minor grid")
axes[1].grid(color="blue", which="both", linestyle=':', linewidth=0.5)

axes[2].set_title("individual x/y major/minor grid")
axes[2].grid(color="grey", which="major", axis='x', linestyle='-', linewidth=0.5)
axes[2].grid(color="grey", which="minor", axis='x', linestyle=':', linewidth=0.25)
axes[2].grid(color="grey", which="major", axis='y', linestyle='-', linewidth=0.5)
plt.show()

除了控制刻度放置之外,Matplotlib mpl.ticker 模块还提供了用于自定义刻度标签的类。如 mpl.ticker 模块的 ScalarFormatter 可用于设置与用科学符号显示刻度标签和显示大数值的轴标签相关的若干有用属性。如果使用 set_scientific 方法激活了科学记数法,则可以使用 set_powerlimits 控制方法使用科学记数法的阈值,在创建 scalmathtext=True 参数时,可以使用 useMathText=True 参数以使指数以数学样式显示,而不是使用代码样式指数(如,1e10)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fig, axes = plt.subplots(1, 2, figsize=(8, 3))

x = np.linspace(0, 1e5, 100)
y = x ** 2

axes[0].plot(x, y, 'b.')
axes[0].set_title("default labels", loc='right')

axes[1].plot(x, y, 'b')
axes[1].set_title("scientific notation labels", loc='right')

formatter = mpl.ticker.ScalarFormatter(useMathText=True)
formatter.set_scientific(True)
formatter.set_powerlimits((-1,1))
axes[1].xaxis.set_major_formatter(formatter)
axes[1].yaxis.set_major_formatter(formatter)
plt.show()

对数绘图

在跨越几个数量级的数据的可视化中,使用对数坐标系是有用的。在 Matplotlib 中,这种坐标系统中有几种绘图函数。例如:loglog、semilogx 和 semilogy,分别对 x 轴和 y 轴、仅对 x 轴和仅对 y 轴使用对数刻度。除对数轴刻度外,这些函数的行为类似于标准绘图方法。另一种方法是使用标准绘图方法,使用 set_xscale 和/或 set_yscale 方法(以 log 作为第一个参数)将坐标轴刻度分别配置为对数刻度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fig, axes = plt.subplots(1, 3, figsize=(12, 3))

x = np.linspace(0, 1e3, 100)
y1, y2 = x**3, x**4

axes[0].set_title('loglog')
axes[0].loglog(x, y1, 'b', x, y2, 'r')

axes[1].set_title('semilogy')
axes[1].semilogy(x, y1, 'b', x, y2, 'r')

axes[2].set_title('plot / set_xscale / set_yscale')
axes[2].plot(x, y1, 'b', x, y2, 'r')
axes[2].set_xscale('log')
axes[2].set_yscale('log')

plt.show()

双轴

Matplotlib 提供的一个有趣的轴技巧是双轴特性,它允许显示彼此重叠的两个独立轴。当在同一图形中以不同单位绘制两个不同的量时,这很有用。下图演示了此特性。在这里,我们使用了 twinx 方法(也有 twiny 方法)生成具有共享 x 轴和新的独立 y 轴的第二个 Axes 实例,该实例显示在图形的右侧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fig, ax1 = plt.subplots(figsize=(8, 4))

r = np.linspace(0, 5, 100)
a = 4 * np.pi * r ** 2 # area
v = (4 * np.pi / 3) * r ** 3 # volume

ax1.set_title("surface area and volume of a sphere", fontsize=16)
ax1.set_xlabel("radius [m]", fontsize=16)

ax1.plot(r, a, lw=2, color="blue")
ax1.set_ylabel(r"surface area ($m^2$)", fontsize=16, color="blue")
for label in ax1.get_yticklabels():
label.set_color("blue")

ax2 = ax1.twinx()
ax2.plot(r, v, lw=2, color="red")
ax2.set_ylabel(r"volume ($m^3$)", fontsize=16, color="red")
for label in ax2.get_yticklabels():
label.set_color("red")

plt.show()

Spine

在迄今为止生成的所有图形中,我们始终有一个包围轴区域的框。这确实是科学和技术图表的常见样式,但在某些情况下,例如,当表示示意图时,可能需要移动这些坐标线。在 Matplotlib 中,构成周围框的线称为 axis spines,可以使用 Axes.spines 更改它们的属性。例如,我们可能需要移除顶部和右侧的 spines,并移动 spines 与坐标系的原点重合。

Axes 对象的 spine 属性是一个字典,具有 right、left、top 和 bottom 键,可用于单独访问每个 spine。我们可以使用 set_color 方法将颜色设置为None,以指示不显示特定的 spine,在这种情况下,我们还需要使用 Axes.xaxisAxes.yaxisset_ticks_position 方法删除与该 spine 相关联的刻度。使用这些方法,我们可以将周围的框转换为 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
x = np.linspace(-10, 10, 500)
y = np.sin(x) / x

fig, ax = plt.subplots(figsize=(8, 4))

ax.plot(x, y, linewidth=2)

# remove top and right spines
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')

# remove top and right spine ticks
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')

# move bottom and left spine to x = 0 and y = 0
ax.spines['bottom'].set_position(('data', 0))
ax.spines['left'].set_position(('data', 0))

ax.set_xticks([-10, -5, 5, 10])
ax.set_yticks([0.5, 1])

# give each label a solid background of white, to not overlap with the plot line
for label in ax.get_xticklabels() + ax.get_yticklabels():
label.set_bbox({'facecolor': 'white', 'edgecolor': 'white'})

plt.show()

高级 Axes 布局

到目前为止,我们已经多次使用 plt.figureFigure.make_axesplt.subplots 来创建新的 Figure 和 Axes 实例,然后使用这些实例来生成图。在科技图表中,通常将多个图形打包在不同的面板中。在 Matplotlib 中,有自动创建 Axes 对象并使用各种不同的布局策略将其放置在图形画布上的函数。我们已经使用了 plt.subplots 函数,它能够为 Axes 生成对象的统一网格。在本节中,我们将探讨 plt.subplots 函数的其他功能,并介绍 subplot2gridGridSpec 布局管理器。

Inset

在深入了解如何使用更高级的 Axes 布局管理器之前,值得后退一步,考虑我们用来向图形画布添加 Axes 实例的第一种方法的重要用例: figure.add_axes 方法。这种方法非常适合于创建 inset,这是一个较小的图形,显示在另一个图形的区域内。Inset 经常用于在较大的图形中显示特别感兴趣的放大区域,或者用于显示一些次要的相关图形。

在 Matplotlib 中,我们可以在图形画布中的任意位置放置其他 Axes 对象,即使它们与现有 Axes 对象重叠。因此,要创建一个 inset,我们只需使用 Figure.make_axes 添加一个新的 Axes 对象, 并指定该 inset 放置的坐标即可。

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
fig = plt.figure(figsize=(8, 4))

def f(x):
return 1/(1 + x**2) + 0.1/(1 + ((3 - x)/0.1)**2)

def plot_and_format_axes(ax, x, f, fontsize):
ax.plot(x, f(x), linewidth=2)
ax.xaxis.set_major_locator(mpl.ticker.MaxNLocator(5))
ax.yaxis.set_major_locator(mpl.ticker.MaxNLocator(4))
ax.set_xlabel(r"$x$", fontsize=fontsize)
ax.set_ylabel(r"$f(x)$", fontsize=fontsize)

# main graph
ax = fig.add_axes([0.1, 0.15, 0.8, 0.8], facecolor="#f5f5f5")
x = np.linspace(-4, 14, 1000)
plot_and_format_axes(ax, x, f, 18)

# inset
x0, x1 = 2.5, 3.5
ax.axvline(x0, ymax=0.3, color="grey", linestyle=":")
ax.axvline(x1, ymax=0.3, color="grey", linestyle=":")

ax = fig.add_axes([0.5, 0.5, 0.38, 0.42], facecolor='none')
x = np.linspace(x0, x1, 1000)
plot_and_format_axes(ax, x, f, 14)

plt.show()

Subplot

我们已经广泛地使用了 plt.subplots。在绘制子图的网格时,通常是在子图之间共享 x 轴或 y 轴或全部。在这种情况下,使用 sharex 和 sharey 参数对 plt.subplots 非常有用,因为它可以防止在多个轴上重复使用相同的轴标签。

还值得注意的是,plt.subplots 返回的 Axes 实例的 Numpy 数组默认是“压缩”的:即从数组中删除长度为 1 的维度。如果所请求的列或行的数目都大于 1,则返回二维数组,但如果列或行的数目为 1(或两者都为 1),则返回一维数组。我们可以通过将参数 squeeze=False 传递到 plt.subplots 函数来关闭对 NumPy 数组维度的压缩。在这种情况下,fig, axes = plt.subplots(nrows, ncols) axes 变量总是二维数组。

最后一点可配置性是使用 plt.subplots_adjust 函数,该函数允许我们显式设置整个 Axes 网格的左、右、底和顶坐标,以及网格中 Axes 实例之间的宽度(wspace)和高度间隔(hspace)。

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
fig, axes = plt.subplots(2, 2, figsize=(6, 6), sharex=True, sharey=True,
squeeze=False)

x1 = np.random.randn(100)
x2 = np.random.randn(100)

axes[0, 0].set_title("Uncorrelated")
axes[0, 0].scatter(x1, x2)

axes[0, 1].set_title("Weakly positively correlated")
axes[0, 1].scatter(x1, x1 + x2)

axes[1, 0].set_title("Weakly negatively correlated")
axes[1, 0].scatter(x1, -x1 + x2)

axes[1, 1].set_title("Strongly correlated")
axes[1, 1].scatter(x1, x1 + 0.15 * x2)

axes[1, 1].set_xlabel("x")
axes[1, 0].set_xlabel("x")
axes[0, 0].set_ylabel("y")
axes[1, 0].set_ylabel("y")

plt.subplots_adjust(left=0.1, right=0.95, bottom=0.1, top=0.95, wspace=0.1,
hspace=0.2)

plt.show()

Subplot2grid

plt.subplot2grid函数是 plt.subplotsGridSpec 之间的过渡,它提供了比 plt.subplots 更灵活的 Axes 布局管理,同时比 GridSpec 更易用。特别地,plt.subplot2grid 能够创建跨越多个行和/或列的 Axes 实例的网格。plt.subplot2grid 有两个强制参数:第一个参数是轴网格的形状,形式为元组 (nrows, ncols),第二个参数是指定网格中的起始位置的元组 (row, col)。还有两个可选关键字参数 colspan 和 rowspan,可用于指示新 Axes 实例应跨越的行数和列数。请注意,对 plt.subplot2grid 函数的每次调用都将生成一个新的 Axes 实例。

GridSpec

这里介绍的最后一个网格布局管理器是 mpl.GridSpec 模块的 GridSpec。这是 Matplotlib 中最通用的网格布局管理器,尤其是,允许创建并非所有行和列都具有相等宽度和高度的网格,这在前面介绍的网格布局管理器中是不容易实现的。

GridSpec 对象仅用于指定网格布局,其本身不创建任何 Axes 对象。创建 GridSpec 类的新实例时,必须指定网格中的行数和列数。与其他网格布局管理器一样,我们也可以使用关键字参数 left、bottom、right和 top 来设置网格的位置,并且可以使用 wspace 和 hspace 来设置子块之间的宽度和高度间距。此外,GricSpec 允许使用 width_ratiosheight_ratios 参数指定列和行的相对宽度和高度。

创建 GridSpec 实例后,可以使用 figure.add_subplot方法创建 Axes
对象并将其放置在图形画布上。add_subplot 的参数需要传递 mpl.GridSpec .subplotSpec 实例,我们可以使用类数组索引从 GridSpec 对象生成该实例。

绘制色图

到目前为止,我们只考虑单变量函数的图形。二维 Axes 对象还可以使用所谓的色图(或热图)来可视化二元函数,其中 Axes 区域中的每个像素根据对应 z 的值来着色。Matplotlib 为这种类型的绘图提供了函数 pcolorimshow,而 contourcontourf 函数通过绘制等高线(而不是色图)以相同的格式绘制数据。

要使用 pcolor 生成色图,首先需要以适当的格式准备数据。虽然标准的二维图需要 x 和 y 值的一维坐标数组,但在本例中,我们需要使用二维坐标数组,可以使用 NumPy meshgrid 函数生成的二维坐标数组。为了绘制具有两个因变量的二元函数或数据,我们首先定义一维坐标数组 x 和 y,然后,将 x 和 y 数组传递到 np.meshgrid 函数,该函数能够生成所需的二维坐标数组 X 和 Y。

一旦准备好二维坐标数组和数据数组,通过将 X、Y 和 Z 数组作为前几个参数传递,就可以使用 pcolorcontourcontourf 来可视化它们。imshow 方法的工作原理类似,但是将数据数组 Z 作为参数,并且必须使用范围参数设置相关坐标范围,该范围参数格式为 [xmin, xmax, ymin, ymax] 列表。控制色图外观的其他重要关键字参数包括 vminvmaxnormcmapvminvmax 用于设置映射到色轴的值范围。这可以等效地通过设置 norm=mpl.colors.Normalize(vmin, vamx) 来实现。cmap 参数指定用于将数据值映射到颜色的颜色映射。此参数可以是预定义的颜色映射的名称字符串,也可以是 colormap 实例。Matplotlib 中的预定义的颜色映射在 mpl.cm 模块中。

完整的色图绘制所需的最后一步是 colorbar 元素,它为图形的查看者提供了读取不同颜色对应的数值的方法。在 Matplotlib 中,可以使用 plt.colorbar 函数将 colorbar 附加到已绘制的色图中。它将绘图句柄(handle)作为第一个参数,并有两个可选参数 ax 和 cax,这两个参数可用于控制 colorbar 在图形中的显示位置。如果给定了 ax,则 colorbar 所需空间将取自 Axes 对象;另一方面,如果给定了 cax,则 colorbar 将在该 Axes 对象上绘制。colorbar 实例 cb 有其自己的 axis 对象,可以在 cb.ax 对象上使用 axis 属性的标准方法,可以像 x、y 轴一样,使用如 set_labelset_ticksset_ticklabels方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
x = y = np.linspace(-10, 10, 150)
X, Y = np.meshgrid(x, y)
Z = np.cos(X) * np.cos(Y) * np.exp(-(X/5)**2-(Y/5)**2)
fig, ax = plt.subplots(figsize=(6, 5))
norm = mpl.colors.Normalize(-abs(Z).max(), abs(Z).max())
p = ax.pcolor(X, Y, Z, norm=norm, cmap=mpl.cm.bwr)
ax.axis('tight')
ax.set_xlabel(r"$x$", fontsize=18)
ax.set_ylabel(r"$y$", fontsize=18)
ax.xaxis.set_major_locator(mpl.ticker.MaxNLocator(4))
ax.yaxis.set_major_locator(mpl.ticker.MaxNLocator(4))
cb = fig.colorbar(p, ax=ax)
cb.set_label(r"$z$", fontsize=18)
cb.set_ticks([-1, -.5, 0, .5, 1])

plt.show()

绘制 3D 图形

上一节讨论的通过色图可视化两个因变量的数据。可视化同类型数据的另一种方式是使用 3D 图形,其中引入第三条轴 z 并且图形以透视的方式显示在屏幕上。在 Matplotlib 中,绘制 3D 图形需要使用不同的 axes 对象,即 mpl_toolkits.mplot3d模块中提供的 Axes3D 对象。我们可以使用 Axes3D 类的构造函数显式创建一个 3D-aware axes 实例,方法是将 Figure 实例作为参数传递:ax = Axes3D(fig)
或者,我们可以将 add_subplot 函数与 projection='3d' 参数一起使用:

ax = fig.add_subplot(1, 1, 1, projection='3d')

或使用带 subplot_kw={'projection': '3d'} 参数的 plt.subplots

fig, ax = plt.subplots(1, 1, figsize=(8, 6), subplot_kw={'projection': '3d'})

这样,只要我们以适当的方式指定 projection 参数,我们就可以使用以前用于 2D 图形的所有 Axes 布局方法。请注意,使用 add_subplot 时,可以将 axes 对象与同一图形中的 2D 和 3D 投影混合,但使用 plt.subplots 时,参数 subplot_kw 将应用于所有子图。

创建 3D-aware axes 实例并将其添加到图形后,Axes3D 类方法(plot_surfaceplot_wireframecontour)可用于在三维透视图中将数据绘制为曲面。这些函数的使用方式与上一节中使用色图的方式几乎相同:这些 3D 绘图函数都将二维坐标和数据数组 X、Y 和 Z 作为第一个参数。每个函数还有其他参数来调整特定属性。如,plot_surface 函数使用参数 rstride 和 cstride(行和列跨距)从输入数组中选择数据(以避免数据点过于密集)。contour 函数和 contourf 函数有可选参数 zdir 和 offset,用于选择投影方向(可选值有 x、y 和 z)和显示投影的平面。

除了 3D 曲面绘图的方法之外,还有从 2D axes 直接广义化的绘图函数,如 plot、scatter、bar 和 bar3d,在 Axes3D 类可用的版本中,这些函数对 z 坐标有附加参数。像它们的 2D 版本一样,这些函数需要一维数据数组,而不是的二维坐标数组。

当涉及轴标题、标签、刻度和刻度标签时,本章前面详细描述的用于 2D 图形的所有方法都直接推广到 3D 图形。如有新方法 set_zlabelset_zticksset_zticklabels 来处理 z 轴的属性。Axes3D 对象还为 3D 特定操作和属性提供了新的类方法。特别是,view_init 方法可用于更改查看图形的角度,该方法以 elevation(仰角)和 azimuth(方位角)作为第一个和第二个参数。

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
fig, axes = plt.subplots(1, 3, figsize=(14, 4), subplot_kw={'projection': '3d'})

def title_and_labels(ax, title):
ax.set_title(title)
ax.set_xlabel("$x$", fontsize=16)
ax.set_ylabel("$y$", fontsize=16)
ax.set_zlabel("$z$", fontsize=16)


x = y = np.linspace(-3, 3, 74)
X, Y = np.meshgrid(x, y)

R = np.sqrt(X**2 + Y**2)
Z = np.sin(4 * R) / R

norm = mpl.colors.Normalize(-abs(Z).max(), abs(Z).max())

p = axes[0].plot_surface(X, Y, Z, rstride=1, cstride=1, linewidth=0,
antialiased=False, norm=norm, cmap=mpl.cm.Blues)

cb = fig.colorbar(p, ax=axes[0], shrink=0.6)
title_and_labels(axes[0], "plot_surface")

axes[1].plot_wireframe(X, Y, Z, rstride=2, cstride=2, color="darkgrey")
title_and_labels(axes[1], "plot_wireframe")

axes[2].contour(X, Y, Z, zdir='z', offset=0, norm=norm, cmap=mpl.cm.Blues)
axes[2].contour(X, Y, Z, zdir='y', offset=3, norm=norm, cmap=mpl.cm.Blues)
title_and_labels(axes[2], "contour")

plt.show()

本文翻译自:Numerical Python: A Practical Techniques Approach for Industry(R. Johansson, Numerical Python, DOI 10.1007/978-1-4842-0553-2_1)