转换教程

与任何图形包一样,matplotlib构建在转换框架之上,以便在坐标系(即用户区)之间轻松移动。 data 坐标系 axes 坐标系 图形 坐标系,以及 显示 坐标系。在95%的绘图中,您不需要考虑这一点,因为它是在遮光罩下发生的,但是当您推进自定义图形生成的限制时,了解这些对象有助于重用Matplotlib提供给您的现有转换,或创建自己的转换(请参见 matplotlib.transforms )下表总结了一些有用的坐标系、在该坐标系中应使用的转换对象以及该系统的说明。在 Transformation Object 列, ax 是一个 Axes 实例,以及 fig 是一个 Figure 实例。

协调 转换对象 描述
“数据” ax.transData 数据的坐标系,由xlim和ylim控制。
“斧子” ax.transAxes 的坐标系 Axes ;(0,0)是轴的左下角,(1,1)是轴的右上角。
“数字” fig.transFigure 的坐标系 Figure ;(0,0)是图形的左下角,(1,1)是图形的右上角。
“图形英寸” fig.dpi_scale_trans 的坐标系 Figure 以英寸为单位;(0,0)是图形的左下角,(宽度,高度)是图形的右上角(以英寸为单位)。
“显示” None, or IdentityTransform() 显示窗口的像素坐标系;(0,0)是窗口的左下角,(宽度,高度)是显示窗口的右上角(像素)。
“xaxis”,“yaxis” ax.get_xaxis_transform(), ax.get_yaxis_transform() 混合坐标系;在一个轴上使用数据坐标,在另一个轴上使用轴坐标。

上表中的所有转换对象在其坐标系中接受输入,并将输入转换为 显示 坐标系。这就是为什么 显示 坐标系有 None 对于 Transformation Object 列--它已经在 显示 协调。这些变换也知道如何反转自己,从 显示 回到原始坐标系。这在处理来自用户界面的事件时尤其有用,这些事件通常发生在显示空间中,并且您想知道在 data 坐标系。

请注意,在中指定对象 显示 坐标将改变其位置,如果 dpi 数字的变化。当打印或更改屏幕分辨率时,这可能会导致混淆,因为对象可以更改位置和大小。因此,对于放置在轴或图形中的艺术家来说,将其变换设置为某个对象是最常见的。 其他IdentityTransform() ;将艺术家放置在轴上时的默认值,使用 add_artist 是为了改变 ax.transData .

数据坐标

让我们从最常用的坐标开始, data 坐标系。每当向轴添加数据时,matplotlib都会更新数据限制,最常见的更新方式是 set_xlim()set_ylim() 方法。例如,在下图中,数据限制在X轴上从0延伸到10,在Y轴上从-1延伸到1。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches

x = np.arange(0, 10, 0.005)
y = np.exp(-x/2.) * np.sin(2*np.pi*x)

fig, ax = plt.subplots()
ax.plot(x, y)
ax.set_xlim(0, 10)
ax.set_ylim(-1, 1)

plt.show()
transforms tutorial

你可以使用 ax.transData 要从中转换的实例 data 对你 显示 坐标系,单点或点序列,如下所示:

In [14]: type(ax.transData)
Out[14]: <class 'matplotlib.transforms.CompositeGenericTransform'>

In [15]: ax.transData.transform((5, 0))
Out[15]: array([ 335.175,  247.   ])

In [16]: ax.transData.transform([(5, 0), (1, 2)])
Out[16]:
array([[ 335.175,  247.   ],
       [ 132.435,  642.2  ]])

你可以使用 inverted() 方法来创建一个将从 显示data 协调:

In [41]: inv = ax.transData.inverted()

In [42]: type(inv)
Out[42]: <class 'matplotlib.transforms.CompositeGenericTransform'>

In [43]: inv.transform((335.175,  247.))
Out[43]: array([ 5.,  0.])

如果您在本教程中键入,则 显示 如果窗口大小或dpi设置不同,坐标可能会不同。同样,在下图中,标记为点的显示可能与ipython会话中的不同,因为文档中的图形大小默认值不同。

x = np.arange(0, 10, 0.005)
y = np.exp(-x/2.) * np.sin(2*np.pi*x)

fig, ax = plt.subplots()
ax.plot(x, y)
ax.set_xlim(0, 10)
ax.set_ylim(-1, 1)

xdata, ydata = 5, 0
xdisplay, ydisplay = ax.transData.transform((xdata, ydata))

bbox = dict(boxstyle="round", fc="0.8")
arrowprops = dict(
    arrowstyle="->",
    connectionstyle="angle,angleA=0,angleB=90,rad=10")

