Matplotlib基础(4):绘图坐标系及注释位置控制

绘制图形首先需要明确使用的坐标参考系,通常该操作为默认,但在图形内添加文本、辅助线、箭头、图例等装说明时常用,如可将文本放置在每张图想要的位置如正中间(0.5, 0.5)而不需要关心具体数值。由于自己之前作图遇到过,还增加了一个绘制固定大小正圆的实现方式。

坐标系及转换

基础坐标变换

详细介绍

除了画布的坐标系(NFC, 0-1)fig.transFigure,画布像素坐标系(FC, pixels)fig.dpi_scale_trans,还包括数据本身的坐标系(DC)ax.transData以及标准化的坐标系(NDC, 0-1)ax.transAxes

不同坐标之间的变换,即如何从一个坐标系中的数值转换到对应的目标坐标系的数值。会用到的通常只有DC_to_FC、DC_to_NDC、DC_to_NFC,即从数值转到其它坐标系,使用方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import matplotlib.pyplot as plt
import matplotlib.transforms as transforms
# 函数重命名
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))

fig = plt.figure(figsize=(6, 5), dpi=100)
ax = fig.add_subplot(1, 1, 1)

ax.set_xlim(0, 360)
ax.set_ylim(-1, 1)
print(NFC_to_FC([1, 1]))
# (600,500)
源坐标系 目标坐标系 简写 转换函数
数据x-y 画布pixels DC_to_FC ax.transData.transform
画布pixels 数据x-y FC_to_DC ax.transData.inverted().transform
数据x-y 数据0-1 DC_to_NDC lambda x: FC_to_NDC(DC_to_FC(x))
数据x-y 画布0-1 DC_to_NFC lambda x: FC_to_NFC(DC_to_FC(x))
数据0-1 画布pixels NDC_to_FC ax.transAxes.transform
画布pixels 数据0-1 FC_to_NDC ax.transAxes.inverted().transform
画布0-1 画布pixels NFC_to_FC fig.transFigure.transform
画布pixels 画布0-1 FC_to_NFC fig.transFigure.inverted().transform

案例

下图展示了各种变换方式的应用:

  • transform=ax.transAxes:使用标准化坐标表示位置,即用百分比(0.5,0.5)表示将注释放在正中间
  • transform= transforms.blended_transform_factory(ax.transData, ax.transAxes):X轴使用数据,Y使用百分比确定文本的初始位置,同理transY相反
  • 见画圆时案例-transform=fig.dpi_scale_trans + ScaledTranslation(x0, y0, ax.transData):使用像素的坐标系,但是添加偏移量使得图形从ax的左下角开始绘制
1
2
3
4
5
6
7
8
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.transforms as transforms
import matplotlib.patches as mpatches
from matplotlib.transforms import ScaledTranslation

mpl.rcParams['figure.dpi'] = 120
mpl.rcParams['font.size']=10.5

案例1-文本注释:对应上方前三幅图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# matplotlib是行优先的,也就是索引先数行再列
fig = plt.figure(figsize=(10, 6))
ax = fig.add_subplot(2, 3, 1)
ax.set_xlim(0,5)
ax.set_ylim(0,10)
ax.text(0.05, 0.85, "(0.05, 0.85)", transform=ax.transAxes,
fontsize=8, fontweight='bold', va='top')
ax.scatter(0.05,0.85,s=5,c='r',transform=ax.transAxes)
# *************************************************************
ax = fig.add_subplot(2, 3, 2)
ax.set_xlim(0,5)
ax.set_ylim(0,10)
transX = transforms.blended_transform_factory(
ax.transData, ax.transAxes)
ax.text(3, 0.5, "(3, 0.5)", transform=transX,
fontsize=8, fontweight='bold', va='top')
ax.scatter(3,0.5,s=5,c='r',transform=transX)
# *************************************************************
ax = fig.add_subplot(2, 3, 3)
ax.set_xlim(0,5)
ax.set_ylim(0,10)
transY = transforms.blended_transform_factory(
ax.transAxes, ax.transData)
ax.text(0.5, 4, "(0.5, 4)", transform=transY,
fontsize=8, fontweight='bold', va='top')
ax.scatter(0.5,4,s=5,c='r',transform=transY)

案例2-绘固定的圆形:对应图4 图5,不转换的画绘制圆形会因为XY像素比例不一致而变形,注意圆半径r的单位是英寸,此处画布的大小为10*6英寸

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ax = fig.add_subplot(2, 3, 4)
ax.set_xlim(0,6)
ax.set_ylim(0,10)

circ = mpatches.Circle((0.5, 0.5), radius=0.25, transform=ax.transAxes,
facecolor='blue', alpha=0.75)
ax.add_patch(circ)
ax.scatter(0.5,0.5,s=5,c='r',transform=ax.transAxes)
# *************************************************************
ax = fig.add_subplot(2, 3, 5)
ax.set_xlim(0,6)
ax.set_ylim(0,10)

x0, y0 = 3, 4 # 圆心
offset = ScaledTranslation(x0, y0, ax.transData)

circ = mpatches.Circle((0, 0), radius=0.5, transform=fig.dpi_scale_trans + offset,
facecolor='g', alpha=0.75)
ax.add_patch(circ)
ax.scatter(x0,y0,c='r')

案例3-矩形注释,对应图6

1
2
3
4
5
6
7
8
9
10
11
ax = fig.add_subplot(2, 3, 6)

x = np.random.randn(1000)
ax.hist(x, 50)

transX = transforms.blended_transform_factory(
ax.transData, ax.transAxes)
# 变换X为数据坐标,y为标准化的值,宽度也相应变换
rect = mpatches.Rectangle((-1.5, 0), width=2.5, height=0.8, transform=transX,
color='yellow', alpha=0.7)
ax.add_patch(rect)

图外注释添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.transforms import blended_transform_factory, ScaledTranslation
# 长6英寸
fig = plt.figure(figsize=(6, 4))

ax = fig.add_subplot(1, 1, 1, aspect=1)
ax.set_xlim(0, 10)
ax.set_xticks(range(11))
ax.set_ylim(0, 5)
ax.set_xticks(range(11))

# dy = 3*12/72英寸 ,往外偏移
point = 1 / 72
fontsize = 12
dx, dy = 0, -3 * fontsize * point
offset = ScaledTranslation(dx, dy, fig.dpi_scale_trans)
transform = blended_transform_factory(ax.transData, ax.transAxes + offset)

for x in range(11):
plt.text(x, 0, "↑", transform=transform, ha="center", va="top", fontsize=fontsize)

plt.tight_layout()
plt.show()

公式或辅助线或箭头注释的案例

关于注释的内容比较多,置于后续展开参考

  1. Annotating text with Matplotlib.
  2. ax.annotate,下方有很多案例,如:

极坐标

暂不展开