在matplotlib中标注数据范围

jmp7cifd  于 2022-11-30  发布在  其他
关注(0)|答案(5)|浏览(164)

如何注解数据范围?例如,假设从x = 5x = 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,
                            )
            )

我尝试的解决方案的另一个问题是,方括号的形状并没有真正清楚地表明我正在突出显示一个范围(不像花括号),但我想这只是在这一点上吹毛求疵。

ekqde3dh

ekqde3dh1#

this answer中所述,您可以使用S形函数来构造花括号。下面是一个在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')

输出量:

kiayqfof

kiayqfof2#

我修改了Joooeey's answer以允许更改大括号的垂直位置:
第一个
输出量:

另请注意,在Joooeey's answer中,行

x_half = x[:resolution/2+1]

应该是

x_half = x[:int(resolution/2)+1]

否则,脚本尝试在此处用作索引的数字是浮点数。
最后,请注意,现在如果将大括号移出边界,它将不会显示出来。

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

oalqel3c3#

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

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

可替换地,

start, end = .6, .8
ax.axvspan(start, end, alpha=.2, color='r')
trans = ax.get_xaxis_transform()
ax.text((start + end) / 2, .5, 'test', transform=trans)
tp5buhyn

tp5buhyn4#

这里是对guzeyjooeey's答案的一个小修改,以绘制轴外的花括号。

def draw_brace(ax, xspan, yy, text):
"""Draws an annotated brace outside 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, clip_on=False)

    ax.text((xmax+xmin)/2., -yy-.17*yspan, text, ha='center', va='bottom')

    
# Sample code
fmax = 1
fstart = -100
fend = 0
frise = 50
ffall = 20

def S(x):
   if x<=0:
       return 0
   elif x>=1:
       return 1
   else:
       return 1/(1+np.exp((1/(x-1))+(1/x)))

x = np.linspace(700,1000,500)
lam = [fmax*(S((i-880)/60)-S(((i-1000)/25)+1)) for i in x]
fig = plt.figure(1)
ax = fig.add_subplot(111)
plt.plot(x,lam)
plt.xlim([850,1000])
ax.set_aspect(50,adjustable='box')
plt.ylabel('$\lambda$')
plt.xlabel('$x$')
ax.xaxis.set_label_coords(0.5, -0.35)
draw_brace(ax, (900,950),0.2, 'rise')
draw_brace(ax, (980,1000),0.2, 'fall')
plt.text(822,0.95,'$(\lambda_{\mathrm{max}})$')

Sample output

bwleehnv

bwleehnv5#

对@ Jooooeey和@guezy的draw_brace进行了小修改,以便也将大括号颠倒
+参数颠倒

def draw_brace(ax, xspan, yy, text, upsidedown=False):
    """Draws an annotated brace on the axes."""
    # shamelessly copied from https://stackoverflow.com/questions/18386210/annotating-ranges-of-data-in-matplotlib
    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]))))
    if upsidedown:
        y = np.concatenate((y_half_brace[-2::-1], y_half_brace))
    else:
        y = np.concatenate((y_half_brace, y_half_brace[-2::-1]))
    y = yy + (.05*y - .01)*yspan # adjust vertical position

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

    if upsidedown:
        text = ax.text((xmax+xmin)/2., yy+-.07*yspan, text, ha='center', va='bottom',fontsize=7)
    else:
        text = ax.text((xmax+xmin)/2., yy+.07*yspan, text, ha='center', va='bottom',fontsize=7)
    return line, text

相关问题