Plotly: como plotar um intervalo com uma linha no centro usando um índice de data e hora?
Eu gostaria de traçar uma linha com um intervalo ao redor dela, como nesta foto:
Eu postei uma pergunta original, mas não especifiquei que o índice é um índice datetime. Achei que não seria importante, mas me enganei.
Existe uma resposta que o cobre com um índice numérico:
Plotly: Como fazer uma figura com várias linhas e área sombreada para desvios padrão?
e documentação aqui:
https://plotly.com/python/continuous-error-bars/
mas a questão do índice de data e hora não é coberta.
Aqui estão alguns dados de teste:
timestamp price min mean max
1596267946298 100.0 100 100.5 101
1596267946299 101.0 100 100.5 101
1596267946300 102.0 98 99.5 102
1596267948301 99.0 98 99.5 102
1596267948302 98.0 98 99.5 102
1596267949303 99.0 98 995. 102
onde eu gostaria que a banda cobrisse do mínimo ao máximo e a média fosse desenhada no centro.
outra opção é pegar o código da primeira resposta da pergunta postada acima ( Plotly: Como fazer uma figura com várias linhas e área sombreada para desvios padrão? ) e alterar a geração de dados para:
index = pd.date_range('1/1/2000', periods=25, freq='T')
df = pd.DataFrame(dict(A=np.random.uniform(low=-1, high=2, size=25).tolist(),
B=np.random.uniform(low=-4, high=3, size=25).tolist(),
C=np.random.uniform(low=-1, high=3, size=25).tolist()),
index=index)
isso funcionará da mesma maneira, mas criará um índice de data e hora.
Respostas
Em comparação com a configuração da pergunta vinculada , o que causa problemas é o fato de que x+x[::-1]não funciona muito bem com um índice datetime. Mas se você definir x=df.indexem:
# add line and shaded area for each series and standards deviation
for i, col in enumerate(df):
new_col = next(line_color)
# x = list(df.index.values+1)
x = df.index
Em seguida, substitua x+x[::-1]por x=x.append(x[::-1]):
# standard deviation area
fig.add_traces(go.Scatter(
#x+x[::-1],
x=x.append(x[::-1]),
Então as coisas devem funcionar perfeitamente bem.
Enredo:
Código completo:
# imports
import plotly.graph_objs as go
import plotly.express as px
import pandas as pd
import numpy as np
# sample data in a pandas dataframe
np.random.seed(1)
df=pd.DataFrame(dict(A=np.random.uniform(low=-1, high=2, size=25).tolist(),
B=np.random.uniform(low=-4, high=3, size=25).tolist(),
C=np.random.uniform(low=-1, high=3, size=25).tolist(),
))
df = df.cumsum()
# set daterange as index
df['dates'] = pd.date_range('2020', freq='D', periods=len(df))
df.set_index('dates', inplace=True)
# ---
# define colors as a list
colors = px.colors.qualitative.Plotly
# convert plotly hex colors to rgba to enable transparency adjustments
def hex_rgba(hex, transparency):
col_hex = hex.lstrip('#')
col_rgb = list(int(col_hex[i:i+2], 16) for i in (0, 2, 4))
col_rgb.extend([transparency])
areacol = tuple(col_rgb)
return areacol
rgba = [hex_rgba(c, transparency=0.2) for c in colors]
colCycle = ['rgba'+str(elem) for elem in rgba]
# Make sure the colors run in cycles if there are more lines than colors
def next_col(cols):
while True:
for col in cols:
yield col
line_color=next_col(cols=colCycle)
# plotly figure
fig = go.Figure()
# add line and shaded area for each series and standards deviation
for i, col in enumerate(df):
new_col = next(line_color)
x = df.index
y1 = df[col]
y1_upper = [(y + np.std(df[col])) for y in df[col]]
y1_lower = [(y - np.std(df[col])) for y in df[col]]
y1_lower = y1_lower[::-1]
# standard deviation area
fig.add_traces(go.Scatter(
#x+x[::-1],
x=x.append(x[::-1]),
y=y1_upper+y1_lower,
fill='tozerox',
fillcolor=new_col,
line=dict(color='rgba(255,255,255,0)'),
showlegend=False,
name=col))
# line trace
fig.add_traces(go.Scatter(x=df.index,
y=y1,
line=dict(color=new_col, width=2.5),
mode='lines',
name=col)
)
fig.update_layout(xaxis=dict(range=[df.index[1],df.index[-1]]))
fig.show()