Plotly: как установить цвет заливки между двумя вертикальными линиями?

Aug 20 2020

Используя matplotlib, мы можем «тривиально» заполнить область между двумя вертикальными линиями, fill_between()как в примере:

https://matplotlib.org/3.2.1/gallery/lines_bars_and_markers/fill_between_demo.html#selectively-marking-horizontal-regions-across-the-whole-axes

Используя matplotlib, я могу сделать то, что мне нужно:

У нас есть два сигнала, и я вычисляю корреляцию катящегося / движущегося Пирсона и Спирмена. Когда корреляция опускается ниже -0,5 или выше 0,5, я хочу заштриховать период (синий для Пирсона и оранжевый для Спирмена). Я также затемняю выходные серым цветом на всех участках.

Однако мне трудно сделать то же самое с помощью Plotly. И также будет полезно знать, как это сделать между двумя горизонтальными линиями.

Обратите внимание, что я использую Plotly и Dash для ускорения визуализации нескольких графиков. Пользователи просили более «динамичный тип вещей». Однако я не специалист по графическому интерфейсу и не могу тратить на это время, хотя мне нужно кормить их начальными результатами.

Кстати, я пробовал Боке в прошлом и отказался по какой-то причине, которую не могу вспомнить. Plotly выглядит неплохо, поскольку я могу использовать Python или R, которые являются моими основными инструментами разработки.

Благодаря,

Карлос

Ответы

4 DerekO Aug 20 2020 at 04:35

Я не думаю, что существует какой-либо встроенный метод Plotly, эквивалентный методу matplotlib fill_between(). Однако вы можете рисовать фигуры, поэтому возможный обходной путь - нарисовать серый прямоугольник и установить параметр layer="below"так, чтобы сигнал все еще был виден. Вы также можете установить координаты прямоугольника за пределами диапазона оси, чтобы прямоугольник доходил до краев графика.

Вы можете заполнить область между горизонтальными линиями, нарисовав прямоугольник и аналогичным образом установив диапазоны осей.

import numpy as np
import plotly.graph_objects as go

x = np.arange(0, 4 * np.pi, 0.01)
y = np.sin(x)

fig = go.Figure()
fig.add_trace(go.Scatter(
    x=x,
    y=y
))

# hard-code the axes
fig.update_xaxes(range=[0, 4 * np.pi])
fig.update_yaxes(range=[-1.2, 1.2])

# specify the corners of the rectangles
fig.update_layout(
    shapes=[
    dict(
        type="rect",
        xref="x",
        yref="y",
        x0="4",
        y0="-1.3",
        x1="5",
        y1="1.3",
        fillcolor="lightgray",
        opacity=0.4,
        line_width=0,
        layer="below"
    ),
    dict(
        type="rect",
        xref="x",
        yref="y",
        x0="9",
        y0="-1.3",
        x1="10",
        y1="1.3",
        fillcolor="lightgray",
        opacity=0.4,
        line_width=0,
        layer="below"
    ),
    ]
)

fig.show()

4 vestland Aug 20 2020 at 05:53

Вы не предоставили образец данных, поэтому я собираюсь использовать синтетический временной ряд, чтобы показать вам, как можно добавить несколько фигур с определенными датами начала и окончания для нескольких разных категорий, используя настраиваемую функцию. bgLevel


Две вертикальные линии с заливкой между ними очень быстро превращаются в прямоугольник. А прямоугольники можно легко добавлять как фигуры с помощью fig.add_shape. В приведенном ниже примере показано, как найти даты начала и окончания для периодов, заданных определенными критериями. В вашем случае эти критерии заключаются в том, является ли значение переменной выше или ниже определенного уровня.

Использование фигур вместо следов с помощью fig.add_trace()позволит вам определить положение относительно использования слоев графика layer='below'. А контуры фигур легко скрыть с помощью line=dict(color="rgba(0,0,0,0)).

График 1: Рисунок временного ряда со случайными данными:

График 2: Фон становится непрозрачным серым, когда A > 100:

График 2: Фон также становится непрозрачным красным, когдаD < 60

Полный код:

import numpy as np
import pandas as pd
import plotly.graph_objects as go
import plotly.express as px
import datetime

pd.set_option('display.max_rows', None)

# data sample
nperiods = 200
np.random.seed(123)
df = pd.DataFrame(np.random.randint(-10, 12, size=(nperiods, 4)),
                  columns=list('ABCD'))
datelist = pd.date_range(datetime.datetime(2020, 1, 1).strftime('%Y-%m-%d'),periods=nperiods).tolist()
df['dates'] = datelist 
df = df.set_index(['dates'])
df.index = pd.to_datetime(df.index)
df.iloc[0] = 0
df = df.cumsum().reset_index()

# function to set background color for a
# specified variable and a specified level



# plotly setup
fig = px.line(df, x='dates', y=df.columns[1:])
fig.update_xaxes(showgrid=True, gridwidth=1, gridcolor='rgba(0,0,255,0.1)')
fig.update_yaxes(showgrid=True, gridwidth=1, gridcolor='rgba(0,0,255,0.1)')

def bgLevels(fig, variable, level, mode, fillcolor, layer):
    """
    Set a specified color as background for given
    levels of a specified variable using a shape.
    
    Keyword arguments:
    ==================
    fig -- plotly figure
    variable -- column name in a pandas dataframe
    level -- int or float
    mode -- set threshold above or below
    fillcolor -- any color type that plotly can handle
    layer -- position of shape in plotly fiugre, like "below"
    
    """
    
    if mode == 'above':
        m = df[variable].gt(level)
    
    if mode == 'below':
        m = df[variable].lt(level)
        
    df1 = df[m].groupby((~m).cumsum())['dates'].agg(['first','last'])

    for index, row in df1.iterrows():
        #print(row['first'], row['last'])
        fig.add_shape(type="rect",
                        xref="x",
                        yref="paper",
                        x0=row['first'],
                        y0=0,
                        x1=row['last'],
                        y1=1,
                        line=dict(color="rgba(0,0,0,0)",width=3,),
                        fillcolor=fillcolor,
                        layer=layer) 
    return(fig)

fig = bgLevels(fig = fig, variable = 'A', level = 100, mode = 'above',
               fillcolor = 'rgba(100,100,100,0.2)', layer = 'below')

fig = bgLevels(fig = fig, variable = 'D', level = -60, mode = 'below',
               fillcolor = 'rgba(255,0,0,0.2)', layer = 'below')

fig.show()