Animated charts with frames

Plotly · Practical · 12 min

Animations show change over time memorably, as in the classic Gapminder chart, with a single extra argument.

In Plotly Express, adding animation_frame="year" creates one frame per value of that column, plus a slider and play/pause buttons. animation_group="country" tells Plotly which points are the same object across frames, so they move smoothly instead of reappearing.

px.scatter(df, x="gdpPercap", y="lifeExp", size="pop", color="continent",
           animation_frame="year", animation_group="country",
           range_x=[100, 100_000], range_y=[25, 90], log_x=True)

Always set range_x and range_y: axis ranges are computed for the first frame only, so later data can fly off the chart.

Under the hood the figure gets fig.frames, a list of go.Frame objects each holding its own data (and optionally layout), plus layout.updatemenus (the buttons) and layout.sliders. The initial fig.data is the first frame. You can build this yourself with graph objects for custom animations, and change speed through the button's animation arguments:

fig.layout.updatemenus[0].buttons[0].args[1]["frame"]["duration"] = 300

Plotly animates by interpolating between frames, which works well for scatter and bar traces; other trace types may just jump between states.

Animations suit exploration and presentations; for printed reports, small multiples (facets) of a few chosen years usually communicate better.

Example

import plotly.express as px

df = px.data.gapminder()
fig = px.scatter(
    df, x="gdpPercap", y="lifeExp", size="pop", size_max=45,
    color="continent", hover_name="country", log_x=True,
    animation_frame="year", animation_group="country",
    range_x=[200, 60_000], range_y=[25, 90],
)
print(len(fig.frames))                       # 12: one per year
print(fig.frames[0].name, fig.frames[-1].name)   # 1952 2007
print(len(fig.layout.sliders[0].steps))      # 12
print(fig.layout.updatemenus[0].buttons[0].method) # animate (the play button)

fig.layout.updatemenus[0].buttons[0].args[1]["frame"]["duration"] = 300
fig.write_html("gapminder_anim.html")

Remember

  • animation_frame creates frames, a slider and play buttons
  • animation_group keeps object identity across frames
  • Fix range_x/range_y, since ranges come from the first frame

Watch out for

  • Leaving axis ranges automatic, so points leave the visible area in later frames.

Check yourself

  1. Why should you set `range_y` on an animated px chart?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: plotly.com/python/animations/