Artist 教程#

使用 Artist 对象在画布上进行渲染。

Matplotlib API 分为三个层级。

  • matplotlib.backend_bases.FigureCanvas 是绘图区域,用于绘制图形。

  • matplotlib.backend_bases.Renderer 是知道如何向 matplotlib.backend_bases.FigureCanvas 上进行绘制的对象。

  • matplotlib.artist.Artist 是知道如何使用渲染器在画布上进行绘图的对象。

matplotlib.backend_bases.FigureCanvasmatplotlib.backend_bases.Renderer 处理与用户界面工具包(如 wxPython)或绘图语言(如 PostScript®)交互的所有细节,而 Artist 则处理所有高级结构,例如表示和布局图形、文本和线条。普通用户 95% 的时间都在与 Artists 打交道。

Artists 有两种类型:图元(primitives)和容器(containers)。图元代表我们要绘制到画布上的标准图形对象:Line2DRectangleTextAxesImage 等;而容器则是放置它们的地方(AxisAxesFigure)。标准用法是创建一个 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())来创建最常见的图形图元(分别为 Line2DTextRectangleAxesImage)。这些辅助方法将获取您的数据(例如 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))

继续我们的示例

import numpy as np
t = np.arange(0.0, 1.0, 0.01)
s = np.sin(2*np.pi*t)
line, = ax.plot(t, s, color='blue', lw=2)

在此示例中,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 方法来移除某条线。

line = ax.lines[0]
line.remove()

Axes 还有辅助方法来配置和装饰 x 轴和 y 轴的刻度、刻度标签以及轴标签。

xtext = ax.set_xlabel('my xdata')  # returns a Text instance
ytext = ax.set_ylabel('my ydata')

当您调用 ax.set_xlabel 时,它会将信息传递给 XAxisText 实例。每个 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()
A sine wave

自定义您的对象#

图形中的每个元素都由 Matplotlib Artist 表示,每个 Artist 都有广泛的属性列表来配置其外观。图形本身包含一个大小与图形完全相同的 Rectangle,您可以使用它来设置图形的背景颜色和透明度。同样,每个 Axes 边界框(标准 Matplotlib 图中带有黑色边缘的白色矩形)都有一个 Rectangle 实例,它决定了 Axes 的颜色、透明度和其他属性。这些实例作为成员变量 Figure.patchAxes.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 的类,例如 FigureRectangle。以下是上述提到的 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 容器#

顶层容器 Artistmatplotlib.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.gcaFigure.sca)以支持 pylab/pyplot 状态机,您不应直接从 Axes 列表中插入或删除 Axes,而应使用 add_subplot()add_axes() 方法来插入,并使用 Axes.remove 方法来删除。但是,您可以自由地遍历 Axes 列表或通过索引访问您想要自定义的 Axes 实例。这是一个开启所有 Axes 网格的示例:

for ax in fig.axes:
    ax.grid(True)

图形还有它自己的 imageslinespatchestext 属性,您可以使用它们直接添加图元。执行此操作时,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()
artists

以下是 Figure 所包含 Artists 的摘要:

Figure 属性

描述

坐标轴

Axes 实例列表

patch

Rectangle 背景

images

FigureImage 补丁列表 - 用于原始像素显示

legends

图形 Legend 实例列表(不同于 Axes.get_legend()

lines

图形 Line2D 实例列表(很少使用,请参阅 Axes.lines

patches

图形 Patch 列表(很少使用,请参阅 Axes.patches

texts

图形 Text 实例列表

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.linesAxes.patches 列表中,因为 Axes 在创建和添加对象时需要执行一些操作:

  • 它设置 Artistfigureaxes 属性;

  • 它设置默认的 Axes 变换(除非已经设置了一个);

  • 它检查 Artist 中包含的数据,以更新控制自动缩放的数据结构,从而使视图限制能够调整为包含已绘制的数据。

尽管如此,您仍然可以自行创建对象,并使用 add_lineadd_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

容器

annotate - 文本注释

Annotation

ax.texts

bar - 条形图

Rectangle

ax.patches

errorbar - 误差线图

Line2DRectangle

ax.lines 和 ax.patches

fill - 填充区域

Polygon

ax.patches

hist - 直方图

Rectangle

ax.patches

imshow - 图像数据

AxesImage

ax.images

legend - Axes 图例

Legend

ax.get_legend()

plot - xy 图

Line2D

ax.lines

scatter - 散点图

PolyCollection

ax.collections

text - 文本

文本

ax.texts

除了所有这些 Artists 外,Axes 还包含两个重要的 Artist 容器:XAxisYAxis,它们负责刻度和标签的绘制。这些存储为实例变量 matplotlib.axes.Axes.xaxismatplotlib.axes.Axes.yaxisXAxisYAxis 容器将在下面详细说明,但请注意,Axes 包含许多辅助方法,这些方法将调用转发到 Axis 实例,因此除非您愿意,否则通常不需要直接使用它们。例如,您可以使用 Axes 辅助方法设置 XAxis 刻度标签的字体颜色。

ax.tick_params(axis='x', labelcolor='orange')

以下是 Axes 所包含 Artists 的摘要:

Axes 属性

描述

艺术对象(artists)

Artist 实例的 ArtistList

patch

用于 Axes 背景的 Rectangle 实例

collections

Collection 实例的 ArtistList

images

AxesImageArtistList

lines

Line2D 实例的 ArtistList

patches

Patch 实例的 ArtistList

texts

Text 实例的 ArtistList

xaxis

matplotlib.axis.XAxis 实例

yaxis

matplotlib.axis.YAxis 实例

可以通过 get_legend 访问图例。

Axis 容器#

matplotlib.axis.Axis 实例负责刻度线、网格线、刻度标签和轴标签的绘制。您可以分别为 y 轴配置左右刻度,并分别为 x 轴配置上下刻度。Axis 还存储自动缩放、平移和缩放所使用的数据和视图间隔,以及控制刻度放置位置及其字符串表示方式的 LocatorFormatter 实例。

每个 Axis 对象都包含一个 label 属性(这是 set_xlabel / set_ylabel 在内部修改的内容)以及主要和次要刻度列表。刻度是 axis.XTickaxis.YTick 实例,它们包含渲染刻度和刻度标签的实际线条和文本图元。由于刻度是根据需要动态创建和修改的(例如在平移和缩放时),因此不建议直接处理刻度及其部分(刻度线、刻度标签、网格线)。相反,应使用刻度定位器、刻度格式化程序以及通过 tick_params 进行的样式配置等高级概念。有关详细信息,请参阅 Axis ticks

以下是 Axis 一些有用访问器方法的摘要(在适当的情况下,它们有相应的 setter,例如 set_major_formatter()):

Axis 访问器方法

描述

get_scale

Axis 的比例,例如 'log' 或 'linear'

get_view_interval

Axis 视图限制的间隔实例

get_data_interval

Axis 数据限制的间隔实例

get_label

Axis 标签 - 一个 Text 实例

get_major_locator

用于主要刻度的 ticker.Locator 实例

get_major_formatter

用于主要刻度的 ticker.Formatter 实例

get_minor_locator

用于次要刻度的 ticker.Locator 实例

get_minor_formatter

用于次要刻度的 ticker.Formatter 实例

get_tick_params

刻度、刻度标签和网格线的样式设置

grid

开启或关闭主要或次要刻度的网格

完整的 Axis API 可在 matplotlib.axis 找到。

由 Sphinx-Gallery 生成的画廊