Plotly: Add Time Series Trend layer in python

Viewed 326

I am new to python plotly and wondering if there's something like geom_smooth of R in Python. Currently, I have a monthly times series box plot with varying distribution:

1

I would like to add a smooth range curve to the box splot, so that it looks something like the pink geom_smooth trend line from R:

2

Here's the code I am using to create the plotly boxplot:

import pandas as pd
import numpy as np

df = pd.DataFrame()

df['date'] = pd.date_range('2021-01-01', '2021-12-20', freq='D')
df['month'] = df['date'].dt.month
df['spot_rate'] = np.random.randint(low=150, high=400, size=len(df.index))
            
fig2 = px.box(df, x="month", y="spot_rate")
fig2.show()
1 Answers

You can calculate the medians of each boxplot, y_bar, and draw the smoothed blue trendline by calling go.Scatter with the argument line_shape="spline".

If you want the trendline to go through the mean of each boxplot instead of the median, then you can use:

trendline = df.groupby(['month']).spot_rate.mean()

Then you can calculate the points that a 95% confidence interval should go through by using y_bar ± 1.96*stds/np.sqrt(n_vals)) where y_bar in the median of each month, stds are the standard deviations for each month, and n_vals are the number of data points for each month.

Then draw a spline through an array of the upper confidence interval, followed by a spline through lower confidence interval and fill the area between the two lines (similar to the "Filled Lines" section of the Plotly documentation).

EDIT: to display all of the months on the x-axis, you can set the tickmode to array, and tickvals=x1.

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

df = pd.DataFrame()

df['date'] = pd.date_range('2021-01-01', '2021-12-20', freq='D')
df['month'] = df['date'].dt.month

## set seed for reproducibility
np.random.seed(42)
df['spot_rate'] = np.random.randint(low=150, high=400, size=len(df.index))

fig = px.box(df, x="month", y="spot_rate")

trendline = df.groupby(['month']).spot_rate.mean()
x1,y1 = list(trendline.index), trendline.values
stds = df.groupby(['month']).spot_rate.std().values
n_vals = df.groupby(['month']).date.count().values

## construct a 0.95 CI using mean ± z*std/sqrt(n)
y1_upper = list(y1 + 1.96*stds/np.sqrt(n_vals))
y1_lower = list(y1 - 1.96*stds/np.sqrt(n_vals))
y1_lower = y1_lower[::-1]

x1_rev = x1[::-1]

fig.add_trace(go.Scatter(x=x1, y=y1, line_shape="spline", line_color="blue", name="trendline"))

fig.add_trace(go.Scatter(
    x=x1+x1_rev,
    y=y1_upper+y1_lower,
    line_shape="spline",
    fill='toself',
    fillcolor='rgba(255,192,203,0.5)',
    line_color='rgba(255,255,255,0)',
    showlegend=False,
    name="0.95 CI",
))

fig.update_layout(xaxis = dict(tickmode='array', tickvals=x1))

fig.show()

enter image description here

Related