Skip to content

Commit a15d212

Browse files
committed
Update tutorial and scatter example
1 parent 986e8ce commit a15d212

File tree

1 file changed

+66
-72
lines changed

1 file changed

+66
-72
lines changed

tutorials/introductory/animation_tutorial.py

Lines changed: 66 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,16 @@
2020
#
2121
# The process of animation can be thought about in 2 different ways:
2222
#
23-
# 1. Generate data for first frame and then modify this data for each frame to
24-
# create an animated plot.
25-
# 2. Generate a list (iterable) of plots (/images) that will each be a frame in
26-
# the animation.
23+
# - :class:`~matplotlib.animation.FuncAnimation`: Generate data for first
24+
# frame and then modify this data for each frame to create an animated plot.
2725
#
28-
# Both of these thought processes can be translated into matplotlib using the
29-
# :class:`~matplotlib.animation`'s :class:`~matplotlib.animation.FuncAnimation`
30-
# and :class:`~matplotlib.animation.ArtistAnimation` classes respectively.
26+
# - :class:`~matplotlib.animation.FuncAnimation`: Generate a list (iterable)
27+
# of artists that will draw in each frame in the animation.
28+
#
29+
# :class:`~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 :class:`~matplotlib.animation.ArtistAnimation` is flexible as it
32+
# allows any iterable of artists to be animated in a sequence.
3133
#
3234
# :class:`~matplotlib.animation.FuncAnimation`
3335
# --------------------------------------------
@@ -43,13 +45,12 @@
4345
# *func* that modifies the data plotted on the figure. It uses the *frames*
4446
# parameter to determine the length of the animation. The *interval* parameter
4547
# is used to determine time in milliseconds between drawing of two frames.
46-
# (This is different than *fps* in `.animation.save` that we will
47-
# cover later). We will now look at some examples of using
48+
# We will now look at some examples of using
4849
# :class:`~matplotlib.animation.FuncAnimation` with different artists.
4950

5051
###############################################################################
5152
# Animating :class:`~matplotlib.lines.Line2D`
52-
# ---------------------------------------------------------
53+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5354
#
5455
# `.pyplot.plot` returns a :class:`~matplotlib.lines.Line2D` collection. The
5556
# data on this collection can be modified by using the
@@ -60,62 +61,62 @@
6061
rng = np.random.default_rng()
6162

6263
xdata, ydata = [], []
63-
line, = ax.plot(xdata, ydata, c='b')
64+
(line,) = ax.plot(xdata, ydata, c="b")
6465
ax.grid()
6566
ax.set_ylim(-1, 1)
67+
ax.set_xlim(0, 10)
6668

6769

6870
def update(frame):
69-
xdata.append(frame)
71+
# .set_data resets all the data for the line, so we add the new point to
72+
# the existing line data and set that again.
73+
xdata.append(frame / 30)
7074
ydata.append(np.sin(2 * np.pi * frame / 30))
71-
xmin, xmax = ax.get_xlim()
7275

73-
if frame >= xmax:
74-
ax.set_xlim(xmin, 2*xmax)
75-
ax.figure.canvas.draw()
7676
line.set_data(xdata, ydata)
77-
return line,
77+
return (line,)
7878

79-
ani = animation.FuncAnimation(
80-
fig=fig, func=update, interval=30
81-
)
79+
80+
ani = animation.FuncAnimation(fig=fig, func=update, interval=30)
8281
plt.show()
8382

8483
###############################################################################
8584
# Animating :class:`~matplotlib.collections.PathCollection`
86-
# ---------------------------------------------------------
85+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
8786
#
8887
# `.pyplot.scatter` returns a :class:`~matplotlib.collections.PathCollection`
8988
# that can similarly be modified by using the
9089
# `.collections.PathCollection.set_offsets` function.
9190

9291
fig, ax = plt.subplots()
9392
rng = np.random.default_rng()
93+
t = np.linspace(-4, 4, 1000)
94+
a, b = 3, 2
95+
delta = np.pi / 2
9496