offset = 72
ax.annotate('data = (%.1f, %.1f)' % (xdata, ydata),
            (xdata, ydata), xytext=(-2*offset, offset), textcoords='offset points',
            bbox=bbox, arrowprops=arrowprops)

disp = ax.annotate('display = (%.1f, %.1f)' % (xdisplay, ydisplay),
                   (xdisplay, ydisplay), xytext=(0.5*offset, -offset),
                   xycoords='figure pixels',
                   textcoords='offset points',
                   bbox=bbox, arrowprops=arrowprops)

plt.show()
transforms tutorial

注解

如果在GUI后端中运行上述示例中的源代码,还可能发现 data显示 注释不指向完全相同的点。这是因为显示点是在显示图形之前计算的,而图形用户界面后端在创建图形时可能会稍微调整其大小。如果您自己调整图形大小,效果会更明显。这就是为什么你很少想工作的一个很好的原因 显示 空间,但你可以连接到 'on_draw' Event 更新 图形 图形上的坐标;参见 事件处理和挑选 .

更改轴的X或Y限制时,数据限制将更新,以便转换生成新的显示点。请注意,当我们只更改ylim时,只有y显示坐标会被更改,当我们也更改xlim时,两者都会被更改。稍后当我们谈到 Bbox .

In [54]: ax.transData.transform((5, 0))
Out[54]: array([ 335.175,  247.   ])

In [55]: ax.set_ylim(-1, 2)
Out[55]: (-1, 2)

In [56]: ax.transData.transform((5, 0))
Out[56]: array([ 335.175     ,  181.13333333])

In [57]: ax.set_xlim(10, 20)
Out[57]: (10, 20)

In [58]: ax.transData.transform((5, 0))
Out[58]: array([-171.675     ,  181.13333333])

坐标轴

data 坐标系, axes 可能是第二个最有用的坐标系。这里的点(0,0)是轴或子地块的左下角,(0.5,0.5)是中心,(1.0,1.0)是右上角。您还可以引用范围之外的点,因此(-0.1,1.1)在轴的左侧和上方。在将文本放置到轴中时,此坐标系非常有用,因为您通常希望文本气泡位于固定的位置,例如,轴窗格的左上角,并且在平移或缩放时保持该位置不变。下面是一个简单的例子,它创建了四个面板,并将它们标记为“A”、“B”、“C”、“D”,正如您在日记中经常看到的那样。

fig = plt.figure()
for i, label in enumerate(('A', 'B', 'C', 'D')):
    ax = fig.add_subplot(2, 2, i+1)
    ax.text(0.05, 0.95, label, transform=ax.transAxes,
            fontsize=16, fontweight='bold', va='top')

plt.show()
transforms tutorial

