matplotlib 自动缩放轴以包括注解

d5vmydt9  于 2023-10-24  发布在  其他
关注(0)|答案(4)|浏览(100)

有没有人知道一个简单的方法来扩展绘图区域以包括注解?我有一个图,其中一些标签是长的和/或多行字符串,而不是将这些裁剪到轴上,我想扩展轴以包括注解。
Autoscale_view不执行此操作,ax.relim也不拾取注解的位置,因此这似乎不是一个选项。
我尝试过类似下面的代码,它循环遍历所有注解(假设它们在数据坐标中)以获取它们的范围,然后相应地更新轴,但理想情况下,我不希望我的注解在数据坐标中(它们从实际数据点偏移)。

xmin, xmax = plt.xlim()
ymin, ymax = plt.ylim()
# expand figure to include labels
for l in my_labels:
    # get box surrounding text, in data coordinates
    bbox = l.get_window_extent(renderer=plt.gcf().canvas.get_renderer())
    l_xmin, l_ymin, l_xmax, l_ymax = bbox.extents
    xmin = min(xmin, l_xmin); xmax = max(xmax, l_xmax); ymin = min(ymin, l_ymin); ymax = max(ymax, l_ymax)
plt.xlim(xmin, xmax)
plt.ylim(ymin, ymax)
gev0vcfq

gev0vcfq1#

我也很纠结这个问题,关键是matplotlib在实际绘制文本之前不会确定文本的大小,所以你需要显式调用plt.draw(),然后调整边界,然后再绘制一次。
根据文档,get_window_extent方法应该给予显示坐标的答案,而不是数据坐标。但是如果画布还没有绘制,它似乎会在annotatetextcoords关键字参数中指定的任何坐标系中响应。这就是为什么上面的代码使用textcoords='data'而不是'offset points'工作。
下面是一个示例:

x = np.linspace(0,360,101)
y = np.sin(np.radians(x))

line, = plt.plot(x, y)
label = plt.annotate('finish', (360,0),
                     xytext=(12, 0), textcoords='offset points',
                     ha='left', va='center')

bbox = label.get_window_extent(plt.gcf().canvas.get_renderer())
print(bbox.extents)

array([ 12.     ,  -5.     ,  42.84375,   5.     ])

我们想改变限制,使文本标签在轴内。bbox的值没有多大帮助:因为它是相对于标记点的点:在x轴上偏移12个点,一个长度明显超过30个点的字符串,用10磅字体(y中的-5到5)。弄清楚如何从那里得到一组新的轴边界是很重要的。
然而,如果我们在绘制完这个方法后再次调用它,我们会得到一个完全不同的bbox:

bbox = label.get_window_extent(plt.gcf().canvas.get_renderer())
print(bbox.extents)

现在我们得到

array([ 578.36666667,  216.66666667,  609.21041667,  226.66666667])

这是在显示坐标中,我们可以像以前一样用ax.transData转换。所以为了让我们的标签进入边界,我们可以这样做:

x = np.linspace(0,360,101)
y = np.sin(np.radians(x))

line, = plt.plot(x, y)
label = plt.annotate('finish', (360,0),
                     xytext=(8, 0), textcoords='offset points',
                     ha='left', va='center')

plt.draw()
bbox = label.get_window_extent()

ax = plt.gca()
bbox_data = bbox.transformed(ax.transData.inverted())
ax.update_datalim(bbox_data.corners())
ax.autoscale_view()

请注意,在绘制一次图之后,不再需要显式地将plt.gcf().canvas.get_renderer()传递给get_window_extent。此外,我使用update_datalim而不是直接使用xlimylim,以便自动缩放可以自动将其本身标记为一个整数。
我以笔记本格式here发布了这个答案。

q0qdq0h2

q0qdq0h22#

对我来说,tight_layout通常可以解决这个问题,但在某些情况下,我不得不使用subplots_adjust进行“手动”调整,如下所示:

fig = plt.figure()
fig.subplots_adjust(bottom=0.2, top=0.12, left=0.12, right=0.1)

这些数字通常不会发生显著变化,因此您可以修复它们,而不是尝试从实际图中计算。
顺便说一句,像您在示例中所做的那样设置xlim只会更改您绘制的数据范围,而不会更改所有标签周围的白色区域。

wi3ka0sx

wi3ka0sx3#

matplotlib1.1中引入tight_layout来解决一些布局问题。这里有一个很好的教程。

2w3kk1z5

2w3kk1z54#

matplotlib不把annotation / text当作数据。所以默认的autoscale不能正常工作。我的快速解决方案可以手动设置边距。
ax.margin(0.2)可以增加用于注解的边距空间。

相关问题