-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
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
Animation Tutorial #24274
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 88e3bcc
Simplify examples by removing data gen functions
chahak13 76270ac
Add tutorial text
chahak13 986e8ce
Format to agree with flake8
chahak13 a15d212
Update tutorial and scatter example
chahak13 9a0e85e
Fix example for line animation
chahak13 6c63bb3
Change to simpler line animation example
chahak13 2ebdcd0
Change headings and update set_offsets description
chahak13 5e2f4ef
Change writer type to pipe-based from standard
chahak13 eaa22e1
Update saving animation section
chahak13 cfe596f
Add new example for images and ArtistAnimation
chahak13 c3c16b6
Remove unused variables
chahak13 8a148ea
Correct type of animation class
chahak13 b4137a3
Remove unused variable
chahak13 b42966b
Correct wrong correction
chahak13 e645c62
Update ArtistAnimation example to use bar chart
chahak13 d05eaed
Barchart race example for ArtistAnimation
chahak13 46e691d
Change FuncAnimation to have one general guideline
chahak13 5f4e555
Add a table for set methods for example artists
chahak13 8fba320
Add plotting method, return artist type table
chahak13 67c4f53
Reword to avoid confusion
chahak13 85913db
Reposition note about pipe based writers
chahak13 57ae9ed
Change color and don't sort bar chart
chahak13 b8e0e1d
Remove animation code in save animation section
chahak13 abbb031
Change inline codeblocks to clear CI
chahak13 c0ea38d
Make small corrections as per code review
chahak13 cbef186
Remove :class: from links
chahak13 1def505
Set the animation range so that it loops
chahak13 b5af4e6
Update to better animation range for scatter
chahak13 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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*. |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.