95-
scat = ax.scatter(
96-
rng.uniform(low=0, high=1, size=100),
97-
rng.uniform(low=0, high=1, size=100),
98-
c='b'
99-
)
97+
scat = ax.scatter(np.sin(a * t[0] + delta), np.sin(b * t[0]), c="b", s=2)
10098
ax.grid()
99+
ax.set_xlim(-1, 1)
100+
ax.set_ylim(-1, 1)
101101

102102

103103
def update(frame):
104-
x = rng.uniform(low=0, high=1, size=100)
105-
y = rng.uniform(low=0, high=1, size=100)
104+
# .set_offsets also resets the entire data for the collection.
105+
# Therefore, we create the entire data in each frame to draw
106+
x = np.sin(a * t[:frame] + delta)
107+
y = np.sin(b * t[:frame])
106108
data = np.stack([x, y]).T
107109
scat.set_offsets(data)
108-
return scat,
110+
return (scat,)
109111

110-
ani = animation.FuncAnimation(
111-
fig=fig, func=update, frames=240, interval=300
112-
)
112+
113+
ani = animation.FuncAnimation(fig=fig, func=update, interval=30)
113114
plt.show()
114115

115116

116117
###############################################################################
117118
# Animating :class:`~matplotlib.image.AxesImage`
118-
# ---------------------------------------------------
119+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
119120
#
120121
# When we plot an image using `.pyplot.imshow`, it returns an
121122
# :class:`~matplotlib.image.AxesImage` object. The data in this object can also
@@ -130,11 +131,10 @@ def update(frame):
130131
def update(frame):
131132
data = rng.uniform(low=0, high=1, size=(10, 10))
132133
aximg.set_data(data)
133-
return aximg,
134+
return (aximg,)
134135

135-
ani = animation.FuncAnimation(
136-
fig=fig, func=update, frames=None, interval=200
137-
)
136+
137+
ani = animation.FuncAnimation(fig=fig, func=update, frames=None, interval=200)
138138
plt.show()
139139

140140
###############################################################################
@@ -153,49 +153,56 @@ def update(frame):
153153
x_frames = rng.uniform(low=0, high=1, size=(100, 120))
154154
y_frames = rng.uniform(low=0, high=1, size=(100, 120))
155155
artists = [
156-
[
157-
ax.scatter(x_frames[:, i], y_frames[:, i], c="b")
158-
]
156+
[ax.scatter(x_frames[:, i], y_frames[:, i], c="b")]
159157
for i in range(x_frames.shape[-1])
160158
]
161159

162160
ani = animation.ArtistAnimation(fig=fig, artists=artists, repeat_delay=1000)
163161
plt.show()
164162

