艺术家教程#

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

Matplotlib API 有三层。

  • the matplotlib.backend_bases.FigureCanvas 是绘制图形的区域

  • 这个 matplotlib.backend_bases.Renderer 对象知道如何在 FigureCanvas 上绘制。

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

The FigureCanvasRenderer 处理与用户界面工具包(如 wxPython)或绘图语言(如 PostScript®)通信的所有细节,而 Artist 处理所有高级构造,如表示和布局图形、文本和线条。典型用户将花费 95% 的时间与 Artists 打交道。

有两种类型的 Artists:基本图形和容器。基本图形代表我们想要绘制到画布上的标准图形对象:Line2DRectangleTextAxesImage 等,而容器是放置它们的地方(AxisAxesFigure)。标准的用法是创建一个 Figure 实例,使用 Figure 来创建一个或多个 Axes 实例,并使用 Axes 实例的辅助方法来创建基本图形。在下面的例子中,我们使用 matplotlib.pyplot.figure() 创建一个 Figure 实例,这是一个方便的方法,用于实例化 Figure 实例并将它们与用户界面或绘图工具包 FigureCanvas 连接起来。正如我们将在下面讨论的,这不是必须的——你可以直接使用 PostScript、PDF、Gtk+ 或 wxPython 的 FigureCanvas 实例,直接实例化你的 Figures 并自己连接它们——但由于我们在这里专注于 Artist API,我们将让 pyplot 为我们处理一些细节:

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

The 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 列表的长度为1,并且包含与 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 表示,每个元素都有大量的属性来配置其外观。图本身包含一个与图大小完全相同的 Rectangle,您可以使用它来设置图的背景颜色和透明度。同样,每个 Axes 边界框(典型 Matplotlib 图中的标准白色框和黑色边缘)都有一个 Rectangle 实例,该实例决定了 Axes 的颜色、透明度和其他属性。这些实例存储为成员变量 Figure.patchAxes.patch <matplotlib.axes.Axes.patch>`(“Patch”是从 MATLAB 继承的名称,是图上的 2D 颜色“补丁”,例如矩形、圆形和多边形)。每个 Matplotlib ``Artist` 都有以下属性

属性

描述

alpha

透明度 - 一个从 0-1 的标量

动画

一个用于辅助动画绘制的布尔值

axes

艺术家所在的轴,可能为 None

clip_box

裁剪艺术家的边界框

clip_on

是否启用裁剪

clip_path

艺术家被裁剪的路径

包含

一个拾取函数,用于测试艺术家是否包含拾取点

figure

艺术家所处的图形实例,可能为 None

标签

文本标签(例如,用于自动标记)

选择器

一个控制对象拾取的Python对象

变换

变换

可见的

一个布尔值,指示是否应绘制艺术家

zorder

一个决定绘制顺序的数字

栅格化

布尔值;将矢量转换为光栅图形(用于压缩和EPS透明度)

每个属性都通过传统的 setter 或 getter 来访问(是的,我们知道这会让 Python 开发者感到不适,我们计划支持通过属性或特性直接访问,但目前尚未实现)。例如,要将当前的 alpha 值乘以一半:

a = o.get_alpha()
o.set_alpha(0.5*a)

如果你想一次设置多个属性,你也可以使用带有关键字参数的 set 方法。例如:

o.set(alpha=0.5, zorder=2)

如果你在Python shell中进行交互式工作,检查``Artist``属性的一个便捷方法是使用:func:matplotlib.artist.getp`函数(在pyplot中简化为:func:`~matplotlib.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 属性,因此您可以参考交互式“帮助”或 matplotlib.artist 以获取给定对象的属性列表。

对象容器#

既然我们已经知道如何检查和设置我们想要配置的给定对象的属性,我们需要知道如何获取该对象。如引言中所述,有两种类型的对象:原始对象和容器对象。原始对象通常是您想要配置的内容(例如 Text 实例的字体,Line2D 的宽度),尽管容器对象也有一些属性——例如 Axes Artist 是一个包含图表中许多原始对象的容器,但它也有一些属性,如 xscale 来控制 x 轴是 'linear' 还是 'log'。在本节中,我们将回顾各种容器对象存储您想要获取的 Artists 的位置。