也可以在 axes 坐标系,但这在我的经验中比使用 ax.transAxes 用于放置文本。尽管如此,这里有一个愚蠢的例子,它在数据空间中绘制一些随机点,并覆盖一个半透明的 Circle 以轴的四分之一为半径的轴的中间为中心--如果轴不保持纵横比(请参见 set_aspect() ,这看起来像一个椭圆。使用平移/缩放工具移动,或手动更改数据xlim和ylim,您将看到数据移动,但圆将保持固定,因为它不在 data 坐标和将始终保持在轴的中心。

fig, ax = plt.subplots()
x, y = 10*np.random.rand(2, 1000)
ax.plot(x, y, 'go', alpha=0.2)  # plot some data in data coordinates

circ = mpatches.Circle((0.5, 0.5), 0.25, transform=ax.transAxes,
                       facecolor='blue', alpha=0.75)
ax.add_patch(circ)
plt.show()
transforms tutorial

混合变换

并入 混合 混合的坐标空间 axes 具有 data 坐标非常有用,例如创建一个水平跨度,它突出显示Y数据的某些区域,但跨越X轴的跨度与数据限制、平移或缩放级别等无关。事实上,这些混合线和跨度非常有用,我们有内置函数使它们易于绘制(请参见 axhline()axvline()axhspan()axvspan() )但是为了教学目的,我们将在这里使用混合转换实现水平跨度。这个技巧只适用于可分离的转换,就像你在正常笛卡尔坐标系中看到的那样,但不适用于不可分离的转换,比如 PolarTransform .

import matplotlib.transforms as transforms

fig, ax = plt.subplots()
x = np.random.randn(1000)

ax.hist(x, 30)
ax.set_title(r'$\sigma=1 \/ \dots \/ \sigma=2$', fontsize=16)

# the x coords of this transformation are data, and the y coord are axes
trans = transforms.blended_transform_factory(
    ax.transData, ax.transAxes)
# highlight the 1..2 stddev region with a span.
# We want x to be in data coordinates and y to span from 0..1 in axes coords.
rect = mpatches.Rectangle((1, 0), width=1, height=1, transform=trans,
                          color='yellow', alpha=0.5)
ax.add_patch(rect)

plt.show()
$\sigma=1 \/ \dots \/ \sigma=2$

注解

x所在的混合变换 data 坐标和y轴 axes 坐标非常有用,因此我们有helper方法来返回Matplotlib内部用于绘制记号、记号标签等的版本 matplotlib.axes.Axes.get_xaxis_transform()matplotlib.axes.Axes.get_yaxis_transform() . 所以在上面的示例中,调用 blended_transform_factory() 可替换为 get_xaxis_transform ::

trans = ax.get_xaxis_transform()

在物理坐标中绘图

有时我们希望一个物体在绘图上有一定的物理尺寸。这里我们画了和上面一样的圆,但是在物理坐标系中。如果以交互方式进行,则可以看到更改地物的大小不会更改圆从左下角的偏移,也不会更改其大小,并且无论轴的纵横比如何,圆都保持为一个圆。

fig, ax = plt.subplots(figsize=(5, 4))
x, y = 10*np.random.rand(2, 1000)
ax.plot(x, y*10., 'go', alpha=0.2)  # plot some data in data coordinates
# add a circle in fixed-coordinates
circ = mpatches.Circle((2.5, 2), 1.0, transform=fig.dpi_scale_trans,
                       facecolor='blue', alpha=0.75)
ax.add_patch(circ)
plt.show()
transforms tutorial

如果我们改变图形大小,圆就不会改变它的绝对位置,而是被裁剪。

fig, ax = plt.subplots(figsize=(7, 2))
x, y = 10*np.random.rand(2, 1000)
ax.plot(x, y*10., 'go', alpha=0.2)  # plot some data in data coordinates
# add a circle in fixed-coordinates
circ = mpatches.Circle((2.5, 2), 1.0, transform=fig.dpi_scale_trans,
                       facecolor='blue', alpha=0.75)
ax.add_patch(circ)
plt.show()
transforms tutorial

另一个用途是在轴上的数据点周围放置具有设定物理尺寸的补丁。这里我们把两个变换加在一起。第一个设置椭圆大小的缩放比例,第二个设置椭圆的位置。然后将椭圆放置在原点,然后使用辅助变换 ScaledTranslation 把它移到正确的地方 ax.transData 坐标系。此帮助程序实例化为:

trans = ScaledTranslation(xt, yt, scale_trans)

在哪里? xtyt 是平移偏移量,以及 scale_trans 是一种可扩展的转换 xtyt 在应用偏移之前的转换时间。

注意在下面的转换中使用加号运算符。这段代码说:首先应用比例转换 fig.dpi_scale_trans 使椭圆的大小正确,但仍以(0,0)为中心,然后将数据转换为 xdata[0]ydata[0] 在数据空间中。

在交互使用中,椭圆保持相同的大小,即使轴限制通过缩放进行更改。

fig, ax = plt.subplots()
xdata, ydata = (0.2, 0.7), (0.5, 0.5)
ax.plot(xdata, ydata, "o")
ax.set_xlim((0, 1))

trans = (fig.dpi_scale_trans +
         transforms.ScaledTranslation(xdata[0], ydata[0], ax.transData))

# plot an ellipse around the point that is 150 x 130 points in diameter...
circle = mpatches.Ellipse((0, 0), 150/72, 130/72, angle=40,
                          fill=None, transform=trans)
ax.add_patch(circle)
plt.show()
transforms tutorial

注解

转变的顺序很重要。在这里,椭圆在显示空间中的尺寸是正确的。 第一 然后在数据空间中移动到正确的位置。如果我们做了 ScaledTranslation 首先,然后 xdata[0]ydata[0] 将首先转换为 显示 协调 ([ 358.4  475.2] 在200 dpi监视器上),然后这些坐标将被缩放 fig.dpi_scale_trans 将椭圆中心推离屏幕(即 [ 71680.  95040.]

使用偏移变换创建阴影效果

另一种用途 ScaledTranslation 是创建一个与另一个变换偏移的新变换,例如,放置一个相对于另一个对象偏移一点的对象。通常情况下,您希望偏移在某个物理维度上,例如点或英寸,而不是 data 坐标,以便在不同的缩放级别和dpi设置下移动效果是恒定的。

偏移的一个用途是创建阴影效果,在其中绘制一个与第一个对象相同的对象,刚好在其右侧,并在其下方,调整zorder以确保先绘制阴影,然后再绘制其上方阴影的对象。

这里我们将转换应用于 相反的 命令的使用 ScaledTranslation 上面。绘图首先在数据坐标中绘制 (ax.transData )然后被 dxdy 使用点 fig.dpi_scale_trans . (在印刷术中 point 是1/72英寸,通过指定以点为单位的偏移量,无论保存的dpi分辨率如何,您的图形都将保持不变。)

fig, ax = plt.subplots()

# make a simple sine wave
x = np.arange(0., 2., 0.01)
y = np.sin(2*np.pi*x)
line, = ax.plot(x, y, lw=3, color='blue')

# shift the object over 2 points, and down 2 points
dx, dy = 2/72., -2/72.
offset = transforms.ScaledTranslation(dx, dy, fig.dpi_scale_trans)
shadow_transform = ax.transData + offset

# now plot the same data with our offset transform;
# use the zorder to make sure we are below the line
ax.plot(x, y, lw=3, color='gray',
        transform=shadow_transform,
        zorder=0.5*line.get_zorder())

ax.set_title('creating a shadow effect with an offset transform')
plt.show()
creating a shadow effect with an offset transform

注解

dpi和inches偏移是一个非常常见的用例,我们有一个特殊的助手函数来创建它。 matplotlib.transforms.offset_copy() ,返回带有附加偏移量的新转换。所以上面我们可以这样做:

shadow_transform = transforms.offset_copy(ax.transData,
         fig=fig, dx, dy, units='inches')

改造管道

这个 ax.transData 我们在本教程中一直使用的转换是由三个不同的转换组成的,这些转换构成了 data > 显示 协调。Michael Droettboom实现了转换框架,注意提供一个干净的API,将极坐标图和对数图中发生的非线性投影和比例与平移和缩放时发生的线性仿射转换隔离开来。这里有一个效率,因为您可以在影响仿射变换的轴上平移和缩放,但您可能不需要计算简单导航事件上潜在的昂贵非线性比例或投影。也可以将仿射变换矩阵相乘,然后在一个步骤中将它们应用于坐标。这并不是所有可能的转换都是如此。

这里是如何 ax.transData 实例在基本可分离轴中定义 Axes 班级:

self.transData = self.transScale + (self.transLimits + self.transAxes)

我们被介绍到 transAxes 实例在上面 坐标轴 ,它将轴或子地块边界框的(0、0)、(1、1)角映射到 显示 空间,让我们看看另外两个部分。

self.transLimits 是你的转变 dataaxes 坐标;即,它将视图xlim和ylim映射到轴的单位空间(以及 transAxes 然后利用单位空间显示空间)。我们可以在这里看到它的作用

In [80]: ax = subplot(111)

In [81]: ax.set_xlim(0, 10)
Out[81]: (0, 10)

In [82]: ax.set_ylim(-1, 1)
Out[82]: (-1, 1)

In [84]: ax.transLimits.transform((0, -1))
Out[84]: array([ 0.,  0.])

In [85]: ax.transLimits.transform((10, -1))
Out[85]: array([ 1.,  0.])

In [86]: ax.transLimits.transform((10, 1))
Out[86]: array([ 1.,  1.])

In [87]: ax.transLimits.transform((5, 0))
Out[87]: array([ 0.5,  0.5])

我们可以用同样的反向变换从这个单元开始 axes 坐标返回到 data 协调。

In [90]: inv.transform((0.25, 0.25))
Out[90]: array([ 2.5, -0.5])

最后一件是 self.transScale 属性,负责数据的可选非线性缩放,例如对数轴。当一个轴最初设置时,这只是设置为标识转换,因为基本Matplotlib轴具有线性比例,但是当您调用对数比例函数时 semilogx() 或者显式地将比例设置为对数 set_xscale() 然后 ax.transScale 属性设置为处理非线性投影。尺度变换是 xaxisyaxis Axis 实例。例如,当你打电话 ax.set_xscale('log') ,Xaxis将其比例更新为 matplotlib.scale.LogScale 实例。

对于不可分离轴极轴,还有一个要考虑的部分,投影变换。这个 transData matplotlib.projections.polar.PolarAxes 类似于典型的可分离Matplotlib轴,带有一个附加件 transProjection ::

self.transData = self.transScale + self.transProjection + \
    (self.transProjectionAffine + self.transAxes)

transProjection 处理从空间(例如地图数据的纬度和经度,或极坐标数据的半径和θ)到可分离笛卡尔坐标系的投影。在 matplotlib.projections 包,了解更多信息的最佳方法是打开这些包的源代码,并查看如何创建自己的包,因为Matplotlib支持可扩展的轴和投影。Michael DroettBoom提供了一个创建锤子投影轴的很好的教程示例;请参见 自定义投影 .

脚本的总运行时间: (0分3.555秒)

关键词:matplotlib代码示例,codex,python plot,pyplot Gallery generated by Sphinx-Gallery