matplotlib自动缩放轴包含注释

时间:2012-07-18 15:37:12

标签: python matplotlib axes

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

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)

3 个答案:

答案 0 :(得分:3)

我也在努力解决这个问题。关键点在于,matplotlib在确定文本实际绘制之前不会确定文本的大小。因此,您需要显式调用plt.draw(),然后调整边界,然后再次绘制它。

get_window_extent方法应该根据documentation给出显示坐标的答案,而不是数据坐标。但是如果尚未绘制画布,它似乎会在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)

plot with annotation clipped

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

我们想要更改限制,以便文本标签位于轴内。 bbox给出的值没有多大帮助:因为它相对于标记点的点数:在x中偏移12个点,显然是一个长度超过30个点的字符串,10点字体(-5到5 y)。如何从那里获得一组新的轴界限是非常重要的。

但是,如果我们现在再次调用该方法,我们就会得到一个完全不同的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()

fixed plot

请注意,在绘制一次绘图后,不再需要将plt.gcf().canvas.get_renderer()显式传递给get_window_extent。另外,我直接使用update_datalim代替xlimylim,因此自动缩放可以自动将自身划分为一个整数。

我用笔记本格式here发布了这个答案。

答案 1 :(得分:2)

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

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

数字通常不会发生显着变化,因此您可以修复它们,而不是尝试从实际情节中进行计算。

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

答案 2 :(得分:1)

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