注意
转至底部 下载完整示例代码。
Artist 教程#
使用 Artist 对象在画布上进行渲染。
Matplotlib API 分为三个层级。
matplotlib.backend_bases.FigureCanvas是绘图区域,用于绘制图形。matplotlib.backend_bases.Renderer是知道如何向matplotlib.backend_bases.FigureCanvas上进行绘制的对象。而
matplotlib.artist.Artist是知道如何使用渲染器在画布上进行绘图的对象。
matplotlib.backend_bases.FigureCanvas 和 matplotlib.backend_bases.Renderer 处理与用户界面工具包(如 wxPython)或绘图语言(如 PostScript®)交互的所有细节,而 Artist 则处理所有高级结构,例如表示和布局图形、文本和线条。普通用户 95% 的时间都在与 Artists 打交道。
Artists 有两种类型:图元(primitives)和容器(containers)。图元代表我们要绘制到画布上的标准图形对象:Line2D、Rectangle、Text、AxesImage 等;而容器则是放置它们的地方(Axis、Axes 和 Figure)。标准用法是创建一个 Figure 实例,使用该 Figure 创建一个或多个 Axes 实例,并使用 Axes 实例的辅助方法来创建图元。在下面的示例中,我们使用 matplotlib.pyplot.figure() 创建一个 Figure 实例,这是一种用于实例化 Figure 对象并将其与 GUI 框架连接的便捷方法,以便它们能显示在屏幕窗口中。
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(2, 1, 1) # two rows, one column, first plot
Axes 可能是 Matplotlib API 中最重要的类,也是您大部分时间要使用的类。这是因为 Axes 是大多数对象放置的绘图区域,并且 Axes 拥有许多特殊的辅助方法(plot()、text()、hist()、imshow())来创建最常见的图形图元(分别为 Line2D、Text、Rectangle、AxesImage)。这些辅助方法将获取您的数据(例如 numpy 数组和字符串),并根据需要创建原始的 Artist 实例(例如 Line2D),将它们添加到相关的容器中,并在请求时进行绘制。如果您想在任意位置创建 Axes,只需使用 add_axes() 方法,该方法接收一个 [left, bottom, width, height] 列表,其值为 0-1 的相对图形坐标。
fig2 = plt.figure()
ax2 = fig2.add_axes((0.15, 0.1, 0.7, 0.3))
继续我们的示例
在此示例中,ax 是由上述 fig.add_subplot 调用创建的 Axes 实例;当您调用 ax.plot 时,它会创建一个 Line2D 实例并将其添加到 Axes 中。在下面的交互式 IPython 会话中,您可以看到 Axes.lines 列表长度为一,并且包含与 line, = ax.plot... 调用所返回的相同的线。
In [101]: ax.lines[0]
Out[101]: <matplotlib.lines.Line2D at 0x19a95710>
In [102]: line
Out[102]: <matplotlib.lines.Line2D at 0x19a95710>
如果您后续继续调用 ax.plot,那么额外的线会被添加到该列表中。您可以稍后通过调用其 remove 方法来移除某条线。
Axes 还有辅助方法来配置和装饰 x 轴和 y 轴的刻度、刻度标签以及轴标签。
xtext = ax.set_xlabel('my xdata') # returns a Text instance
ytext = ax.set_ylabel('my ydata')
当您调用 ax.set_xlabel 时,它会将信息传递给 XAxis 的 Text 实例。每个 Axes 实例都包含一个 XAxis 和一个 YAxis 实例,它们负责处理刻度、刻度标签和轴标签的布局与绘制。
尝试创建下方的图形。
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
fig.subplots_adjust(top=0.8)
ax1 = fig.add_subplot(211)
ax1.set_ylabel('Voltage [V]')
ax1.set_title('A sine wave')
t = np.arange(0.0, 1.0, 0.01)
s = np.sin(2*np.pi*t)
line, = ax1.plot(t, s, color='blue', lw=2)
# Fixing random state for reproducibility
np.random.seed(19680801)
ax2 = fig.add_axes((0.15, 0.1, 0.7, 0.3))
n, bins, patches = ax2.hist(np.random.randn(1000), 50,
facecolor='yellow', edgecolor='yellow')
ax2.set_xlabel('Time [s]')
plt.show()

