【问题标题】:matplotlib autoscale axes to include annotationsmatplotlib 自动缩放轴以包含注释
【发布时间】:2012-07-17 16:57:04
【问题描述】:

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

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 axes


    【解决方案1】:

    我也为此苦苦挣扎。关键是 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)
    

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

    我们想要更改限制,使文本标签位于轴内。给定的bbox 的值没有多大帮助:因为它是相对于标记点的点数:在 x 中偏移 12 个点,一个字符串显然会长 30 点多一点,字体为 10 点(-5到 5 in 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()
    

    请注意,在绘制一次绘图后,不再需要将plt.gcf().canvas.get_renderer() 显式传递给get_window_extent。另外,我直接使用update_datalim 而不是xlim 和ylim,这样自动缩放就可以自动调整到整数。

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

    【讨论】:

      【解决方案2】:

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

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

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

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

      【讨论】:

      • 谢谢。问题是我有一些注释跨越了很多行,并且应该是图中的一大块文本,所以这不仅仅是边界稍微太紧的问题......
      【解决方案3】:

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

      【讨论】:

      • 不幸的是,tight_layout 也不接受注释,据我所知。
      猜你喜欢
      • 2020-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-10
      • 1970-01-01
      • 1970-01-01
      • 2021-06-22
      • 1970-01-01
      相关资源
      最近更新 更多