在 matplotlib 中剪切注释

trn*_*rnl 5 python matplotlib

有没有办法设置剪辑边界matplotlib.text.Annotation

import matplotlib.pyplot as plt
from matplotlib.transforms import Bbox

fig, ax = plt.subplots()
vals = [
        (10,20,"Lorem ipsum dolor sit amet, consectetur adipiscing elit"),
        (30,20, "Pellentesque scelerisque congue fermentum."),
        (50,10, "Aliquam erat volutpat")
]
ax.set_ylim(0,1)
ax.broken_barh(map(lambda v: v[:2],vals), (0, 1), facecolors=('yellow','red','green'))
for v in vals:
    ax.annotate(
        v[2], 
        xy=(v[0],0.4), 
        clip_on=True, 
        clip_box=Bbox([[v[0],0],[v[0]+v[1],1]])
    )

plt.show()
Run Code Online (Sandbox Code Playgroud)

我尝试过设置clip_box&clip_path通话ax.annotate,但这不起作用。 在此输入图像描述

Joe*_*ton 7

首先,好问题!您正在深入研究细节,因此您需要了解大量半未记录的细节。

您当前的方法可以工作,但有两件事除外:

  1. 预计clip_box位于显示坐标中,因此您需要对其进行转换。
  2. Bbox为此仅使用“普通”是不够的。为了在每次显示更改时重新完成数据坐标的转换,您需要使用TransformedBbox.

为了更多地演示正在发生的事情,让我们解决第一个问题,但不解决第二个问题:

import matplotlib.pyplot as plt
from matplotlib.transforms import Bbox

fig, ax = plt.subplots()
vals = [
        (10,20,"Lorem ipsum dolor sit amet, consectetur adipiscing elit"),
        (30,20, "Pellentesque scelerisque congue fermentum."),
        (50,10, "Aliquam erat volutpat")
]
ax.set_ylim(0,1)
ax.broken_barh(map(lambda v: v[:2],vals), (0, 1), 
              facecolors=('yellow','red','green'))
for v in vals:
    box = Bbox([[v[0],0],[v[0]+v[1],1]]).transformed(ax.transData)
    anno = ax.annotate(v[2], xy=(v[0],0.4), clip_box=box)

plt.show()
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

一开始看起来不错,但还有一个额外的问题:因为它clip_box是静态的并且在显示坐标中,所以一旦您以任何方式更改绘图(例如缩放、平移、自动缩放、更改图形窗口),它就会处于错误的位置大小,更改图形 DPI)。

例如,如果我点击“保存”按钮(或调用savefig),剪辑框将不正确,因为在保存之前图形的 DPI 将被更改(从 80 到 100)!

在此输入图像描述

因此,您需要使用 aTransformedBbox来实现此目的。它只是保存对转换的引用的一个版本,并在转换发生变化时Bbox重新转换底层。matplotlib 中Bbox有一些类似的类:例如,等等。它们并不是您始终需要了解的详细程度,但它们可能非常有用。Transformed*TransformedPath

作为最后一个示例,无论绘图如何缩放/平移/更改,它都会正常工作:

import matplotlib.pyplot as plt
from matplotlib.transforms import TransformedBbox, Bbox

fig, ax = plt.subplots()
vals = [
        (10,20,"Lorem ipsum dolor sit amet, consectetur adipiscing elit"),
        (30,20, "Pellentesque scelerisque congue fermentum."),
        (50,10, "Aliquam erat volutpat")
]
ax.set_ylim(0,1)
ax.broken_barh(map(lambda v: v[:2],vals), (0, 1),
              facecolors=('yellow','red','green'))
for v in vals:
    box = TransformedBbox(Bbox([[v[0],0],[v[0]+v[1],1]]), ax.transData)
    anno = ax.annotate(v[2], xy=(v[0],0.4), clip_box=box)

plt.show()
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述