自定义您的对象#
图形中的每个元素都由 Matplotlib Artist 表示,每个 Artist 都有广泛的属性列表来配置其外观。图形本身包含一个大小与图形完全相同的 Rectangle,您可以使用它来设置图形的背景颜色和透明度。同样,每个 Axes 边界框(标准 Matplotlib 图中带有黑色边缘的白色矩形)都有一个 Rectangle 实例,它决定了 Axes 的颜色、透明度和其他属性。这些实例作为成员变量 Figure.patch 和 Axes.patch 存储(“Patch”是一个继承自 MATLAB 的名称,它是图形上的 2D“色块”,例如矩形、圆形和多边形)。每个 Matplotlib Artist 都具有以下属性:
属性 |
描述 |
|---|---|
alpha |
透明度 - 一个 0 到 1 之间的标量 |
animated |
一个用于促进动画绘制的布尔值 |
坐标轴 |
Artist 所属的 Axes(可能为 None) |
clip_box |
剪裁 Artist 的边界框 |
clip_on |
是否启用了剪裁 |
clip_path |
Artist 剪裁到的路径 |
contains |
一个用于测试 Artist 是否包含拾取点的拾取函数 |
figure |
Artist 所属的 Figure 实例(可能为 None) |
label |
文本标签(例如用于自动标注) |
picker |
一个控制对象拾取的 Python 对象 |
transform |
变换 (Transformation) |
visible |
一个决定是否绘制该 Artist 的布尔值 |
zorder |
一个决定绘制顺序的数字 |
rasterized |
布尔值;将矢量转换为栅格图形(用于压缩和 EPS 透明度) |
每个属性都可以通过老式的 setter 或 getter 访问(是的,我们知道这会惹恼 Python 开发者,我们计划支持通过 properties 或 traits 进行直接访问,但尚未实现)。例如,将当前透明度乘以二分之一:
a = o.get_alpha()
o.set_alpha(0.5*a)
如果您想一次性设置多个属性,也可以使用带有关键字参数的 set 方法。例如:
o.set(alpha=0.5, zorder=2)
如果您在 Python shell 中进行交互式操作,查看 Artist 属性的一种便捷方法是使用 matplotlib.artist.getp() 函数(在 pyplot 中仅为 getp()),它会列出属性及其值。这也适用于派生自 Artist 的类,例如 Figure 和 Rectangle。以下是上述提到的 Figure 矩形属性:
In [149]: matplotlib.artist.getp(fig.patch)
agg_filter = None
alpha = None
animated = False
antialiased or aa = False
bbox = Bbox(x0=0.0, y0=0.0, x1=1.0, y1=1.0)
capstyle = butt
children = []
clip_box = None
clip_on = True
clip_path = None
contains = None
data_transform = BboxTransformTo( TransformedBbox( Bbox...
edgecolor or ec = (1.0, 1.0, 1.0, 1.0)
extents = Bbox(x0=0.0, y0=0.0, x1=640.0, y1=480.0)
facecolor or fc = (1.0, 1.0, 1.0, 1.0)
figure = Figure(640x480)
fill = True
gid = None
hatch = None
height = 1
in_layout = False
joinstyle = miter
label =
linestyle or ls = solid
linewidth or lw = 0.0
patch_transform = CompositeGenericTransform( BboxTransformTo( ...
path = Path(array([[0., 0.], [1., 0.], [1.,...
path_effects = []
picker = None
rasterized = None
sketch_params = None
snap = None
transform = CompositeGenericTransform( CompositeGenericTra...
transformed_clip_path_and_affine = (None, None)
url = None
verts = [[ 0. 0.] [640. 0.] [640. 480.] [ 0. 480....
visible = True
width = 1
window_extent = Bbox(x0=0.0, y0=0.0, x1=640.0, y1=480.0)
x = 0
xy = (0, 0)
y = 0
zorder = 1
所有类的文档字符串中也都包含了 Artist 属性,因此您可以查阅交互式“help”或 matplotlib.artist 以获取给定对象的属性列表。
对象容器#
既然我们已经知道如何检查和设置我们想要配置的给定对象的属性,我们需要知道如何获取该对象。正如在引言中提到的,有两种对象:图元和容器。图元通常是您想要配置的对象(Text 实例的字体、Line2D 的宽度),尽管容器也有一些属性——例如 Axes Artist 是一个包含您绘图中许多图元的容器,但它也具有诸如 xscale 之类的属性,用于控制 x 轴是“线性”还是“对数”。在本节中,我们将回顾各种容器对象存储您想要获取的 Artists 的位置。
Figure 容器#
顶层容器 Artist 是 matplotlib.figure.Figure,它包含图形中的所有内容。图形的背景是一个 Rectangle,存储在 Figure.patch 中。当您向图形添加子图(add_subplot())和 Axes(add_axes())时,它们会被追加到 Figure.axes 中。这些对象也会由创建它们的方法返回。
In [156]: fig = plt.figure()
In [157]: ax1 = fig.add_subplot(211)
In [158]: ax2 = fig.add_axes((0.1, 0.1, 0.7, 0.3))
In [159]: ax1
Out[159]: <Axes:>
In [160]: print(fig.axes)
[<Axes: >, <Axes: >]
因为图形维持着“当前 Axes”的概念(请参阅 Figure.gca 和 Figure.sca)以支持 pylab/pyplot 状态机,您不应直接从 Axes 列表中插入或删除 Axes,而应使用 add_subplot() 和 add_axes() 方法来插入,并使用 Axes.remove 方法来删除。但是,您可以自由地遍历 Axes 列表或通过索引访问您想要自定义的 Axes 实例。这是一个开启所有 Axes 网格的示例:
for ax in fig.axes:
ax.grid(True)
图形还有它自己的 images、lines、patches 和 text 属性,您可以使用它们直接添加图元。执行此操作时,Figure 的默认坐标系仅为像素(这通常不是您想要的)。如果您改用图形级方法来添加 Artists(例如,使用 Figure.text 添加文本),则默认坐标系将是“图形坐标”,其中 (0, 0) 是图形的左下角,(1, 1) 是图形的右上角。
与所有 Artist 一样,您可以通过设置变换属性来控制此坐标系。您可以通过将 Artist 的变换设置为 fig.transFigure 来显式使用“图形坐标”。
import matplotlib.lines as lines
fig = plt.figure()
l1 = lines.Line2D([0, 1], [0, 1], transform=fig.transFigure, figure=fig)
l2 = lines.Line2D([0, 1], [1, 0], transform=fig.transFigure, figure=fig)
fig.lines.extend([l1, l2])
plt.show()

以下是 Figure 所包含 Artists 的摘要:
Axes 容器#
matplotlib.axes.Axes 是 Matplotlib 宇宙的中心——它包含了图形中使用的绝大多数 Artists,并拥有许多用于创建这些 Artists 并将其添加到自身的方法,以及用于访问和自定义其包含的 Artists 的辅助方法。与 Figure 一样,它包含一个 Patch(即 matplotlib.axes.Axes.patch),对于笛卡尔坐标系,它是一个 Rectangle;对于极坐标系,它是一个 Circle;此 patch 决定了绘图区域的形状、背景和边框。
ax = fig.add_subplot()
rect = ax.patch # a Rectangle instance
rect.set_facecolor('green')
当您调用绘图方法时(例如经典的 plot)并传入数组或列表,该方法将创建一个 matplotlib.lines.Line2D 实例,用作为关键字参数传入的所有 Line2D 属性更新该线,将线添加到 Axes 中,并将其返回给您。
In [213]: x, y = np.random.rand(2, 100)
In [214]: line, = ax.plot(x, y, '-', color='blue', linewidth=2)
plot 返回一个线列表,因为您可以传入多组 x, y 对进行绘制,我们正在将长度为 1 的列表中的第一个元素解包到 line 变量中。该线已被添加到 Axes.lines 列表中。
In [229]: print(ax.lines)
[<matplotlib.lines.Line2D at 0xd378b0c>]
同样,创建 patches 的方法(如 bar() 会创建一个矩形列表)会将 patches 添加到 Axes.patches 列表中。
In [233]: n, bins, rectangles = ax.hist(np.random.randn(1000), 50)
In [234]: rectangles
Out[234]: <BarContainer object of 50 artists>
In [235]: print(len(ax.patches))
Out[235]: 50
您不应将对象直接添加到 Axes.lines 或 Axes.patches 列表中,因为 Axes 在创建和添加对象时需要执行一些操作:
它设置
Artist的figure和axes属性;它设置默认的
Axes变换(除非已经设置了一个);它检查
Artist中包含的数据,以更新控制自动缩放的数据结构,从而使视图限制能够调整为包含已绘制的数据。
尽管如此,您仍然可以自行创建对象,并使用 add_line 和 add_patch 等辅助方法将其直接添加到 Axes 中。以下是一个带注释的交互式会话,说明了其过程:
In [262]: fig, ax = plt.subplots()
# create a rectangle instance
In [263]: rect = matplotlib.patches.Rectangle((1, 1), width=5, height=12)
# by default the Axes instance is None
In [264]: print(rect.axes)
None
# and the transformation instance is set to the "identity transform"
In [265]: print(rect.get_data_transform())
IdentityTransform()
# now we add the Rectangle to the Axes
In [266]: ax.add_patch(rect)
# and notice that the ax.add_patch method has set the Axes
# instance
In [267]: print(rect.axes)
Axes(0.125,0.1;0.775x0.8)
# and the transformation has been set too
In [268]: print(rect.get_data_transform())
CompositeGenericTransform(
TransformWrapper(
BlendedAffine2D(
IdentityTransform(),
IdentityTransform())),
CompositeGenericTransform(
BboxTransformFrom(
TransformedBbox(
Bbox(x0=0.0, y0=0.0, x1=1.0, y1=1.0),
TransformWrapper(
BlendedAffine2D(
IdentityTransform(),
IdentityTransform())))),
BboxTransformTo(
TransformedBbox(
Bbox(x0=0.125, y0=0.10999999999999999, x1=0.9, y1=0.88),
BboxTransformTo(
TransformedBbox(
Bbox(x0=0.0, y0=0.0, x1=6.4, y1=4.8),
Affine2D(
[[100. 0. 0.]
[ 0. 100. 0.]
[ 0. 0. 1.]])))))))
# the default Axes transformation is ax.transData
In [269]: print(ax.transData)
CompositeGenericTransform(
TransformWrapper(
BlendedAffine2D(
IdentityTransform(),
IdentityTransform())),
CompositeGenericTransform(
BboxTransformFrom(
TransformedBbox(
Bbox(x0=0.0, y0=0.0, x1=1.0, y1=1.0),
TransformWrapper(
BlendedAffine2D(
IdentityTransform(),
IdentityTransform())))),
BboxTransformTo(
TransformedBbox(
Bbox(x0=0.125, y0=0.10999999999999999, x1=0.9, y1=0.88),
BboxTransformTo(
TransformedBbox(
Bbox(x0=0.0, y0=0.0, x1=6.4, y1=4.8),
Affine2D(
[[100. 0. 0.]
[ 0. 100. 0.]
[ 0. 0. 1.]])))))))
# notice that the xlimits of the Axes have not been changed
In [270]: print(ax.get_xlim())
(0.0, 1.0)
# but the data limits have been updated to encompass the rectangle
In [271]: print(ax.dataLim.bounds)
(1.0, 1.0, 5.0, 12.0)
# we can manually invoke the auto-scaling machinery
In [272]: ax.autoscale_view()
# and now the xlim are updated to encompass the rectangle, plus margins
In [273]: print(ax.get_xlim())
(0.75, 6.25)
# we have to manually force a figure draw
In [274]: fig.canvas.draw()
有许多 Axes 辅助方法用于创建图元 Artists 并将其添加到各自的容器中。下表汇总了其中的一小部分,它们创建的 Artist 类型以及它们存储的位置:
Axes 辅助方法 |
Artist |
容器 |
|---|---|---|
|
ax.texts |
|
|
ax.patches |
|
|
ax.lines 和 ax.patches |
|
|
ax.patches |
|
|
ax.patches |
|
|
ax.images |
|
|
ax.get_legend() |
|
|
ax.lines |
|
|
ax.collections |
|
|
ax.texts |
除了所有这些 Artists 外,Axes 还包含两个重要的 Artist 容器:XAxis 和 YAxis,它们负责刻度和标签的绘制。这些存储为实例变量 matplotlib.axes.Axes.xaxis 和 matplotlib.axes.Axes.yaxis。XAxis 和 YAxis 容器将在下面详细说明,但请注意,Axes 包含许多辅助方法,这些方法将调用转发到 Axis 实例,因此除非您愿意,否则通常不需要直接使用它们。例如,您可以使用 Axes 辅助方法设置 XAxis 刻度标签的字体颜色。
ax.tick_params(axis='x', labelcolor='orange')
以下是 Axes 所包含 Artists 的摘要:
Axes 属性 |
描述 |
|---|---|
艺术对象(artists) |
|
patch |
用于 Axes 背景的 |
collections |
|
images |
|
lines |
|
patches |
|
texts |
|
xaxis |
|
yaxis |
可以通过 get_legend 访问图例。
Axis 容器#
matplotlib.axis.Axis 实例负责刻度线、网格线、刻度标签和轴标签的绘制。您可以分别为 y 轴配置左右刻度,并分别为 x 轴配置上下刻度。Axis 还存储自动缩放、平移和缩放所使用的数据和视图间隔,以及控制刻度放置位置及其字符串表示方式的 Locator 和 Formatter 实例。
每个 Axis 对象都包含一个 label 属性(这是 set_xlabel / set_ylabel 在内部修改的内容)以及主要和次要刻度列表。刻度是 axis.XTick 和 axis.YTick 实例,它们包含渲染刻度和刻度标签的实际线条和文本图元。由于刻度是根据需要动态创建和修改的(例如在平移和缩放时),因此不建议直接处理刻度及其部分(刻度线、刻度标签、网格线)。相反,应使用刻度定位器、刻度格式化程序以及通过 tick_params 进行的样式配置等高级概念。有关详细信息,请参阅 Axis ticks。
以下是 Axis 一些有用访问器方法的摘要(在适当的情况下,它们有相应的 setter,例如 set_major_formatter()):
Axis 访问器方法 |
描述 |
|---|---|
Axis 的比例,例如 'log' 或 'linear' |
|
Axis 视图限制的间隔实例 |
|
Axis 数据限制的间隔实例 |
|
Axis 标签 - 一个 |
|
用于主要刻度的 |
|
用于主要刻度的 |
|
用于次要刻度的 |
|
用于次要刻度的 |
|
刻度、刻度标签和网格线的样式设置 |
|
开启或关闭主要或次要刻度的网格 |
完整的 Axis API 可在 matplotlib.axis 找到。