165-
###############################################################################
166-
# :class:`~matplotlib.animation.FuncAnimation` is more efficient in terms of
167-
# speed and memory as it draws an artist once and then modifies it. On the
168-
# other hand :class:`~matplotlib.animation.ArtistAnimation` is flexible as it
169-
# allows any iterable of artists to be animated in a sequence.
170-
171163
###############################################################################
172164
# Animation Writers
173-
# -----------------
165+
# =================
174166
#
175167
# Animation objects can be saved to disk using various multimedia writers
176168
# (ex: Pillow, *ffpmeg*, *imagemagick*). Not all video formats are supported
177-
# by all writers. A list of supported formats for each writer can be found at
178-
# the end of this tutorial. There are 4 major types of writers:
169+
# by all writers. There are 4 major types of writers:
179170
#
180-
# 1. :class:`~matplotlib.animation.PillowWriter` - Uses the Pillow library to
171+
# - :class:`~matplotlib.animation.PillowWriter` - Uses the Pillow library to
181172
# create the animation.
182173
#
183-
# 2. :class:`~matplotlib.animation.HTMLWriter` - Used to create JS-based
174+
# - :class:`~matplotlib.animation.HTMLWriter` - Used to create JS-based
184175
# animations.
185176
#
186-
# 3. Pipe-based writers - :class:`~matplotlib.animation.FFMpegWriter` and
177+
# - Pipe-based writers - :class:`~matplotlib.animation.FFMpegWriter` and
187178
# :class:`~matplotlib.animation.ImageMagickWriter` are pipe based writers.
188179
# These writers pipe each frame to the utility (*ffmpeg* / *imagemagick*) which
189180
# then stitches all of them together to create the animation.
190181
#
191-
# 4. File-based writers - :class:`~matplotlib.animation.FFMpegFileWriter` and
182+
# - File-based writers - :class:`~matplotlib.animation.FFMpegFileWriter` and
192183
# :class:`~matplotlib.animation.ImageMagickFileWriter` are examples of
193184
# file-based writers. These writers are slower than their standard writers but
194185
# are more useful for debugging as they save each frame in a file before
195186
# stitching them together into an animation.
196187
#
197-
# Below we see examples for how to use different writers with
198-
# `.animation.Animation.save`
188+
# ================================================ ===========================
189+
# Writer Supported Formats
190+
# ================================================ ===========================
191+
# :class:`~matplotlib.animation.PillowWriter` .gif, .apng
192+
# :class:`~matplotlib.animation.HTMLWriter` .htm, .html, .png
193+
# :class:`~matplotlib.animation.FFMpegWriter` All formats supported by
194+
# *ffmpeg*
195+
# :class:`~matplotlib.animation.ImageMagickWriter` .gif
196+
# ================================================ ===========================
197+
#
198+
# To save animations using any of the writers, we can use the
199+
# `.animation.Animation.save` method. It takes the *filename* that we want to
200+
# save the animation as and the *writer*, which is either a string or a writer
201+
# object. It also takes an *fps* argument. This argument is different than the
202+
# *interval* argument that `~.animation.FuncAnimation` or
203+
# `~.animation.ArtistAnimation` uses. *fps* determines the frame rate that the
204+
# **saved** animation uses, whereas *interval* determines the frame rate that
205+
# the **displayed** animation uses.
199206

200207
fig, ax = plt.subplots()
201208
ax.grid()
@@ -204,7 +211,7 @@ def update(frame):
204211
scat = ax.scatter(
205212
rng.uniform(low=0, high=1, size=100),
206213
rng.uniform(low=0, high=1, size=100),
207-
c='b'
214+
c="b"
208215
)
209216

210217

@@ -213,11 +220,10 @@ def update(frame):
213220
y = rng.uniform(low=0, high=1, size=100)
214221
data = np.stack([x, y]).T
215222
scat.set_offsets(data)
216-
return scat,
223+
return (scat,)
217224

218-
ani = animation.FuncAnimation(
219-
fig=fig, func=update, frames=240, interval=200
220-
)
225+
226+
ani = animation.FuncAnimation(fig=fig, func=update, frames=240, interval=200)
221227
# ani.save(filename="/tmp/pillow_example.gif", writer="pillow")
222228
# ani.save(filename="/tmp/pillow_example.apng", writer="pillow")
223229

@@ -233,15 +239,3 @@ def update(frame):
233239

234240
# Imagemagick
235241
# ani.save(filename="/tmp/imagemagick_example.gif", writer="imagemagick")
236-
237-
###############################################################################
238-
#
239-
# ================================================ ===========================
240-
# Writer Supported Formats
241-
# ================================================ ===========================
242-
# :class:`~matplotlib.animation.PillowWriter` .gif, .apng
243-
# :class:`~matplotlib.animation.HTMLWriter` .htm, .html, .png
244-
# :class:`~matplotlib.animation.FFMpegWriter` All formats supported by
245-
# *ffmpeg*
246-
# :class:`~matplotlib.animation.ImageMagickWriter` .gif
247-
# ================================================ ===========================

0 commit comments

Comments
 (0)