图形容器#

顶级容器 Artistmatplotlib.figure.Figure,它包含了图中的所有内容。图的背景是一个 Rectangle,存储在 Figure.patch 中。当你添加子图(add_subplot())和轴(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:>, <matplotlib.axes._axes.Axes object at 0x7f0768702be0>]

因为图形保持了“当前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 的默认坐标系统将简单地以像素为单位(这通常不是您想要的)。如果您改为使用图形级方法来添加艺术家(例如,使用 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包含的艺术家的总结

图属性

描述

axes

Axes 实例列表

补丁

Rectangle 背景

图片

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

传说

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

Figure Line2D 实例列表(很少使用,参见 Axes.lines

补丁

图表 Patch 列表(很少使用,参见 Axes.patches

texts

一个 Figure Text 实例的列表

Axes 容器#

The matplotlib.axes.Axes 是 Matplotlib 宇宙的中心 -- 它包含了图表中使用的大多数 Artists,并有许多辅助方法来创建和添加这些 Artists 到自身,以及访问和自定义其包含的 Artists 的辅助方法。与 Figure 类似,它包含一个 Patch 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 对,我们将长度为一的列表的第一个元素解包到 line 变量中。该线条已被添加到 Axes.lines 列表中:

In [229]: print(ax.lines)
[<matplotlib.lines.Line2D at 0xd378b0c>]

同样地,创建补丁的方法,如 bar() 创建一个矩形列表,会将补丁添加到 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 中包含的数据,以更新控制自动缩放的数据结构,从而调整视图限制以包含绘制的数据。

不过,您可以自己创建对象并将它们直接添加到 Axes 中,使用诸如 add_lineadd_patch 等辅助方法。以下是一个注释的交互式会话,说明了正在发生的事情:

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 类型以及存储位置。

坐标轴辅助方法

艺术家

容器

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 - 文本

Text

ax.texts

除了所有这些 ArtistsAxes 包含两个重要的 Artist 容器:XAxisYAxis,它们负责绘制刻度和标签。这些存储为实例变量 xaxisyaxisXAxisYAxis 容器将在下面详细介绍,但请注意,Axes 包含许多辅助方法,这些方法将调用转发给 Axis 实例,因此您通常不需要直接与它们打交道,除非您想这样做。例如,您可以使用 Axes 辅助方法设置 XAxis 刻度标签的字体颜色:

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

以下是 Axes 包含的艺术家概述

Axes 属性

描述

艺术家

一个 Artist 实例的 ArtistList

补丁

用于 Axes 背景的 Rectangle 实例

集合

ArtistListCollection 实例

图片

一个 AxesImageArtistList

一个 Line2D 实例的 ArtistList

补丁

一个 Patch 实例的 ArtistList

texts

一个 Text 实例的 ArtistList

x轴

一个 matplotlib.axis.XAxis 实例

y轴

一个 matplotlib.axis.YAxis 实例

图例可以通过 get_legend 访问。

轴容器#

The matplotlib.axis.Axis 实例处理刻度线、网格线、刻度标签和轴标签的绘制。您可以分别为 y 轴配置左侧和右侧的刻度,以及分别为 x 轴配置上侧和下侧的刻度。Axis 还存储用于自动缩放、平移和缩放的数据和视图间隔,以及控制刻度放置位置和它们如何表示为字符串的 LocatorFormatter 实例。

每个 Axis 对象包含一个 label 属性(这是 pyplot 在调用 xlabelylabel 时修改的内容),以及一个主要和次要刻度列表。刻度是 axis.XTickaxis.YTick 实例,它们包含实际渲染刻度和刻度标签的线和文本原语。因为刻度是根据需要动态创建的(例如,在平移和缩放时),您应该通过访问器方法 axis.Axis.get_major_ticksaxis.Axis.get_minor_ticks 来访问主要和次要刻度列表。尽管刻度包含所有原语并且将在下面介绍,但 Axis 实例有访问器方法,可以返回刻度线、刻度标签、刻度位置等:

fig, ax = plt.subplots()
axis = ax.xaxis
axis.get_ticklocs()
artists
array([0. , 0.2, 0.4, 0.6, 0.8, 1. ])
axis.get_ticklabels()
[Text(0.0, 0, '0.0'), Text(0.2, 0, '0.2'), Text(0.4, 0, '0.4'), Text(0.6000000000000001, 0, '0.6'), Text(0.8, 0, '0.8'), Text(1.0, 0, '1.0')]

注意,刻度线的数量是标签的两倍,因为默认情况下顶部和底部都有刻度线,但只有x轴下方有刻度标签;不过,这可以自定义。

axis.get_ticklines()
<a list of 12 Line2D ticklines objects>

通过上述方法,默认情况下你只能得到主要刻度的列表,但你也可以请求次要刻度:

axis.get_ticklabels(minor=True)
axis.get_ticklines(minor=True)
<a list of 0 Line2D ticklines objects>

以下是 Axis 的一些有用访问器方法的总结(这些方法在有用的地方有相应的设置器,例如 set_major_formatter()。)

轴访问器方法

描述

get_scale

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

get_view_interval

轴视图限制的间隔实例

get_data_interval

轴数据限制的间隔实例

get_gridlines

轴的网格线列表

get_label

轴标签 - 一个 Text 实例

get_offset_text

轴偏移文本 - 一个 Text 实例

get_ticklabels

一个 Text 实例列表 - 关键字 minor=True|False

get_ticklines

一个 Line2D 实例列表 - 关键字 minor=True|False

get_ticklocs

刻度位置列表 - 关键字 minor=True|False

get_major_locator

主要刻度的 ticker.Locator 实例

get_major_formatter

主要刻度的 ticker.Formatter 实例

get_minor_locator

次要刻度的 ticker.Locator 实例

get_minor_formatter

次刻度的 ticker.Formatter 实例

get_major_ticks

主要刻度的 Tick 实例列表

get_minor_ticks

次要刻度的 Tick 实例列表

grid

为主刻度或次刻度打开或关闭网格

这是一个例子,虽然不推荐其美观性,但它自定义了Axes和Tick属性。

# plt.figure creates a matplotlib.figure.Figure instance
fig = plt.figure()
rect = fig.patch  # a rectangle instance
rect.set_facecolor('lightgoldenrodyellow')

ax1 = fig.add_axes([0.1, 0.3, 0.4, 0.4])
rect = ax1.patch
rect.set_facecolor('lightslategray')


for label in ax1.xaxis.get_ticklabels():
    # label is a Text instance
    label.set_color('red')
    label.set_rotation(45)
    label.set_fontsize(16)

for line in ax1.yaxis.get_ticklines():
    # line is a Line2D instance
    line.set_color('green')
    line.set_markersize(25)
    line.set_markeredgewidth(3)

plt.show()
artists

Tick 容器#

在从 FigureAxes 再到 Axis 的下降过程中,matplotlib.axis.Tick 是我们最终的容器对象。Tick 包含刻度和网格线实例,以及上下刻度的标签实例。这些中的每一个都可以直接作为 Tick 的属性访问。

Tick 属性

描述

tick1line

一个 Line2D 实例

tick2line

一个 Line2D 实例

网格线

一个 Line2D 实例

label1

一个 Text 实例

label2

一个 Text 实例

以下是一个示例,它为右侧刻度设置了美元符号的格式,并将y轴右侧的刻度颜色设置为绿色。

import matplotlib.pyplot as plt
import numpy as np

# Fixing random state for reproducibility
np.random.seed(19680801)

fig, ax = plt.subplots()
ax.plot(100*np.random.rand(20))

# Use automatic StrMethodFormatter
ax.yaxis.set_major_formatter('${x:1.2f}')

ax.yaxis.set_tick_params(which='major', labelcolor='green',
                         labelleft=False, labelright=True)

plt.show()
dollar ticks

由 Sphinx-Gallery 生成的图库