Reputation: 12087
I would like to plot a line with a range around it, like on this photo:
I posted an original question, but didn't specify the index being a datetime index. I thought it wouldn't be important, but I was wrong.
There is an answer that covers it with a numerical index:
Plotly: How to make a figure with multiple lines and shaded area for standard deviations?
and documentation here:
https://plotly.com/python/continuous-error-bars/
but the issue of datetime index is not covered.
Here is some test data:
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
where I'd like the band to cover from min to max and the mean to be drawn in the center.
another option is to take the code from the first answer of the question posted above (Plotly: How to make a figure with multiple lines and shaded area for standard deviations?) and change the data generation to:
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)
this will work the same way but create a datetime index.
Upvotes: 4
Views: 2525
Reputation: 61104
Compared to the setup in the linked question, what causes trouble is the fact that x+x[::-1]
doesn't work very well with a datetime index. But if you set x=df.index
in:
# 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
And then replace x+x[::-1]
with x=x.append(x[::-1])
:
# standard deviation area
fig.add_traces(go.Scatter(
#x+x[::-1],
x=x.append(x[::-1]),
Then things should work out perfectly well.
# 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()
Upvotes: 1