I’ve got a Dash graph “animation” (multiple frames, which show different days) whose lines don’t show up when the frame changes. I’ve been using Dash for a few years now and I’ve never run into this problem before, but I have a reproducible example below.
Here’s what all of the frames are supposed to look like (note the orange lines on top and blue lines on the bottom):
That was the first frame, above. The second frame from the right looks like this, with no orange lines above the blue lines. I can assure you there’s data there; it’s just not showing up!
To get set up to answer this question, install the following libraries:
pip install dash plotly pandas colour
The dataframe I’m using can be downloaded from here, as a CSV file called “dataframe.csv”:
http://www.wikiupload.com/WML4A6FKXHPPLA1
Here’s a backup link, in case the above link doesn’t work for some reason:
https://www.filehosting.org/file/details/919052/dataframe.csv
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objects as go
import pandas as pd
from colour import Color
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
df = pd.read_csv("./dataframe.csv")
x_min = df["inches"].min()
x_max = df["inches"].max()
y_min = df["load"].min()
y_max = df["load"].max()
frames = []
slider_steps = []
slider_distinct_days_set = set()
mode = "lines"
marker = dict(
size=5,
opacity=0.5,
)
line = dict(
shape="spline",
smoothing=0.4,
)
# Transition in milliseconds for the animation (default 500)
duration_frame = 1000
duration_transition = 0
duration_transition_slider = 1000
# Docs say redraw not needed for scatterplots, but if it doesn't redraw,
# the annotations stay the same as for the first frame...
redraw = True
easing = "exp-in-out"
ordering = "layout first" # default
mode_animate = "immediate" # default
bootstrap_blue_base = Color("#5bc0de")
bootstrap_blue_lum = Color("#5bc0de")
bootstrap_blue_lum.luminance = 0.9
bootstrap_red_base = Color("#f0ad4e")
bootstrap_red_lum = Color("#f0ad4e")
bootstrap_red_lum.luminance = 0.9
# Add scatters to the animation by day
for gname_day, gdf_day in df.groupby("timestamp_local_day"):
frame = {
"data": [],
"name": gname_day,
"layout": {}
}
hours_in_day = gdf_day["timestamp_local"].nunique()
up_colors = list(
bootstrap_red_lum.range_to(bootstrap_red_base, hours_in_day)
)
down_colors = list(
bootstrap_blue_lum.range_to(bootstrap_blue_base, hours_in_day)
)
for gname_isup, gdf_isup in gdf_day.groupby("up_down"):
i = 0
colors = down_colors if gname_isup == "Downstroke" else up_colors
for label, gdf_ts in gdf_isup.groupby("timestamp_local"):
print(f"{gname_day} {gname_isup} {label} color: {colors[i].hex}")
frame["data"].append(
go.Scatter(
name=label,
mode=mode, # lines or markers
x=gdf_ts["inches"],
y=gdf_ts["load"],
marker=dict(
color=colors[i].hex,
),
line=line,
)
)
i += 1
frames.append(frame)
if gname_day not in slider_distinct_days_set:
slider_distinct_days_set.add(gname_day)
slider_steps.append(
{
"method": "animate",
"label": gname_day, # text label to appear on the slider
"args": [
[gname_day],
{
"mode": mode_animate,
"frame": {"duration": duration_frame, "redraw": redraw},
"transition": {
"duration": duration_transition_slider,
"easing": easing,
},
"ordering": ordering,
},
],
}
)
most_recent_day_available_index = max(0, len(slider_distinct_days_set) - 1)
sliders = [
{
# IMPORTANT: this is the "active" step in the slider, which shows up on load
"active": most_recent_day_available_index,
"pad": {"b": 10, "t": 60},
"len": 0.9,
"x": 0.1,
"xanchor": "left",
"y": 0,
"yanchor": "top",
"steps": slider_steps,
"transition": {"duration": duration_transition_slider},
}
]
updatemenus = [
{
"type": "buttons",
"direction": "left",
"pad": {"r": 10, "t": 70},
"showactive": False,
"x": 0.1,
"xanchor": "right",
"y": 0,
"yanchor": "top",
"buttons": [
{
"label": "Play",
"method": "animate",
"args": [
None,
{
"mode": mode_animate,
"direction": "reverse", # forward or reverse
"fromcurrent": True,
"frame": {"duration": duration_frame, "redraw": redraw},
"transition": {
"duration": duration_transition,
"easing": easing,
},
"ordering": ordering,
},
],
},
{
"label": "Pause",
"method": "animate",
"args": [
[None],
{
"mode": "immediate",
"frame": {"duration": 0, "redraw": redraw},
"transition": {
"duration": 0,
},
},
],
},
],
}
]
fig = go.Figure(
# Make the initial data, before the animation frames start
data=frames[-1]["data"],
frames=frames,
layout=go.Layout(
hovermode="closest",
height=500,
plot_bgcolor="white",
showlegend=False,
font={"family": "Segoe UI", "color": "#717174"},
xaxis=dict(
gridcolor="rgb(238,238,238)",
range=[x_min - 5, x_max + 5],
title="position",
),
yaxis=dict(
gridcolor="rgb(238,238,238)",
range=[y_min - 5, y_max + 5],
title="Weight",
),
margin=go.layout.Margin(l=0, r=10, b=0, t=0),
sliders=sliders,
updatemenus=updatemenus,
),
)
app.layout = html.Div(
[
dcc.Graph(
figure=fig,
)
]
)
if __name__ == '__main__':
app.run_server(debug=True, host='0.0.0.0', port=5000)
FYI, I’ve posted the same question on StackOverflow here
1 post - 1 participant