自动缩放轴以包含注释

问题描述 投票:0回答:4

有谁知道扩展绘图区域以包含注释的简单方法?我有一个图形,其中一些标签是长字符串和/或多行字符串,我想扩展轴以包含注释,而不是将它们剪切到轴上。

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)
python matplotlib axis plot-annotations
4个回答
12
投票

我也为此苦苦挣扎。关键是 matplotlib 在实际绘制文本之前无法确定文本有多大。因此,您需要显式调用

plt.draw()
,然后调整边界,然后再次绘制。

根据

文档
get_window_extent方法应该以显示坐标而不是数据坐标给出答案。但是,如果画布尚未绘制,它似乎会响应您在
textcoords
关键字参数中指定的任何坐标系到
annotate
。这就是为什么上面的代码可以使用
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 点字体(-5到 y 中的 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
而不是
xlim
ylim
,这样自动缩放就可以自动将其自身调整为整数。

我以笔记本格式发布了这个答案这里


3
投票

对我来说

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

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

这些数字通常不会发生巨大变化,因此您可以修复它们,而不是尝试根据实际绘图进行计算。

顺便说一句,像在示例中那样设置 xlim 仅更改您绘制的数据范围,而不更改所有标签周围的白色区域。


1
投票

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


0
投票

matplotlib 不将注释/文本视为数据。所以默认的

autoscale
无法正常工作。我的快速解决方案可以手动设置边距。

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

© www.soinside.com 2019 - 2024. All rights reserved.