Matplotlib is hiring a Research Software Engineering Fellow! See discourse for details. Apply by January 3, 2020
与任何图形包一样,matplotlib构建在转换框架之上,以便在坐标系(即用户区)之间轻松移动。 data
坐标系 axes
坐标系 figure
坐标系,以及 display
坐标系。在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() |
混合坐标系;在一个轴上使用数据坐标,在另一个轴上使用轴坐标。 |
上表中的所有转换对象在其坐标系中接受输入,并将输入转换为 display
坐标系。这就是为什么 display
坐标系有 None
对于 Transformation Object
列--它已经在显示坐标中。转换还知道如何反转自己,从 display
回到本机坐标系。当处理用户界面中的事件(通常发生在显示空间中)并且您想知道鼠标单击或按键在数据坐标系中的位置时,这尤其有用。
请注意,在中指定对象 display
坐标将改变其位置,如果 dpi
数字的变化。当打印或更改屏幕分辨率时,这可能会导致混淆,因为对象可以更改位置和大小。因此,对于放置在轴或图形中的艺术家来说,将其变换设置为某个对象是最常见的。 其他 比 IdentityTransform()
;将艺术家放置在轴上时的默认值,使用 add_artist
是为了改变 ax.transData
.
让我们从最常用的坐标开始, data
坐标系。每当向轴添加数据时,matplotlib都会更新数据限制,最常见的更新方式是 set_xlim()
和 set_ylim()
方法。例如,在下图中,数据限制在X轴上从0延伸到10,在Y轴上从-1延伸到1。
你可以使用 ax.transData
要从中转换的实例 data
对你 display
坐标系,单点或点序列,如下所示:
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()
方法创建将从显示坐标转换为数据坐标的转换:
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_point((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()
注解
如果在GUI后端中运行上述示例中的源代码,还可能发现 data
和 display
注释没有指向完全相同的点。这是因为显示点是在图形显示之前计算的,并且在创建图形时,GUI后端可能会稍微调整图形的大小。如果您自己调整图形的大小,效果会更明显。这是您很少希望在显示空间中工作的一个很好的原因,但是您可以连接到 '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()
您也可以在轴坐标系中制作线或面片,但在我的经验中,这比使用 ax.transAxes
用于放置文本。尽管如此,这里还是有一个愚蠢的例子,它在 data
空间,并覆盖半透明 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()
并入 blended
混合的坐标空间 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()
注解
x在数据坐标系中,y在轴坐标系中的混合转换非常有用,因此我们有助手方法来返回MPL内部用于绘制刻度线、刻度线标签等的版本。 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-units
circ = mpatches.Circle((2.5, 2), 1.0, transform=fig.dpi_scale_trans,
facecolor='blue', alpha=0.75)
ax.add_patch(circ)
plt.show()
如果我们改变图形大小,圆就不会改变它的绝对位置,而是被裁剪。
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-units
circ = mpatches.Circle((2.5, 2), 1.0, transform=fig.dpi_scale_trans,
facecolor='blue', alpha=0.75)
ax.add_patch(circ)
plt.show()
另一个用途是在轴上的数据点周围放置具有设定物理尺寸的补丁。这里我们把两个变换加在一起。第一个设置椭圆大小的缩放比例,第二个设置椭圆的位置。然后将椭圆放置在原点,然后使用辅助变换 ScaledTranslation
把它移到正确的地方 ax.transData
坐标系。此帮助程序实例化为:
trans = ScaledTranslation(xt, yt, scale_trans)
在哪里? xt
和 yt
是平移偏移量,以及 scale_trans
是一种可扩展的转换 xt
和 yt
在应用偏移之前的转换时间。
注意在下面的转换中使用加号运算符。这段代码说:首先应用比例转换 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()
注解
转变的顺序很重要。在这里,椭圆在显示空间中的尺寸是正确的。 第一 然后在数据空间中移动到正确的位置。如果我们做了 ScaledTranslation
首先,然后 xdata[0]
和 ydata[0]
将首先转换为 display
协调 ([ 358.4 475.2]
在200 dpi监视器上),然后这些坐标将被缩放 fig.dpi_scale_trans
将椭圆中心推离屏幕(即 [ 71680. 95040.]
)
另一种用途 ScaledTranslation
是创建一个与另一个转换偏移的新转换,例如,将一个对象相对于另一个对象移动一点。通常,您希望移位在一些物理维度中,如点或英寸,而不是在数据坐标中,这样移位效果在不同的缩放级别和dpi设置下是恒定的。
偏移的一个用途是创建阴影效果,在其中绘制一个与第一个对象相同的对象,刚好在其右侧,并在其下方,调整zorder以确保先绘制阴影,然后再绘制其上方阴影的对象。
这里我们将转换应用于 相反的 命令的使用 ScaledTranslation
上面。该图首先以数据单位绘制。 (ax.transData
)然后被 dx
和 dy
使用点 fig.dpi_scale_trans
. (In typography, a`point <https://en.wikipedia.org/wiki/Point_%28typography%29>`_ 是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()
注解
dpi和inches偏移是一个非常常见的用例,我们有一个特殊的助手函数来创建它。 matplotlib.transforms.offset_copy()
,返回带有附加偏移量的新转换。所以上面我们可以这样做:
shadow_transform = transforms.offset_copy(ax.transData,
fig=fig, dx, dy, units='inches')
这个 ax.transData
我们在本教程中一直使用的转换是由三个不同的转换组成的,这些转换构成了 data
> display
协调。Michael Droettboom实现了转换框架,注意提供一个干净的API,将极坐标图和对数图中发生的非线性投影和比例与平移和缩放时发生的线性仿射转换隔离开来。这里有一个效率,因为您可以在影响仿射变换的轴上平移和缩放,但您可能不需要计算简单导航事件上潜在的昂贵非线性比例或投影。也可以将仿射变换矩阵相乘,然后在一个步骤中将它们应用于坐标。这并不是所有可能的转换都是如此。
这里是如何 ax.transData
实例在基本可分离轴中定义 Axes
班级:
self.transData = self.transScale + (self.transLimits + self.transAxes)
我们被介绍到 transAxes
实例在上面 坐标轴 ,它将轴或子地块边界框的(0、0)、(1、1)角映射到 display
空间,让我们看看另外两个部分。
self.transLimits
是你的转变 data
到 axes
坐标;即,它将视图xlim和ylim映射到轴的单位空间(以及