An introduction to creating animations with Plotly in Python.
Plotly Studio: Transform any dataset into an interactive data application in minutes with AI. Sign up for early access now.
Animated figures with Plotly Express¶Several Plotly Express functions support the creation of animated figures through the animation_frame
and animation_group
arguments.
Here is an example of an animated scatter plot created with Plotly Express. Note that you should always fix the x_range
and y_range
to ensure that your data remains visible throughout the animation.
In [1]:
import plotly.express as px df = px.data.gapminder() px.scatter(df, x="gdpPercap", y="lifeExp", animation_frame="year", animation_group="country", size="pop", color="continent", hover_name="country", log_x=True, size_max=55, range_x=[100,100000], range_y=[25,90])Animated figures in Dash¶
Dash is the best way to build analytical apps in Python using Plotly figures. To run the app below, run pip install dash
, click "Download" to get the code and run python app.py
.
Get started with the official Dash docs and learn how to effortlessly style & deploy apps like this with Dash Enterprise.
Animated Bar Charts with Plotly Express¶Note that you should always fix the y_range
to ensure that your data remains visible throughout the animation.
In [3]:
import plotly.express as px df = px.data.gapminder() fig = px.bar(df, x="continent", y="pop", color="continent", animation_frame="year", animation_group="country", range_y=[0,4000000000]) fig.show()Current Animation Limitations and Caveats¶
scatter
and bar
The remainder of this section describes the low-level graph objects API for constructing animated figures manually.
Frames¶Along with data
and layout
, frames
can be added as a key in a figure object. The frames
key points to a list of figures, each of which will be cycled through when animation is triggered.
You can add play and pause buttons to control your animated charts by adding an updatemenus
array to the layout
of your figure
. More information on style and placement of the buttons is available in Plotly's updatemenus
reference.
The buttons are defined as follows:
"updatemenus": [{"type": "buttons",
"buttons": [{"label": "Your Label",
"method": "animate",
"args": [See Below]}]}]
Defining Button Arguments¶
None
: Setting "args"
to undefined (i.e. "args": [None]
) will create a simple play button that will animate all frames."<some string>"
. This is a way of scoping the animations in case you would prefer to animate without explicitly enumerating all frames.["frame1", "frame2", ...]
: Animate a sequence of named frames.[{data: [], layout: {}, traces: []}, {...}]
: Nearly identical to animating named frames; though this variant lets you inline data instead of adding it as named frames. This can be useful for interaction where it's undesirable to add and manage named frames for ephemeral changes.[null]
: A simple way to create a pause button (requires mode: "immediate"
). This argument dumps the currently queued frames (mode: "immediate"
), and then animates an empty sequence of frames ([null]
).[ ]
. This syntax may cause confusion because it looks indistinguishable from a "pause button", but nested properties have logic that treats empty arrays as entirely removable, so it will function as a play button.
Refer to the examples below to see the buttons in action!
In [4]:
import plotly.graph_objects as go fig = go.Figure( data=[go.Scatter(x=[0, 1], y=[0, 1])], layout=go.Layout( xaxis=dict(range=[0, 5], autorange=False), yaxis=dict(range=[0, 5], autorange=False), title=dict(text="Start Title"), updatemenus=[dict( type="buttons", buttons=[dict(label="Play", method="animate", args=[None])])] ), frames=[go.Frame(data=[go.Scatter(x=[1, 2], y=[1, 2])]), go.Frame(data=[go.Scatter(x=[1, 4], y=[1, 4])]), go.Frame(data=[go.Scatter(x=[3, 4], y=[3, 4])], layout=go.Layout(title_text="End Title"))] ) fig.show()
In [5]:
import plotly.graph_objects as go import numpy as np # Generate curve data t = np.linspace(-1, 1, 100) x = t + t ** 2 y = t - t ** 2 xm = np.min(x) - 1.5 xM = np.max(x) + 1.5 ym = np.min(y) - 1.5 yM = np.max(y) + 1.5 N = 25 s = np.linspace(-1, 1, N) xx = s + s ** 2 yy = s - s ** 2 # Create figure fig = go.Figure( data=[go.Scatter(x=x, y=y, mode="lines", line=dict(width=2, color="blue")), go.Scatter(x=[xx[0]], y=[yy[0]], mode="markers", marker=dict(color="red", size=10))]) fig.update_layout(width=600, height=450, xaxis=dict(range=[xm, xM], autorange=False, zeroline=False), yaxis=dict(range=[ym, yM], autorange=False, zeroline=False), title_text="Kinematic Generation of a Planar Curve", title_x=0.5, updatemenus = [dict(type = "buttons", buttons = [ dict( args = [None, {"frame": {"duration": 10, "redraw": False}, "fromcurrent": True, "transition": {"duration": 10}}], label = "Play", method = "animate", )])]) fig.update(frames=[go.Frame( data=[go.Scatter( x=[xx[k]], y=[yy[k]])], traces=[1]) # fig.data[1] is updated by each frame for k in range(N)]) fig.show()Moving Frenet Frame Along a Planar Curve¶
In [6]:
import plotly.graph_objects as go import numpy as np # Generate curve data t = np.linspace(-1, 1, 100) x = t + t ** 2 y = t - t ** 2 xm = np.min(x) - 1.5 xM = np.max(x) + 1.5 ym = np.min(y) - 1.5 yM = np.max(y) + 1.5 N = 50 s = np.linspace(-1, 1, N) xx = s + s ** 2 yy = s - s ** 2 vx = 1 + 2 * s vy = 1 - 2 * s # v=(vx, vy) is the velocity speed = np.sqrt(vx ** 2 + vy ** 2) ux = vx / speed # (ux, uy) unit tangent vector, (-uy, ux) unit normal vector uy = vy / speed xend = xx + ux # end coordinates for the unit tangent vector at (xx, yy) yend = yy + uy xnoe = xx - uy # end coordinates for the unit normal vector at (xx,yy) ynoe = yy + ux # Create figure fig = go.Figure( data=[go.Scatter(x=x, y=y, name="frame", mode="lines", line=dict(width=2, color="blue")), go.Scatter(x=x, y=y, name="curve", mode="lines", line=dict(width=2, color="blue")) ], layout=go.Layout(width=600, height=600, xaxis=dict(range=[xm, xM], autorange=False, zeroline=False), yaxis=dict(range=[ym, yM], autorange=False, zeroline=False), title=dict(text="Moving Frenet Frame Along a Planar Curve"), hovermode="closest", updatemenus=[dict(type="buttons", buttons=[dict(label="Play", method="animate", args=[None])])]), frames=[go.Frame( data=[go.Scatter( x=[xx[k], xend[k], None, xx[k], xnoe[k]], y=[yy[k], yend[k], None, yy[k], ynoe[k]], mode="lines", line=dict(color="red", width=2)) ]) for k in range(N)] ) fig.show()Using a Slider and Buttons¶
The following example uses the well known Gapminder dataset to exemplify animation capabilities. This bubble chart animation shows the change in 'GDP per Capita' against the 'Life Expectancy' of several countries from the year 1952 to 2007, colored by their respective continent and sized by population.
This is also an example of building up the structure of a figure as a Python dictionary, and then constructing a graph object figure from that dictionary.
In [7]:
import plotly.graph_objects as go import pandas as pd url = "https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv" dataset = pd.read_csv(url) years = ["1952", "1962", "1967", "1972", "1977", "1982", "1987", "1992", "1997", "2002", "2007"] # make list of continents continents = [] for continent in dataset["continent"]: if continent not in continents: continents.append(continent) # make figure fig_dict = { "data": [], "layout": {}, "frames": [] } # fill in most of layout fig_dict["layout"]["xaxis"] = {"range": [30, 85], "title": "Life Expectancy"} fig_dict["layout"]["yaxis"] = {"title": "GDP per Capita", "type": "log"} fig_dict["layout"]["hovermode"] = "closest" fig_dict["layout"]["updatemenus"] = [ { "buttons": [ { "args": [None, {"frame": {"duration": 500, "redraw": False}, "fromcurrent": True, "transition": {"duration": 300, "easing": "quadratic-in-out"}}], "label": "Play", "method": "animate" }, { "args": [[None], {"frame": {"duration": 0, "redraw": False}, "mode": "immediate", "transition": {"duration": 0}}], "label": "Pause", "method": "animate" } ], "direction": "left", "pad": {"r": 10, "t": 87}, "showactive": False, "type": "buttons", "x": 0.1, "xanchor": "right", "y": 0, "yanchor": "top" } ] sliders_dict = { "active": 0, "yanchor": "top", "xanchor": "left", "currentvalue": { "font": {"size": 20}, "prefix": "Year:", "visible": True, "xanchor": "right" }, "transition": {"duration": 300, "easing": "cubic-in-out"}, "pad": {"b": 10, "t": 50}, "len": 0.9, "x": 0.1, "y": 0, "steps": [] } # make data year = 1952 for continent in continents: dataset_by_year = dataset[dataset["year"] == year] dataset_by_year_and_cont = dataset_by_year[ dataset_by_year["continent"] == continent] data_dict = { "x": list(dataset_by_year_and_cont["lifeExp"]), "y": list(dataset_by_year_and_cont["gdpPercap"]), "mode": "markers", "text": list(dataset_by_year_and_cont["country"]), "marker": { "sizemode": "area", "sizeref": 200000, "size": list(dataset_by_year_and_cont["pop"]) }, "name": continent } fig_dict["data"].append(data_dict) # make frames for year in years: frame = {"data": [], "name": str(year)} for continent in continents: dataset_by_year = dataset[dataset["year"] == int(year)] dataset_by_year_and_cont = dataset_by_year[ dataset_by_year["continent"] == continent] data_dict = { "x": list(dataset_by_year_and_cont["lifeExp"]), "y": list(dataset_by_year_and_cont["gdpPercap"]), "mode": "markers", "text": list(dataset_by_year_and_cont["country"]), "marker": { "sizemode": "area", "sizeref": 200000, "size": list(dataset_by_year_and_cont["pop"]) }, "name": continent } frame["data"].append(data_dict) fig_dict["frames"].append(frame) slider_step = {"args": [ [year], {"frame": {"duration": 300, "redraw": False}, "mode": "immediate", "transition": {"duration": 300}} ], "label": year, "method": "animate"} sliders_dict["steps"].append(slider_step) fig_dict["layout"]["sliders"] = [sliders_dict] fig = go.Figure(fig_dict) fig.show()Important Notes¶
redraw
: Setting redraw: false
is an optimization for scatter plots so that animate just makes changes without redrawing the whole plot. For other plot types, such as contour plots, every frame must be a total plot redraw, i.e. redraw: true
.For additional information and attributes for creating bubble charts in Plotly see: https://plotly.com/python/bubble-charts/. For more documentation on creating animations with Plotly, see https://plotly.com/python/#animations.
What About Dash?¶Dash is an open-source framework for building analytical applications, with no Javascript required, and it is tightly integrated with the Plotly graphing library.
Learn about how to install Dash at https://dash.plot.ly/installation.
Everywhere in this page that you see fig.show()
, you can display the same figure in a Dash application by passing it to the figure
argument of the Graph
component from the built-in dash_core_components
package like this:
import plotly.graph_objects as go # or plotly.express as px fig = go.Figure() # or any Plotly Express function e.g. px.bar(...) # fig.add_trace( ... ) # fig.update_layout( ... ) from dash import Dash, dcc, html app = Dash() app.layout = html.Div([ dcc.Graph(figure=fig) ]) app.run(debug=True, use_reloader=False) # Turn off reloader if inside Jupyter
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4