在matplotlib中注释数据范围

在matplotlib中注释数据范围,matplotlib,Matplotlib,如何对一系列数据进行注释?例如,假设从x=5到x=10的数据大于某个截止值,我如何在图表上指出这一点。如果我是手工注释,我会在范围上方画一个大括号,然后在括号上方写注释 我见过的最接近的方法是使用arrowstyle='和connectionstyle='bar',使两个箭头指向数据的边缘,并用一条线连接它们的尾部。但这并不完全正确;为注释输入的文本将位于其中一个箭头下方,而不是位于条形图上方 以下是我的尝试及其结果: annotate(' ', xy=(1,.5), xycoords='da

如何对一系列数据进行注释?例如,假设从
x=5
x=10
的数据大于某个截止值,我如何在图表上指出这一点。如果我是手工注释,我会在范围上方画一个大括号,然后在括号上方写注释

我见过的最接近的方法是使用
arrowstyle='
connectionstyle='bar'
,使两个箭头指向数据的边缘,并用一条线连接它们的尾部。但这并不完全正确;为注释输入的文本将位于其中一个箭头下方,而不是位于条形图上方

以下是我的尝试及其结果:

annotate(' ', xy=(1,.5),  xycoords='data',
            xytext=(190, .5), textcoords='data',
            arrowprops=dict(arrowstyle="<->",
                            connectionstyle="bar",
                            ec="k",
                            shrinkA=5, shrinkB=5,
                            )
            )
注释(“”,xy=(1.5),xycoords='data',
xytext=(190.5),textcoords='data',
arrowprops=dict(arrowstyle=“”,
connectionstyle=“bar”,
ec=“k”,
收缩A=5,收缩B=5,
)
)


我尝试的解决方案的另一个问题是,注释括号的方形形状并不能真正清楚地表明我高亮显示了一个范围(不像,例如,大括号)。但我想这只是吹毛求疵而已。

您可以将其全部封装在一个函数中:

def add_range_annotation(ax, start, end, txt_str, y_height=.5, txt_kwargs=None, arrow_kwargs=None):
    """
    Adds horizontal arrow annotation with text in the middle

    Parameters
    ----------
    ax : matplotlib.Axes
        The axes to draw to

    start : float
        start of line

    end : float
        end of line

    txt_str : string
        The text to add

    y_height : float
        The height of the line

    txt_kwargs : dict or None
        Extra kwargs to pass to the text

    arrow_kwargs : dict or None
        Extra kwargs to pass to the annotate

    Returns
    -------
    tuple
        (annotation, text)
    """

    if txt_kwargs is None:
        txt_kwargs = {}
    if arrow_kwargs is None:
        # default to your arrowprops
        arrow_kwargs = {'arrowprops':dict(arrowstyle="<->",
                            connectionstyle="bar",
                            ec="k",
                            shrinkA=5, shrinkB=5,
                            )}

    trans = ax.get_xaxis_transform()

    ann = ax.annotate('', xy=(start, y_height),
                        xytext=(end, y_height),
                        transform=trans,
                        **arrow_kwargs)
    txt = ax.text((start + end) / 2,
                  y_height + .05,
                  txt_str,
                  **txt_kwargs)


    if plt.isinteractive():
        plt.draw()
    return ann, txt
如中所述,可以使用sigmoidal函数构造花括号。下面是一个函数,它在x轴的正上方添加了花括号。只要图形的宽度和高度不发生变化,无论轴的限制如何,它生成的花括号看起来应该是相同的

import numpy as np
import matplotlib.pyplot as plt

