Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit de58d98

Browse files
authored
Add an Animation Tutorial (#24274)
* First version of animation tutorial * Simplify examples by removing data gen functions * Add tutorial text * Format to agree with flake8 * Update tutorial and scatter example * Fix example for line animation The animation for Lines2D was not showing the animation properly but was showing just the final frame. Adding an init function resolved this issue and the animation appears correctly now. * Change to simpler line animation example * Change headings and update set_offsets description * Change writer type to pipe-based from standard * Update saving animation section * Add new example for images and ArtistAnimation * Remove unused variables * Correct type of animation class * Remove unused variable * Correct wrong correction * Update ArtistAnimation example to use bar chart * Barchart race example for ArtistAnimation * Change FuncAnimation to have one general guideline * Add a table for set methods for example artists * Add plotting method, return artist type table * Reword to avoid confusion * Reposition note about pipe based writers * Change color and don't sort bar chart * Remove animation code in save animation section * Change inline codeblocks to clear CI * Make small corrections as per code review * Remove :class: from links * Set the animation range so that it loops * Update to better animation range for scatter
1 parent d13065b commit de58d98

File tree

1 file changed

+224
-0
lines changed

1 file changed

+224
-0
lines changed
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
"""
2+
===========================
3+
Animations using Matplotlib
4+
===========================
5+
6+
Based on its plotting functionality, Matplotlib also provides an interface to
7+
generate animations using the `~matplotlib.animation` module. An
8+
animation is a sequence of frames where each frame corresponds to a plot on a
9+
`~matplotlib.figure.Figure`. This tutorial covers a general guideline on
10+
how to create such animations and the different options available.
11+
"""
12+
13+
import matplotlib.pyplot as plt
14+
import matplotlib.animation as animation
15+
import numpy as np
16+
17+
###############################################################################
18+
# Animation Classes
19+
# =================
20+
#
21+
# The animation process in Matplotlib can be thought of in 2 different ways:
22+
#
23+
# - `~matplotlib.animation.FuncAnimation`: Generate data for first
24+
# frame and then modify this data for each frame to create an animated plot.
25+
#
26+
# - `~matplotlib.animation.ArtistAnimation`: Generate a list (iterable)
27+
# of artists that will draw in each frame in the animation.
28+
#
29+
# `~matplotlib.animation.FuncAnimation` is more efficient in terms of
30+
# speed and memory as it draws an artist once and then modifies it. On the
31+
# other hand `~matplotlib.animation.ArtistAnimation` is flexible as it
32+
# allows any iterable of artists to be animated in a sequence.
33+
#
34+
# ``FuncAnimation``
35+
# -----------------
36+
#
37+
# The `~matplotlib.animation.FuncAnimation` class allows us to create an
38+
# animation by passing a function that iteratively modifies the data of a plot.
39+
# This is achieved by using the *setter* methods on various
40+
# `~matplotlib.artist.Artist` (examples: `~matplotlib.lines.Line2D`,
41+
# `~matplotlib.collections.PathCollection`, etc.). A usual
42+
# `~matplotlib.animation.FuncAnimation` object takes a
43+
# `~matplotlib.figure.Figure` that we want to animate and a function
44+
# *func* that modifies the data plotted on the figure. It uses the *frames*
45+
# parameter to determine the length of the animation. The *interval* parameter
46+
# is used to determine time in milliseconds between drawing of two frames.
47+
# Animating using `.FuncAnimation` would usually follow the following
48+
# structure:
49+
#
50+
# - Plot the initial figure, including all the required artists. Save all the
51+
# artists in variables so that they can be updated later on during the
52+
# animation.
53+
# - Create an animation function that updates the data in each artist to
54+
# generate the new frame at each function call.
55+
# - Create a `.FuncAnimation` object with the `.Figure` and the animation
56+
# function, along with the keyword arguments that determine the animation
57+
# properties.
58+
# - Use `.animation.Animation.save` or `.pyplot.show` to save or show the
59+
# animation.
60+
#
61+
# The update function uses the ``set_*`` function for different artists to
62+
# modify the data. The following table shows a few plotting methods, the artist
63+
# types they return and some methods that can be used to update them.
64+
#
65+
# ================= ============================= ===========================
66+
# Plotting method Artist Set method
67+
# ================= ============================= ===========================
68+
# `.Axes.plot` `.lines.Line2D` `.lines.Line2D.set_data`
69+
# `.Axes.scatter` `.collections.PathCollection` `.collections.\
70+
# PathCollection.set_offsets`
71+
# `.Axes.imshow` `.image.AxesImage` ``AxesImage.set_data``
72+
# `.Axes.annotate` `.text.Annotation` `.text.Annotation.\
73+
# update_positions`
74+
# `.Axes.barh` `.patches.Rectangle` `.Rectangle.set_angle`,
75+
# `.Rectangle.set_bounds`,
76+
# `.Rectangle.set_height`,
77+
# `.Rectangle.set_width`,
78+
# `.Rectangle.set_x`,
79+
# `.Rectangle.set_y`
80+
# `.Rectangle.set_xy`
81+
# `.Axes.fill` `.patches.Polygon` `.Polygon.set_xy`
82+
# `.patches.Circle` `.patches.Ellipse` `.Ellipse.set_angle`,
83+
# `.Ellipse.set_center`,
84+
# `.Ellipse.set_height`,
85+
# `.Ellipse.set_width`
86+
# ================= ============================= ===========================
87+
#
88+
# Covering the set methods for all types of artists is beyond the scope of this
89+
# tutorial but can be found in their respective documentations. An example of
90+
# such update methods in use for `.Axes.scatter` is as follows.
91+
92+
93+
fig, ax = plt.subplots()
94+
t = np.linspace(-np.pi, np.pi, 400)
95+
a, b = 3, 2
96+
delta = np.pi / 2
97+
98+
scat = ax.scatter(np.sin(a * t[0] + delta), np.sin(b * t[0]), c="b", s=2)
99+
ax.set_xlim(-1.5, 1.5)
100+
ax.set_ylim(-1.5, 1.5)
101+
102+
103+
def update(frame):
104+
# .set_offsets replaces the offset data for the entire collection with
105+
# the new values. Therefore, to also carry forward the previously
106+
# calculated information, we use the data from the first to the current
107+
# frame to set the new offsets.
108+
x = np.sin(a * t[:frame] + delta)
109+
y = np.sin(b * t[:frame])
110+
data = np.stack([x, y]).T
111+
scat.set_offsets(data)
112+
return (scat,)
113+
114+
115+
ani = animation.FuncAnimation(fig=fig, func=update, frames=400, interval=30)
116+
plt.show()
117+
118+
119+
###############################################################################
120+
# ``ArtistAnimation``
121+
# -------------------
122+
#
123+
# `~matplotlib.animation.ArtistAnimation` can be used
124+
# to generate animations if there is data stored on various different artists.
125+
# This list of artists is then converted frame by frame into an animation. For
126+
# example, when we use `.Axes.barh` to plot a bar-chart, it creates a number of
127+
# artists for each of the bar and error bars. To update the plot, one would
128+
# need to update each of the bars from the container individually and redraw
129+
# them. Instead, `.animation.ArtistAnimation` can be used to plot each frame
130+
# individually and then stitched together to form an animation. A barchart race
131+
# is a simple example for this.
132+
133+
134+
fig, ax = plt.subplots()
135+
rng = np.random.default_rng(19680801)
136+
data = np.array([20, 20, 20, 20])
137+
x = np.array([1, 2, 3, 4])
138+
139+
artists = []
140+
colors = ['tab:blue', 'tab:red', 'tab:green', 'tab:purple']
141+
for i in range(20):
142+
data += rng.integers(low=0, high=10, size=data.shape)
143+
container = ax.barh(x, data, color=colors)
144+
artists.append(container)
145+
146+
147+
ani = animation.ArtistAnimation(fig=fig, artists=artists, interval=400)
148+
plt.show()
149+
150+
###############################################################################
151+
# Animation Writers
152+
# =================
153+
#
154+
# Animation objects can be saved to disk using various multimedia writers
155+
# (ex: Pillow, *ffpmeg*, *imagemagick*). Not all video formats are supported
156+
# by all writers. There are 4 major types of writers:
157+
#
158+
# - `~matplotlib.animation.PillowWriter` - Uses the Pillow library to
159+
# create the animation.
160+
#
161+
# - `~matplotlib.animation.HTMLWriter` - Used to create JavaScript-based
162+
# animations.
163+
#
164+
# - Pipe-based writers - `~matplotlib.animation.FFMpegWriter` and
165+
# `~matplotlib.animation.ImageMagickWriter` are pipe based writers.
166+
# These writers pipe each frame to the utility (*ffmpeg* / *imagemagick*)
167+
# which then stitches all of them together to create the animation.
168+
#
169+
# - File-based writers - `~matplotlib.animation.FFMpegFileWriter` and
170+
# `~matplotlib.animation.ImageMagickFileWriter` are examples of
171+
# file-based writers. These writers are slower than their pipe-based
172+
# alternatives but are more useful for debugging as they save each frame in
173+
# a file before stitching them together into an animation.
174+
#
175+
# Saving Animations
176+
# -----------------
177+
#
178+
# ========================================== ===========================
179+
# Writer Supported Formats
180+
# ========================================== ===========================
181+
# `~matplotlib.animation.PillowWriter` .gif, .apng
182+
# `~matplotlib.animation.HTMLWriter` .htm, .html, .png
183+
# `~matplotlib.animation.FFMpegWriter` All formats supported by
184+
# *ffmpeg*
185+
# `~matplotlib.animation.ImageMagickWriter` .gif
186+
# ========================================== ===========================
187+
#
188+
# To save animations using any of the writers, we can use the
189+
# `.animation.Animation.save` method. It takes the *filename* that we want to
190+
# save the animation as and the *writer*, which is either a string or a writer
191+
# object. It also takes an *fps* argument. This argument is different than the
192+
# *interval* argument that `~.animation.FuncAnimation` or
193+
# `~.animation.ArtistAnimation` uses. *fps* determines the frame rate that the
194+
# **saved** animation uses, whereas *interval* determines the frame rate that
195+
# the **displayed** animation uses.
196+
#
197+
# Below are a few examples that show how to save an animation with different
198+
# writers.
199+
#
200+
#
201+
# Pillow writers::
202+
#
203+
# ani.save(filename="/tmp/pillow_example.gif", writer="pillow")
204+
# ani.save(filename="/tmp/pillow_example.apng", writer="pillow")
205+
#
206+
# HTML writers::
207+
#
208+
# ani.save(filename="/tmp/html_example.html", writer="html")
209+
# ani.save(filename="/tmp/html_example.htm", writer="html")
210+
# ani.save(filename="/tmp/html_example.png", writer="html")
211+
#
212+
# FFMpegWriter::
213+
#
214+
# ani.save(filename="/tmp/ffmpeg_example.mkv", writer="ffmpeg")
215+
# ani.save(filename="/tmp/ffmpeg_example.mp4", writer="ffmpeg")
216+
# ani.save(filename="/tmp/ffmpeg_example.mjpeg", writer="ffmpeg")
217+
#
218+
# Imagemagick writers::
219+
#
220+
# ani.save(filename="/tmp/imagemagick_example.gif", writer="imagemagick")
221+
#
222+
# Since the frames are piped out to *ffmpeg* or *imagemagick*,
223+
# ``writer="ffmpeg"`` and ``writer="imagemagick"`` support all formats
224+
# supported by *ffmpeg* and *imagemagick*.

0 commit comments

Comments
 (0)