Plotly: come impostare un colore di riempimento tra due linee verticali?
Usando matplotlib, possiamo riempire "banalmente" l'area tra due linee verticali usando fill_between()
come nell'esempio:
https://matplotlib.org/3.2.1/gallery/lines_bars_and_markers/fill_between_demo.html#selectively-marking-horizontal-regions-across-the-whole-axes
Usando matplotlib, posso fare quello che mi serve:

Abbiamo due segnali e sto calcolando la correlazione di Pearson e di Spearman. Quando le correlazioni sono inferiori a -0,5 o superiori a 0,5, voglio ombreggiare il periodo (blu per Pearson e arancione per Spearman). Inoltre oscuro di grigio i fine settimana in tutte le trame.
Tuttavia, trovo difficile ottenere lo stesso risultato usando Plotly. E sarà anche utile sapere come farlo tra due linee orizzontali.
Nota che sto usando Plotly e Dash per velocizzare la visualizzazione di diversi grafici. Gli utenti chiedevano un "tipo di cosa più dinamico". Tuttavia, non sono un tipo di GUI e non posso dedicare tempo a questo, anche se devo dar loro da mangiare con i risultati iniziali.
A proposito, ho provato Bokeh in passato e ho rinunciato per qualche motivo che non ricordo. Plotly sembra buono dato che posso usare sia da Python che da R, che sono i miei principali strumenti di sviluppo.
Grazie,
Carlos
Risposte
Non credo che ci sia alcun metodo Plotly integrato che sia equivalente al fill_between()
metodo di matplotlib . Tuttavia è possibile disegnare forme, quindi una possibile soluzione è disegnare un rettangolo grigio e impostare il parametro in layer="below"
modo che il segnale sia ancora visibile. Puoi anche impostare le coordinate del rettangolo al di fuori dell'intervallo dell'asse per assicurarti che il rettangolo si estenda fino ai bordi del grafico.
È possibile riempire l'area tra le linee orizzontali disegnando un rettangolo e impostando gli intervalli degli assi in modo simile.
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()

Non hai fornito un campione di dati, quindi userò una serie temporale sintetica per mostrarti come puoi aggiungere un numero di forme con date di inizio e fine definite per diverse categorie utilizzando una funzione personalizzata bgLevel
Due linee verticali con un riempimento tra di loro si trasformano molto rapidamente in un rettangolo. E i rettangoli possono essere facilmente aggiunti come forme utilizzando fig.add_shape
. L'esempio seguente ti mostrerà come trovare le date di inizio e fine per periodi dati da un determinato criterio. Nel tuo caso questi criteri sono se il valore di una variabile è superiore o inferiore a un certo livello.
L'uso di forme invece di tracce con fig.add_trace()
ti consentirà di definire la posizione rispetto ai livelli di stampa che utilizzano layer='below'
. E i contorni delle forme possono essere facilmente nascosti usando line=dict(color="rgba(0,0,0,0))
.
Grafico 1: figura della serie temporale con dati casuali:

Grafico 2: lo sfondo è impostato su un grigio opaco quando A > 100
:

Grafico 2: Anche lo sfondo è impostato su un rosso opaco quandoD < 60

Codice completo:
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()