For posterity’s sake I’ll share my finished code.
The primary source which helped me was a forum post on a similar issue which I stumbled across while looking for answers to another problem: Animation with slider not moving when pressing 'play'
I also refactored my code to follow the Slider and Buttons example here: https://plotly.com/python/animations/
I’ll note that the reference documentation is pretty slim for how to get sliders and buttons to work together. There’s no explanation for why the example code I mentioned actually works and the Python reference documentation for updatemenus, slider and frames leaves a lot unsaid. After much trial and error, here’s my final code.
import pandas as pd
import numpy as np
import plotly, plotly.graph_objects as go
import datetime as dt
from urllib.request import urlopen
import json
# read US county geojson file
with open(data_path +"us_county_geo.json") as f:
counties_json = json.load(f)
def numpy_dt64_to_str(dt64):
day_timestamp_dt = (dt64 - np.datetime64('1970-01-01T00:00:00Z')) / np.timedelta64(1, 's')
day_dt = dt.datetime.utcfromtimestamp(day_timestamp_dt)
return day_dt.strftime("%b %d")
plot_var = "ConfirmedPerDate"
us_df = df[(df["Country/Region"] == "United States") & \
(df.Date > pd.datetime(2020, 3, 21))]
days = np.sort(us_df.Date.unique())
plot_df = us_df[us_df.Date == days[-1]]
fig_data =go.Choroplethmapbox(geojson=counties_json, locations=plot_df.FIPS,
z=np.log10(plot_df[plot_var]),
zmin=0,
zmax=np.log10(us_df[plot_var].max()),
customdata=plot_df[plot_var],
name="",
text=plot_df.County.astype(str) + ", " + \
plot_df["Province/State"].astype(str),
hovertemplate="%{text}<br>Cases: %{customdata}",
colorbar=dict(outlinewidth=1,
outlinecolor="#333333",
len=0.9,
lenmode="fraction",
xpad=30,
xanchor="right",
bgcolor=None,
title=dict(text="Cases",
font=dict(size=14)),
tickvals=[0,1,2,3,4,5,6],
ticktext=["1", "10", "100", "1K", "10K", "100K", "1M"],
tickcolor="#333333",
tickwidth=2,
tickfont=dict(color="#333333",
size=12)),
colorscale="ylorrd", #ylgn
#reversescale=True,
marker_opacity=0.7,
marker_line_width=0)
token = open(data_path + ".mapbox_token").read()
fig_layout = go.Layout(mapbox_style="light",
mapbox_zoom=3,
mapbox_accesstoken=token,
mapbox_center={"lat": 37.0902, "lon": -95.7129},
margin={"r":0,"t":0,"l":0,"b":0},
plot_bgcolor=None)
fig_layout["updatemenus"] = [dict(type="buttons",
buttons=[dict(label="Play",
method="animate",
args=[None,
dict(frame=dict(duration=1000,
redraw=True),
fromcurrent=True)]),
dict(label="Pause",
method="animate",
args=[[None],
dict(frame=dict(duration=0,
redraw=True),
mode="immediate")])],
direction="left",
pad={"r": 10, "t": 35},
showactive=False,
x=0.1,
xanchor="right",
y=0,
yanchor="top")]
sliders_dict = dict(active=len(days) - 1,
visible=True,
yanchor="top",
xanchor="left",
currentvalue=dict(font=dict(size=20),
prefix="Date: ",
visible=True,
xanchor="right"),
pad=dict(b=10,
t=10),
len=0.875,
x=0.125,
y=0,
steps=[])
fig_frames = []
for day in days:
plot_df = us_df[us_df.Date == day]
frame = go.Frame(data=[go.Choroplethmapbox(locations=plot_df.FIPS,
z=np.log10(plot_df[plot_var]),
customdata=plot_df[plot_var],
name="",
text=plot_df.County.astype(str) + ", " + \
plot_df["Province/State"].astype(str),
hovertemplate="%{text}<br>%{customdata}")],
name=numpy_dt64_to_str(day))
fig_frames.append(frame)
slider_step = dict(args=[[numpy_dt64_to_str(day)],
dict(mode="immediate",
frame=dict(duration=300,
redraw=True))],
method="animate",
label=numpy_dt64_to_str(day))
sliders_dict["steps"].append(slider_step)
fig_layout.update(sliders=[sliders_dict])
# Plot the figure
fig=go.Figure(data=fig_data, layout=fig_layout, frames=fig_frames)
fig.show(renderer="browser")