Skip to content

Animation Tutorial #24274

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
Nov 23, 2022
Merged
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
fcc5f30
First version of animation tutorial
chahak13 Oct 25, 2022
88e3bcc
Simplify examples by removing data gen functions
chahak13 Oct 27, 2022
76270ac
Add tutorial text
chahak13 Oct 28, 2022
986e8ce
Format to agree with flake8
chahak13 Oct 28, 2022
a15d212
Update tutorial and scatter example
chahak13 Oct 29, 2022
9a0e85e
Fix example for line animation
chahak13 Oct 29, 2022
6c63bb3
Change to simpler line animation example
chahak13 Nov 2, 2022
2ebdcd0
Change headings and update set_offsets description
chahak13 Nov 2, 2022
5e2f4ef
Change writer type to pipe-based from standard
chahak13 Nov 2, 2022
eaa22e1
Update saving animation section
chahak13 Nov 2, 2022
cfe596f
Add new example for images and ArtistAnimation
chahak13 Nov 3, 2022
c3c16b6
Remove unused variables
chahak13 Nov 5, 2022
8a148ea
Correct type of animation class
chahak13 Nov 5, 2022
b4137a3
Remove unused variable
chahak13 Nov 5, 2022
b42966b
Correct wrong correction
chahak13 Nov 5, 2022
e645c62
Update ArtistAnimation example to use bar chart
chahak13 Nov 6, 2022
d05eaed
Barchart race example for ArtistAnimation
chahak13 Nov 7, 2022
46e691d
Change FuncAnimation to have one general guideline
chahak13 Nov 10, 2022
5f4e555
Add a table for set methods for example artists
chahak13 Nov 11, 2022
8fba320
Add plotting method, return artist type table
chahak13 Nov 12, 2022
67c4f53
Reword to avoid confusion
chahak13 Nov 13, 2022
85913db
Reposition note about pipe based writers
chahak13 Nov 15, 2022
57ae9ed
Change color and don't sort bar chart
chahak13 Nov 15, 2022
b8e0e1d
Remove animation code in save animation section
chahak13 Nov 17, 2022
abbb031
Change inline codeblocks to clear CI
chahak13 Nov 18, 2022
c0ea38d
Make small corrections as per code review
chahak13 Nov 19, 2022
cbef186
Remove :class: from links
chahak13 Nov 19, 2022
1def505
Set the animation range so that it loops
chahak13 Nov 19, 2022
b5af4e6
Update to better animation range for scatter
chahak13 Nov 19, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
224 changes: 224 additions & 0 deletions tutorials/introductory/animation_tutorial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
"""
===========================
Animations using Matplotlib
===========================

Based on its plotting functionality, Matplotlib also provides an interface to
generate animations using the `~matplotlib.animation` module. An
animation is a sequence of frames where each frame corresponds to a plot on a
`~matplotlib.figure.Figure`. This tutorial covers a general guideline on
how to create such animations and the different options available.
"""

import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np

###############################################################################
# Animation Classes
# =================
#
# The animation process in Matplotlib can be thought of in 2 different ways:
#
# - `~matplotlib.animation.FuncAnimation`: Generate data for first
# frame and then modify this data for each frame to create an animated plot.
#
# - `~matplotlib.animation.ArtistAnimation`: Generate a list (iterable)
# of artists that will draw in each frame in the animation.
#
# `~matplotlib.animation.FuncAnimation` is more efficient in terms of
# speed and memory as it draws an artist once and then modifies it. On the
# other hand `~matplotlib.animation.ArtistAnimation` is flexible as it
# allows any iterable of artists to be animated in a sequence.
#
# ``FuncAnimation``
# -----------------
#
# The `~matplotlib.animation.FuncAnimation` class allows us to create an
# animation by passing a function that iteratively modifies the data of a plot.
# This is achieved by using the *setter* methods on various
# `~matplotlib.artist.Artist` (examples: `~matplotlib.lines.Line2D`,
# `~matplotlib.collections.PathCollection`, etc.). A usual
# `~matplotlib.animation.FuncAnimation` object takes a
# `~matplotlib.figure.Figure` that we want to animate and a function
# *func* that modifies the data plotted on the figure. It uses the *frames*
# parameter to determine the length of the animation. The *interval* parameter
# is used to determine time in milliseconds between drawing of two frames.
# Animating using `.FuncAnimation` would usually follow the following
# structure:
#
# - Plot the initial figure, including all the required artists. Save all the
# artists in variables so that they can be updated later on during the
# animation.
# - Create an animation function that updates the data in each artist to
# generate the new frame at each function call.
# - Create a `.FuncAnimation` object with the `.Figure` and the animation
# function, along with the keyword arguments that determine the animation
# properties.
# - Use `.animation.Animation.save` or `.pyplot.show` to save or show the
# animation.
#
# The update function uses the ``set_*`` function for different artists to
# modify the data. The following table shows a few plotting methods, the artist
# types they return and some methods that can be used to update them.
#
# ================= ============================= ===========================
# Plotting method Artist Set method
# ================= ============================= ===========================
# `.Axes.plot` `.lines.Line2D` `.lines.Line2D.set_data`
# `.Axes.scatter` `.collections.PathCollection` `.collections.\
# PathCollection.set_offsets`
# `.Axes.imshow` `.image.AxesImage` ``AxesImage.set_data``
# `.Axes.annotate` `.text.Annotation` `.text.Annotation.\
# update_positions`
# `.Axes.barh` `.patches.Rectangle` `.Rectangle.set_angle`,
# `.Rectangle.set_bounds`,
# `.Rectangle.set_height`,
# `.Rectangle.set_width`,
# `.Rectangle.set_x`,
# `.Rectangle.set_y`
# `.Rectangle.set_xy`
# `.Axes.fill` `.patches.Polygon` `.Polygon.set_xy`
# `.patches.Circle` `.patches.Ellipse` `.Ellipse.set_angle`,
# `.Ellipse.set_center`,
# `.Ellipse.set_height`,
# `.Ellipse.set_width`
# ================= ============================= ===========================
#
# Covering the set methods for all types of artists is beyond the scope of this
# tutorial but can be found in their respective documentations. An example of
# such update methods in use for `.Axes.scatter` is as follows.


