自动调整坐标轴以包含注释

15
有人知道一种简单的方法来扩展绘图区域以包括注释吗?我有一个图形,其中一些标签是长的和/或多行字符串,而不是将它们裁剪到坐标轴上,我想要扩展坐标轴以包括注释。
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)
4个回答

12

我也曾遇到这个问题。关键是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)

plot with annotation clipped

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,以便自动将自动缩放调整为一个圆整数。

我在notebook格式中发布了这个答案这里


3

对我而言,tight_layout 通常可以解决问题,但在某些情况下,我需要使用 subplots_adjust 进行“手动”调整,如下所示:

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

这些数字通常不会有太大变化,所以你可以固定它们而不是尝试从实际图表中计算。

另外,像你在示例中所做的那样设置xlim只会改变你绘制的数据范围,而不会影响标签周围的白色区域。


谢谢。问题在于我有一些跨越多行的注释,它们应该是图上的一个大文本块,所以这不仅仅是稍微太紧的边界的问题... - Tango
subplots_adjust 调整图形、数据点(和注释)的比例。它不会改变重叠在绘图边缘的文本。 - drevicko

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

3
据我所知,不幸的是,tight_layout 似乎也无法捕获注释。 - Tango

0

Matplotlib 不会将注释/文本视为数据。因此,默认的 autoscale 无法正常工作。一个快速的解决方案是手动设置边距。

ax.margin(0.2) 可以增加注释的边距空间。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接