matplotlib 标签不同大小的子图与每个子图的角的距离完全相同

matplotlib label subplots of different sizes the exact same distance from corner of each subplot

我正在寻找一种优雅的解决方案,使用 matplotlib 在每个子图的角上方放置图形标签(A、B、C),但每个子图轴角的距离都完全相同。我遇到的问题是典型的标签解决方案利用了坐标轴分数——因此很容易将 A、B、C 放置在相对于每个图的相同位置。

import matplotlib as mpl
import matplotlib.pyplot as plt
fig, ax = plt.subplots(2,2, figsize = (10,10))

texts = ['A', 'B', 'C', 'D']
axes = fig.get_axes()
for a,l in zip(axes, texts):
    a.annotate(l, xy=(-0.1, 1.1), xycoords="axes fraction", fontsize=15, weight = 'bold')
plt.suptitle('Label_Distance Consistent', fontsize = 20)

但是,如果图的大小不同,您将获得距离图的角落不等的标签(由于纵横比)。例如,参见标签 A 和 C。我正在寻找一种好方法来 确保包含多个 sizes/aspect 比率子图的面板的标签与轴角的比例距离,and/or 以明确设置文本特定距离(以英寸为单位或可能图坐标单位)从轴角

过去我在面板中每个子图轴的角上放置了相同大小的方轴,使它们不可见,并将文本缩放到这些,但这是一种复杂的方法。

fig, ax = plt.subplots(2,2, figsize = (10,10))

ax1 = plt.subplot2grid((3, 3), (0, 0), colspan=3)
ax2 = plt.subplot2grid((3, 3), (1, 0), colspan=2)
ax3 = plt.subplot2grid((3, 3), (1, 2), rowspan=2)
ax4 = plt.subplot2grid((3, 3), (2, 0))
ax5 = plt.subplot2grid((3, 3), (2, 1))

axes = fig.get_axes()
texts = ['A', 'B', 'C', 'D', 'E']
for a,l in zip(axes, texts):
    a.annotate(l, xy=(-0.1, 1.1), xycoords="axes fraction", fontsize=15, weight = 'bold')
plt.suptitle('Label Distance Inconsistent', fontsize = 20)

您可以使用 axes pixels 代替 axes fraction 作为参考,并添加单独的高度以补偿参考原点左下角:

为简洁起见,此处仅更改循环中的行,其余代码保持不变:

    a.annotate(l, xy=(-40, 10 + a.bbox.height), xycoords="axes pixels", fontsize=15, weight = 'bold')

简单地添加子图的标题怎么样?

(同样只有循环中要替换的行:)

a.set_title(l, size=15, weight='bold', loc='left')

结果:

您可以获得每个子图的位置并在图形级别添加文本,以便偏移量是图形大小的分数 - 这对所有子图都是相等的:

X = a.get_position().x0
Y = a.get_position().y1    
fig.text(X - .04, Y + .015, l, size=15, weight='bold')

结果:

虽然上述两种解决方案都有效(而且 simple/meet 大多数人需要的更多),但我认为我会 post 拼凑出一个解决方案,让您可以放置​​ text/objects从特定轴点精确偏移 inches/points。这意味着无论图形面板 size/dpi,文本都可以放置在与角的距离相同的位置——如果制作多个不同尺寸的图形并希望它们保持一致(例如出版),这很有用。显然 matplotlib.transforms.offset_copy() 函数是为此目的而设计的,允许指定英寸、点(1/72 英寸)或点作为偏移量。

https://matplotlib.org/examples/pylab_examples/transoffset.html

import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker

fig, ax = plt.subplots(2,2, figsize = (10,7), dpi = 500)

ax1 = plt.subplot2grid((3, 3), (0, 0), colspan=3)
ax2 = plt.subplot2grid((3, 3), (1, 0), colspan=2)
ax3 = plt.subplot2grid((3, 3), (1, 2), rowspan=2)
ax4 = plt.subplot2grid((3, 3), (2, 0))
ax5 = plt.subplot2grid((3, 3), (2, 1))

axes = fig.get_axes()
texts = ['A', 'B', 'C', 'D', 'E']
for a,l in zip(axes, texts):
    a.set_xlim(0,10)
    inv = a.transData.inverted()

    # specify the number of points or inches or dots to transform by
    # places an axes transform here for the axes on which you want to pick an anchor point on
    offsetter = mpl.transforms.offset_copy(a.transAxes, fig=fig, x= -2, y= 6, units = 'points')

    # offset an anchor point - this will return display coordinates 
    corner_offset = offsetter.transform([0,1])

    # convert display coordinate to axes fraction
    axes_frac = inv.transform(corner_offset)
    a.annotate(l, xy=(axes_frac[0],axes_frac[1]), xycoords="axes fraction", fontsize=15, weight = 'bold', color='blue')

或用无花果size/dpi改变