fig, ax = plt.subplots()
t = np.linspace(-np.pi, np.pi, 400)
a, b = 3, 2
delta = np.pi / 2

scat = ax.scatter(np.sin(a * t[0] + delta), np.sin(b * t[0]), c="b", s=2)
ax.set_xlim(-1.5, 1.5)
ax.set_ylim(-1.5, 1.5)


def update(frame):
# .set_offsets replaces the offset data for the entire collection with
# the new values. Therefore, to also carry forward the previously
# calculated information, we use the data from the first to the current
# frame to set the new offsets.
x = np.sin(a * t[:frame] + delta)
y = np.sin(b * t[:frame])
data = np.stack([x, y]).T
scat.set_offsets(data)
return (scat,)


ani = animation.FuncAnimation(fig=fig, func=update, frames=400, interval=30)
plt.show()


###############################################################################
# ``ArtistAnimation``
# -------------------
#
# `~matplotlib.animation.ArtistAnimation` can be used
# to generate animations if there is data stored on various different artists.
# This list of artists is then converted frame by frame into an animation. For
# example, when we use `.Axes.barh` to plot a bar-chart, it creates a number of
# artists for each of the bar and error bars. To update the plot, one would
# need to update each of the bars from the container individually and redraw
# them. Instead, `.animation.ArtistAnimation` can be used to plot each frame
# individually and then stitched together to form an animation. A barchart race
# is a simple example for this.


fig, ax = plt.subplots()
rng = np.random.default_rng(19680801)
data = np.array([20, 20, 20, 20])
x = np.array([1, 2, 3, 4])

artists = []
colors = ['tab:blue', 'tab:red', 'tab:green', 'tab:purple']
for i in range(20):
data += rng.integers(low=0, high=10, size=data.shape)
container = ax.barh(x, data, color=colors)
artists.append(container)


ani = animation.ArtistAnimation(fig=fig, artists=artists, interval=400)
plt.show()

###############################################################################
# Animation Writers
# =================
#
# Animation objects can be saved to disk using various multimedia writers
# (ex: Pillow, *ffpmeg*, *imagemagick*). Not all video formats are supported
# by all writers. There are 4 major types of writers:
#
# - `~matplotlib.animation.PillowWriter` - Uses the Pillow library to
# create the animation.
#
# - `~matplotlib.animation.HTMLWriter` - Used to create JavaScript-based
# animations.
#
# - Pipe-based writers - `~matplotlib.animation.FFMpegWriter` and
# `~matplotlib.animation.ImageMagickWriter` are pipe based writers.
# These writers pipe each frame to the utility (*ffmpeg* / *imagemagick*)
# which then stitches all of them together to create the animation.
#
# - File-based writers - `~matplotlib.animation.FFMpegFileWriter` and
# `~matplotlib.animation.ImageMagickFileWriter` are examples of
# file-based writers. These writers are slower than their pipe-based
# alternatives but are more useful for debugging as they save each frame in
# a file before stitching them together into an animation.
#
# Saving Animations
# -----------------
#
# ========================================== ===========================
# Writer Supported Formats
# ========================================== ===========================
# `~matplotlib.animation.PillowWriter` .gif, .apng
# `~matplotlib.animation.HTMLWriter` .htm, .html, .png
# `~matplotlib.animation.FFMpegWriter` All formats supported by
# *ffmpeg*
# `~matplotlib.animation.ImageMagickWriter` .gif
# ========================================== ===========================
#
# To save animations using any of the writers, we can use the
# `.animation.Animation.save` method. It takes the *filename* that we want to
# save the animation as and the *writer*, which is either a string or a writer
# object. It also takes an *fps* argument. This argument is different than the
# *interval* argument that `~.animation.FuncAnimation` or
# `~.animation.ArtistAnimation` uses. *fps* determines the frame rate that the
# **saved** animation uses, whereas *interval* determines the frame rate that
# the **displayed** animation uses.
#
# Below are a few examples that show how to save an animation with different
# writers.
#
#
# Pillow writers::
#
# ani.save(filename="/tmp/pillow_example.gif", writer="pillow")
# ani.save(filename="/tmp/pillow_example.apng", writer="pillow")
#
# HTML writers::
#
# ani.save(filename="/tmp/html_example.html", writer="html")
# ani.save(filename="/tmp/html_example.htm", writer="html")
# ani.save(filename="/tmp/html_example.png", writer="html")
#
# FFMpegWriter::
#
# ani.save(filename="/tmp/ffmpeg_example.mkv", writer="ffmpeg")
# ani.save(filename="/tmp/ffmpeg_example.mp4", writer="ffmpeg")
# ani.save(filename="/tmp/ffmpeg_example.mjpeg", writer="ffmpeg")
#
# Imagemagick writers::
#
# ani.save(filename="/tmp/imagemagick_example.gif", writer="imagemagick")
#
# Since the frames are piped out to *ffmpeg* or *imagemagick*,
# ``writer="ffmpeg"`` and ``writer="imagemagick"`` support all formats
# supported by *ffmpeg* and *imagemagick*.