大家好,我是云朵君!
下表总结了 matplotlib 绘图常用的坐标系统。
在 Transformation Object 列中,ax
是一个 Axes 实例,fig
是一个 Figure 实例。
Coordinates | Transformation | object Description |
---|---|---|
"data" | ax.transData | Data 坐标系,由 xlim 和 ylim 控制。 |
"axes" | ax.transAxes | Axes 坐标系,(0, 0) 是 axes 的左下角, (1, 1) 是 axes 的右上角。 |
"figure" | fig.transFigure | Figure 坐标系, (0, 0) 是 figure 的左下角, (1, 1) 是 figure 的右上角。这是相对数,包括对象在水平和垂直方向上的尺寸也是相对于 Figure 的宽和高的。 |
"figure-inches" | fig.dpi_scale_trans | Figure 以 inches 为单位;(0, 0) 是 figure 的左下角, (width, height) 是 figure 的右上角。这是绝对数,包括对象在水平和垂直方向上的尺寸也是绝对值,以 inches 为单位。 |
"display" | None, or IdentityTransform() | 显示窗口的 pixel 坐标系统;(0, 0) 是显示窗口的左下角, (width, height) 是显示窗口的右上角,以 pixels 为单位。这是绝对数,包括对象在水平和垂直方向上的尺寸也是绝对值,但是以 pixels 为单位。对象的物理尺寸(以 inches 为单位)还与 dpi 有关。 |
"xaxis", “yaxis" | ax.get_xaxis_transform(), ax.get_yaxis_transform() | 混合坐标系;在一个 axis 上使用 data 坐标,在另一个上使用 axes 坐标系。 |
上表有三列:
Coordinates
,坐标系统的标识符(名称);Transformation object
, 是坐标转换对象,用来将坐标系统中的坐标转换为 display coordinate 系统的坐标;Description
,坐标系统的描述(第一列标识的坐标系统是如何定义的)。
在 matplotlib 中,创建对象时,首先就要告诉 matplotlib 这个对象放在哪个位置,即提供一个坐标参数 (x, y)。由于 matplotlib 有多个坐标系统,所以你还需要告诉 matplotlib 这个坐标值是哪个坐标系统的坐标。在 matplotlib 中,默认是 data 坐标。
五个常用的坐标系统
Data坐标系统
指定 artist 的坐标、尺寸是 data 坐标系统的方法是将 ax.transData 实例传递给 tranform 参数。 matplotlib 默认为 data 坐标系。
Data 坐标系,由 xlim 和 ylim 控制。即提供的坐标值 (x,y)、size 值
,在 xaxis,yaxis 方向上都是相对于 xlim,ylim 的。向坐标轴添加数据,Matplotlib 都会自动更新数据界限。也可以使用 set_xlim()
和 set_ylim()
方法,强制设置数据界限。
使用ax.transData
实例将数据变换为显示坐标系。虽然两个箭头在两个不同的坐标系,但指的同一个地方。
xdisplay, ydisplay = ax.transData.transform((xdata, ydata))
ax.annotate('data = (%.1f, %.1f)'%(xdata, ydata),
(xdata, ydata), xytext=(-2*offset, offset),
textcoords='offset points',
bbox=bbox, arrowprops=arrowprops)
ax.annotate('display = (%.1f, %.1f)'%(xdisplay, ydisplay),
(xdisplay, ydisplay), xytext=(2*offset, -offset),
xycoords='figure pixels',
textcoords='offset points',
bbox=bbox, arrowprops=arrowprops)
Axes坐标系统
除了Data坐标系,Axes是第二有用的坐标系。
transform = ax.transAxes 指定为 axes 坐标系。 点 (0,0)
是 axes 或 subplot 的左下角,(0.5,0.5)
是中心,(1.0,1.0)
是右上角。还可以引用上述范围之外的点,因此(-0.1,1.1)
位于坐标轴的左侧和上方。
当将文本放置在axes中时,这个坐标系非常有用,因为我们通常希望在固定的位置 (例如axes窗格的左上角) 中有一个文本气泡,并且在平移或缩放时该位置保持不变。
下面是在学术期刊中经常看到的一个简单的例子,它创建了四个 axes,并将它们标记为"A"、"B"、"C" 和 "D"
:
# ax.transAxes 将整个 axes 看成一个([0,0],[1.0,1.0])的矩形;
# transform = ax.transAxes 就是将点 (0.05,0.95) 看成 ax 矩形区域中的点
# 添加数据,改变了 xlim, ylim, 但不会影响 ax.transAxes 的行为
for i, label in enumerate(('A', 'B', 'C', 'D')):
Y = curve()
X = np.linspace(-3, 3, len(Y))
ax = fig.add_subplot(2, 2, i 1)
ax.fill_between(X, 3*Y, color=color[i])
ax.plot(X, 3 * Y, color="k", linewidth=0.75)
ax.text(0.05, 0.95, label, transform=ax.transAxes,
fontsize=16, fontweight='bold', va='top')
plt.show()
Figure坐标系统
transform = fig.transFigure 指定为 figure坐标系, (0, 0)
是 figure 的左下角, (1, 1)
是 figure 的右上角。
这是相对数,包括对象在水平和垂直方向上的尺寸也是相对于 Figure 的宽和高的,这一点与 axes 非常相似。
下面的例子,在 fig.transFigure
坐标系统中绘制了 Circele, Ellipse, Rectangle
,它们的坐标和尺寸不受 axes,data 的影响。
import matplotlib.patches as patches
circ = patches.Circle((0.5,0.5),0.3, color='r',
fill=False,linewidth=5, transform=fig.transFigure)
ell = patches.Ellipse((0.5,0.5),0.3,0.3, color='g',
hatch='*',transform=fig.transFigure,fill=False)
rect = patches.Rectangle((0.5,0.5),0.3,0.3,color='grey',
hatch='/',transform=fig.transFigure, fill=False)
Figure-inches 坐标系
使用了 figure-inches 坐标系统:
fig.dpi_scale_transFigure
以 inches 为单位,(0, 0)
是 figure 的左下角,(width, height)
是 figure 的右上角。
这是绝对数,即物理尺寸,包括对象在水平和垂直方向上的尺寸也是绝对值,以 inches 为单位。
下面的例子中,3 个 pathces 的定义代码与前面的区别仅仅是 transfor = transform = fig.dpi_scale_trans
,注意:它们的位置和尺寸变化(图的左下角)。
如果使用plt.savefig()
保存图片,超过Figure以外的部分将会丢失。
import matplotlib.patches as patches
import matplotlib.transforms as transforms
fig = plt.figure(figsize=(8, 4))
ax = fig.add_subplot(1, 1, 1)
ax.plot(X2, 3 * Y2, color="k", linewidth=0.75)
ax.fill_between(X2, 3*Y2, color=cmap(0.6))
circ1 = patches.Circle((1,1), 0.3, color='r',
fill=False, transform=fig.transFigure)
circ2 = patches.Circle((1,1), 0.3, color='g',
fill=False, transform=ax.transAxes)
circ3 = patches.Circle((1,1), 0.3, color='b',
fill=False, transform=fig.dpi_scale_trans)
fig.add_artist(circ1)
混合坐标系
混合坐标系:在一个 axis 上使用 data 坐标,在另一个上使用 axes 坐标系。
在混合 axes 和 data 坐标系的 blended 混合坐标系统中绘图非常有用,例如,创建一个水平跨距突出显示 y 数据的某些区域,但在 x-axis
轴上的跨距不受 x 数据的限制,移动和缩放等的影响。
通常使用下面的工厂函数创建混合坐标系的 Transformation object
对象:
matplotlib.transforms.blended_transform_factory(x_transform, y_transform)
创建一个 "blended" 转换,用 x_transform 转换 x-axis,用 y_transform 转换 y-axis。 两个子变换都是 affine (仿射几何) 变换,快速返回混合变换。
坐标系统变换
以Data坐标系和Figure坐标系为例,具体看看在一张图中的哪些位置。
- FC:figure坐标(pixels像素)
- NFC:归一化figure坐标(0→1)
- DC:data坐标(数据单元)
- NDC:归一化data坐标(0→1)
Data坐标系和Figure坐标系分别有两个归一化的坐标系,其中归一化data坐标即为上节介绍的axes坐标系。
他们之间有如下转换方法:
代码语言:javascript复制DC_to_FC = ax.transData.transform
FC_to_DC = ax.transData.inverted().transform
NDC_to_FC = ax.transAxes.transform
FC_to_NDC = ax.transAxes.inverted().transform
NFC_to_FC = fig.transFigure.transform
FC_to_NFC = fig.transFigure.inverted().transform
DC_to_NDC = lambda x: FC_to_NDC(DC_to_FC(x))
NDC_to_DC = lambda x: FC_to_DC(NDC_to_FC(x))
然而,大多数时候,我们不需要显式地使用这些转换函数,而需要隐式使用。例如,考虑到我们希望在特定的图表上添加一些文本。则需要使用文本功能,并指定要显示文本。matplotlib是如何选择这些坐标?它们用数据坐标表示、归一化数据坐标、归一化图坐标?
其实,默认情况下,它们是用数据坐标表示的。因此,如果我们想在一个不同的坐标系统上添加元素,则需要在调用函数时显式地指定一个转换。例如,我们想在右下角添加一个字母。我们可以写:
代码语言:javascript复制fig = plt.figure(figsize=(6, 5), dpi=100)
ax = fig.add_subplot(1, 1, 1)
ax.text(0.1, 0.1, "A", transform=ax.transAxes)
我们回到在学术期刊中看到的例子中,需要在不同尺度的数据坐标系中的相同位置添加两个文字。如果两个轴刻度具有相同的物理大小(像素),我们使用数据坐标系放置字母,此时字母将会在与右边和底部轴等距的位置上。但如果刻度不同呢,则需要制定使用上述的调用函数时显式地指定一个转换。
则如果同时需要将两个字母放置不同的位置,并且规定了他们之间的偏移量,此时需要指定一种转换,规范化数据坐标(0,0)与以数字原生单位(像素)表示的偏移量的组合。 为此,我们需要构建自己的变换函数来计算偏移量:
代码语言:javascript复制from matplotlib.transforms import ScaleTranslation
fig = plt.figure(figsize=(6, 4))
ax = fig.add_subplot(2, 1, 1)
plt.text(0.1, 0.1, "A", transform=ax.transAxes)
ax = fig.add_subplot(2, 1, 2)
dx, dy = 10/fig.dpi, 10/fig.dpi
# 以数字原生单位(像素)
offset = ScaledTranslation(dx, dy, fig.dpi_scale_trans)
# 规范化数据坐标ax.transAxes
plt.text(0, 0, "B", transform=ax.transAxes offset)
plt.show()
这里使用了ScaledTranslation 对位移量进行伸缩控制的位移变换。
代码语言:javascript复制ScaledTranslation(xt, yt, scale_trans, **kwargs)
参数:
xt, yt
: x / y方向的位移量scale_trans
:位移的伸缩比例
另外,如果我们需要在X轴和Y轴上进行不同的变换时,事情可能会变得更加复杂。我们考虑一下这样的情况:想在X标记的标签下面添加一些文本。标记的X位置是用数据坐标表示的,但是我们如何像下图所示那样将某些东西放在下面呢?
文本的自然单位是点,因此我们希望使用以点表示的Y偏移量来确定『关注*数据STUDIO』
的位置。为此,我们需要使用混合变换:
from matplotlib.transforms import blended_transform_factory, ScaledTranslation
point = 1 / 72
fontsize = 12
dx, dy = 0, -2 * fontsize * point
offset = ScaledTranslation(dx, dy,
fig.dpi_scale_trans)
transform = blended_transform_factory(
ax.transData, ax.transAxes offset)
s = list("关注*数据STUDIO")
for x in range(11):
plt.text(x, 0, s[x],
transform=transform,
ha="center", va="top",
fontsize=fontsize)
应用案例
可变的二维仿射变换
代码语言:javascript复制matplotlib.transforms.Affine2D(
matrix=None, **kwargs)
准备仿射变换 (Affine2D
) data transform 一个图像的形状允许操纵图像的形状和方向。
skew(xShear, yShear)
在适当位置添加倾斜。 X剪 和 YS剪力 剪切角是沿着 x -和 y -轴,分别以弧度表示。skew_deg(xShear, yShear)
在适当位置添加倾斜。 X剪 和 YS剪力 剪切角是沿着 x -和 y -轴,分别以度为单位。rotate(theta)
将旋转(以弧度)添加到此转换中。rotate_around(x, y, theta)
添加围绕点(x,y)的旋转(以弧度为单位)。rotate_deg(degrees)
将旋转(以度为单位)添加到此转换中。rotate_deg_around(x, y, degrees)
在适当位置围绕点(X,Y)添加旋转(以度为单位)。scale(sx, sy=None)
在适当位置添加比例。 如果 sy 为“无”,在两个 x -和 y -方向。
import matplotlib.transforms as mtransforms
mtransforms.Affine2D().rotate_deg(30)
mtransforms.Affine2D().skew_deg(30, 15)
mtransforms.Affine2D().scale(-1, .5)
mtransforms.Affine2D().rotate_deg(30)
.skew_deg(30, 15).scale(-1, .5)
.translate(.5, -1)
参考资料
[1]
Scientific Visualisation-Python & Matplotlib
[2]
https://matplotlib.org/2.0.2/users/transforms_tutorial.html
[3]
https://blog.csdn.net/sinat_32570141/article/details/113048947