matplotlib 自动缩放轴以包含注释 [英] matplotlib autoscale axes to include annotations

查看:52
本文介绍了matplotlib 自动缩放轴以包含注释的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Autoscale_view不会这样做,并且ax.relim不会选择注释的位置,因此这似乎不是一个选择.

我试图做类似下面的代码,它遍历所有注释(假设它们在数据坐标中)以获取其范围,然后相应地更新轴,但是理想情况下,我不希望我的注释进入数据坐标(它们与实际数据点有偏移).

xmin, xmax = plt.xlim()ymin,ymax = plt.ylim()#扩展图形以包含标签对于my_labels中的l:#以数据坐标获取框周围的文本bbox = l.get_window_extent(renderer = plt.gcf().canvas.get_renderer())l_xmin, l_ymin, l_xmax, l_ymax = bbox.extentsxmin = 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)

解决方案

我也为此感到困惑.关键是 matplotlib 在实际绘制文本之前不会确定文本的大小. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .因此,您需要显式调用 plt.draw(),然后调整边界,然后再次绘制.

get_window_extent 方法应该根据

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())打印(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))线,= plt.plot(x, y)标签= plt.annotate('完成',(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 ,以便自动缩放功能可以自动将其自身切成整数./p>

我以笔记本格式此处发布了此答案.

Does anyone know of an easy way to expand the plot area to include annotations? I have a figure where some labels are long and/or multiline strings, and rather than clipping these to the axes, I want to expand the axes to include the annotations.

Autoscale_view doesn't do it, and ax.relim doesn't pick up the position of the annotations, so that doesn't seem to be an option.

I've tried to do something like the code below, which loops over all the annotations (assuming they are in data coordinates) to get their extents and then updates the axes accordingly, but ideally I don't want my annotations in data coordinates (they are offset from the actual data points).

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)

解决方案

I struggled with this too. The key point is that matplotlib doesn't determine how big the text is going to be until it has actually drawn it. So you need to explicitly call plt.draw(), then adjust your bounds, and then draw it again.

The get_window_extent method is supposed to give an answer in display coordinates, not data coordinates, per the documentation. But if the canvas hasn't been drawn yet, it seems to respond in whatever coordinate system you specified in the textcoords keyword argument to annotate. That's why your code above works using textcoords='data', but not 'offset points'.

Here's an example:

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.     ])

We want to change the limits so that the text label is within the axes. The value of bbox given isn't much help: since it's in points relative to the labeled point: offset by 12 points in x, a string that evidently will be a little over 30 points long, in 10 point font (-5 to 5 in y). It's nontrivial to figure out how to get from there to a new set of axes bounds.

However, if we call the method again now that we've drawn it, we get a totally different bbox:

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

Now we get

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

This is in display coordinates, which we can transform with ax.transData like we're used to. So to get our labels into the bounds, we can do:

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()

Note it's no longer necessary to explicitly pass plt.gcf().canvas.get_renderer() to get_window_extent after the plot has been drawn once. Also, I'm using update_datalim instead of xlim and ylim directly, so that the autoscaling can notch itself up to a round number automatically.

I posted this answer in notebook format here.

这篇关于matplotlib 自动缩放轴以包含注释的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