def draw_brace(ax, xspan, text):
    """Draws an annotated brace on the axes."""
    xmin, xmax = xspan
    xspan = xmax - xmin
    ax_xmin, ax_xmax = ax.get_xlim()
    xax_span = ax_xmax - ax_xmin
    ymin, ymax = ax.get_ylim()
    yspan = ymax - ymin
    resolution = int(xspan/xax_span*100)*2+1 # guaranteed uneven
    beta = 300./xax_span # the higher this is, the smaller the radius

    x = np.linspace(xmin, xmax, resolution)
    x_half = x[:resolution//2+1]
    y_half_brace = (1/(1.+np.exp(-beta*(x_half-x_half[0])))
                    + 1/(1.+np.exp(-beta*(x_half-x_half[-1]))))
    y = np.concatenate((y_half_brace, y_half_brace[-2::-1]))
    y = ymin + (.05*y - .01)*yspan # adjust vertical position

    ax.autoscale(False)
    ax.plot(x, y, color='black', lw=1)

    ax.text((xmax+xmin)/2., ymin+.07*yspan, text, ha='center', va='bottom')

ax = plt.gca()
ax.plot(range(10))
draw_brace(ax, (0, 8), 'large brace')
draw_brace(ax, (8, 9), 'small brace')
输出:

我修改为允许更改支架的垂直位置:

def draw_brace(ax, xspan, yy, text):
    """Draws an annotated brace on the axes."""
    xmin, xmax = xspan
    xspan = xmax - xmin
    ax_xmin, ax_xmax = ax.get_xlim()
    xax_span = ax_xmax - ax_xmin

    ymin, ymax = ax.get_ylim()
    yspan = ymax - ymin
    resolution = int(xspan/xax_span*100)*2+1 # guaranteed uneven
    beta = 300./xax_span # the higher this is, the smaller the radius

    x = np.linspace(xmin, xmax, resolution)
    x_half = x[:int(resolution/2)+1]
    y_half_brace = (1/(1.+np.exp(-beta*(x_half-x_half[0])))
                    + 1/(1.+np.exp(-beta*(x_half-x_half[-1]))))
    y = np.concatenate((y_half_brace, y_half_brace[-2::-1]))
    y = yy + (.05*y - .01)*yspan # adjust vertical position

    ax.autoscale(False)
    ax.plot(x, y, color='black', lw=1)

    ax.text((xmax+xmin)/2., yy+.07*yspan, text, ha='center', va='bottom')
输出:

还要注意的是,在

应该是

x_half = x[:int(resolution/2)+1]
否则,脚本尝试在此处用作索引的数字是一个浮点数

最后,请注意,如果您将大括号移出边界,它将不会显示。您需要添加参数
clip\u on=False
,如下所示:

ax.plot(x, y, color='black', lw=1, clip_on=False)

使用两个注释,一个带文本但不带箭头,另一个带箭头但不带文本。另请参见
axvspan
最好显示您尝试过的内容(使用代码片段)。@tcaswell我曾考虑过使用两个注释,但这需要手动定位文本,如果范围移动,则必须手动更新这两个注释。似乎存在一个更优的解决方案将是一个足够普遍的问题。这不是一个足够普遍的操作,无法得到自己的方法?我不知道现有的方法,但我只写了一个;)如果你能提供一些关于使用这个东西的反馈,我建议你把它作为一个内置程序添加到库中。它们都是很好的选择,谢谢分享。您提供的函数在大多数情况下已经足够了,但如果您正在寻找改进方法,我可以想出一些建议。以下是一些建议:-允许任意旋转(起点和终点都有自己的xy坐标),除方括号外的其他形状。我尝试通过更改
连接样式来实现这一点,但没有成功。可以通过使用两个箭头而不是一个连接的箭头来完成。-默认情况下,文本居中-可能扩展
AxesSubplot
类,而不是使用函数;这将允许它更像
annotate
方法——无需通过轴。同样,您的方法已经适用于大多数情况;这些只是糖衣。我不知道如何在评论中列出一个列表,对不起。很好!我注意到一件事:
ax.autoscale(False)
应该在
draw\u brace
之外调用一次。
x_half = x[:resolution/2+1]
x_half = x[:int(resolution/2)+1]
ax.plot(x, y, color='black', lw=1, clip_on=False)