Plotly: Comment définir une couleur de remplissage entre deux lignes verticales?
En utilisant matplotlib, nous pouvons "trivialement" remplir la zone entre deux lignes verticales en utilisant fill_between()
comme dans l'exemple:
https://matplotlib.org/3.2.1/gallery/lines_bars_and_markers/fill_between_demo.html#selectively-marking-horizontal-regions-across-the-whole-axes
En utilisant matplotlib, je peux faire ce dont j'ai besoin:

Nous avons deux signaux, et je calcule la corrélation de Pearson et de Spearman. Lorsque les corrélations sont inférieures à -0,5 ou supérieures à 0,5, je veux ombrer la période (bleu pour Pearson et orange pour Spearman). J'assombris également les week-ends en gris dans toutes les parcelles.
Cependant, j'ai du mal à accomplir la même chose en utilisant Plotly. Et il sera également utile de savoir comment le faire entre deux lignes horizontales.
Notez que j'utilise Plotly et Dash pour accélérer la visualisation de plusieurs parcelles. Les utilisateurs ont demandé un "type de chose plus dynamique". Cependant, je ne suis pas un gars de l'interface graphique et je ne peux pas passer du temps là-dessus, bien que je doive leur donner les premiers résultats.
BTW, j'ai essayé Bokeh dans le passé, et j'ai abandonné pour une raison dont je ne me souviens pas. Cela a l'air bien car je peux utiliser Python ou R, qui sont mes principaux outils de développement.
Merci,
Carlos
Réponses
Je ne pense pas qu'il existe une méthode intégrée de Plotly qui soit équivalente à la fill_between()
méthode de matplotlib . Toutefois, vous pouvez dessiner des formes afin qu'une solution de contournement possible consiste à dessiner un rectangle gris et à définir le paramètre layer="below"
afin que le signal soit toujours visible. Vous pouvez également définir les coordonnées du rectangle en dehors de votre plage d'axes pour vous assurer que le rectangle s'étend jusqu'aux bords du tracé.
Vous pouvez remplir la zone entre les lignes horizontales en dessinant un rectangle et en définissant les plages d'axes de la même manière.
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()

Vous n'avez pas fourni d'échantillon de données, je vais donc utiliser une série chronologique synthétique pour vous montrer comment ajouter un certain nombre de formes avec des dates de début et de fin définies pour plusieurs catégories différentes à l'aide d'une fonction personnalisée bgLevel
Deux lignes verticales avec un remplissage entre elles se transforment très rapidement en un rectangle. Et des rectangles peuvent facilement être ajoutés sous forme de formes à l'aide de fig.add_shape
. L'exemple ci-dessous vous montrera comment trouver les dates de début et de fin pour des périodes données par un certain critère. Dans votre cas, ces critères sont de savoir si la valeur d'une variable est supérieure ou inférieure à un certain niveau.
L'utilisation de formes au lieu de traces avec fig.add_trace()
vous permettra de définir la position par rapport aux calques de tracé à l'aide de layer='below'
. Et les contours des formes peuvent facilement être masqués à l'aide de line=dict(color="rgba(0,0,0,0))
.
Plot 1: Figure de série chronologique avec des données aléatoires:

Tracé 2: l' arrière-plan est défini sur un gris opaque lorsque A > 100
:

Tracé 2: l' arrière-plan est également défini sur un rouge opaque lorsqueD < 60

Code complet:
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()