I want to create a figure that changes between 20 different graphs using Plotly's sliders. I am modelling a moving average, where the slider changes the number of days used in the aggregation of data. The second piece of data I want to visualize, is a savgol filter of that moving average. I am trying to graph the two separate arrays of data on each graph, but as of now, it currently appends the the arrays on separate steps. I was hoping for 20 steps of graphs with 2 different functions, rather than 40 steps with just one function on each.
My mock data is:
# Imports
import pandas as pd
import numpy as np
import plotly
import plotly.express as px
import plotly.graph_objs as go
import sys
import random
Year = 2000
Date = pd.Series(pd.date_range(str(Year) + "-01-01", str(Year) + "-12-31", freq="D"))
Day = Date.diff().astype("timedelta64[D]").fillna(1).cumsum()
dftest = pd.DataFrame({"Day": Day})
dftest = dftest.set_index(Date).reset_index()
dftest = dftest.rename(columns={"index": "Date"})
dftest["Qty"] = [random.randint(1, 10000) for k in dftest.index]
TrendDF = dftest
My code is:
def TrendMAn(n):
TrendDF["MAn"] = TrendDF["Qty"].rolling(window=n).mean().set_axis(TrendDF.index)
return TrendDF["MAn"]
fig = go.Figure()
for step in range(1, 20):
fig.add_trace(
go.Scatter(
line=dict(width=2),
x=TrendDF["Date"],
y=TrendMAn(step),
mode="lines",
visible=False,
)
)
fig.add_trace(
go.Scatter(
line=dict(width=2),
x=TrendDF["Date"],
y=savgol_filter(TrendMAn(step), 51, 3),
mode="lines",
visible=False,
)
)
fig.data[14].visible = True
steps = []
for i in range(len(fig.data)):
step = dict(
method="update",
args=[
{"visible": [False] * len(fig.data)},
{"title": "Slider switched to step: " + str(i)},
], # layout attribute
)
step["args"][0]["visible"][i] = True # Toggle i'th trace to "visible"
steps.append(step)
sliders = [
dict(
active=10,
currentvalue={"prefix": "Rolling Average n: "},
pad={"t": 50},
steps=steps,
)
]
fig.update_layout(sliders=sliders)
fig.show()
The results gives me 40 steps instead of 20, as the "for loop" is creating 40 traces which makes sense. But, I want the two traces I am calling in the "for loop" on the same step.
Since the filter function is not presented, I reused the moving average function and changed the parameters to create two different graphs. fig.data is lined up with the first graph, the second graph, the graph at the next index after the first, and the graph at the second index, so you can show or hide the Add one line of control for display and hide.
import pandas as pd
import numpy as np
import plotly
import plotly.express as px
import plotly.graph_objs as go
import sys
import random
random.seed(20231129)
Year = 2000
Date = pd.Series(pd.date_range(str(Year) + "-01-01", str(Year) + "-12-31", freq="D"))
Day = Date.diff().astype("timedelta64[D]").fillna(1).cumsum()
dftest = pd.DataFrame({"Day": Day})
dftest = dftest.set_index(Date).reset_index()
dftest = dftest.rename(columns={"index": "Date"})
dftest["Qty"] = [random.randint(1, 10000) for k in dftest.index]
TrendDF = dftest
def TrendMAn(n):
TrendDF["MAn"] = TrendDF["Qty"].rolling(window=n).mean().set_axis(TrendDF.index)
return TrendDF["MAn"]
fig = go.Figure()
for step in range(1, 20):
fig.add_trace(
go.Scatter(
line=dict(width=2),
x=TrendDF["Date"],
y=TrendMAn(step),
mode="lines",
visible=False,
)
)
fig.add_trace(
go.Scatter(
line=dict(width=2),
x=TrendDF["Date"],
#y=savgol_filter(TrendMAn(step), 51, 3),
y=TrendMAn(step+30),
mode="lines",
visible=False,
)
)
fig.data[14].visible = True
fig.data[28].visible = True
steps = []
for i in range(1,20):
step = dict(
method="update",
args=[
{"visible": [False] * len(fig.data)},
{"title": "Slider switched to step: " + str(i)},
], # layout attribute
)
step["args"][0]["visible"][i] = True # Toggle i'th trace to "visible"
step["args"][0]["visible"][i+1] = True # Update
steps.append(step)
sliders = [
dict(
active=10,
currentvalue={"prefix": "Rolling Average n: "},
pad={"t": 50},
steps=steps,
)
]
fig.update_layout(sliders=sliders)
fig.show()