20
20
#
21
21
# The process of animation can be thought about in 2 different ways:
22
22
#
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.
27
25
#
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.
31
33
#
32
34
# :class:`~matplotlib.animation.FuncAnimation`
33
35
# --------------------------------------------
43
45
# *func* that modifies the data plotted on the figure. It uses the *frames*
44
46
# parameter to determine the length of the animation. The *interval* parameter
45
47
# 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
48
49
# :class:`~matplotlib.animation.FuncAnimation` with different artists.
49
50
50
51
###############################################################################
51
52
# Animating :class:`~matplotlib.lines.Line2D`
52
- # ---------------------------------------------------------
53
+ # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
53
54
#
54
55
# `.pyplot.plot` returns a :class:`~matplotlib.lines.Line2D` collection. The
55
56
# data on this collection can be modified by using the
60
61
rng = np .random .default_rng ()
61
62
62
63
xdata , ydata = [], []
63
- line , = ax .plot (xdata , ydata , c = 'b' )
64
+ ( line ,) = ax .plot (xdata , ydata , c = "b" )
64
65
ax .grid ()
65
66
ax .set_ylim (- 1 , 1 )
67
+ ax .set_xlim (0 , 10 )
66
68
67
69
68
70
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 )
70
74
ydata .append (np .sin (2 * np .pi * frame / 30 ))
71
- xmin , xmax = ax .get_xlim ()
72
75
73
- if frame >= xmax :
74
- ax .set_xlim (xmin , 2 * xmax )
75
- ax .figure .canvas .draw ()
76
76
line .set_data (xdata , ydata )
77
- return line ,
77
+ return ( line ,)
78
78
79
- ani = animation .FuncAnimation (
80
- fig = fig , func = update , interval = 30
81
- )
79
+
80
+ ani = animation .FuncAnimation (fig = fig , func = update , interval = 30 )
82
81
plt .show ()
83
82
84
83
###############################################################################
85
84
# Animating :class:`~matplotlib.collections.PathCollection`
86
- # ---------------------------------------------------------
85
+ # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
87
86
#
88
87
# `.pyplot.scatter` returns a :class:`~matplotlib.collections.PathCollection`
89
88
# that can similarly be modified by using the
90
89
# `.collections.PathCollection.set_offsets` function.
91
90
92
91
fig , ax = plt .subplots ()
93
92
rng = np .random .default_rng ()
93
+ t = np .linspace (- 4 , 4 , 1000 )
94
+ a , b = 3 , 2
95
+ delta = np .pi / 2
94
96
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 )
100
98
ax .grid ()
99
+ ax .set_xlim (- 1 , 1 )
100
+ ax .set_ylim (- 1 , 1 )
101
101
102
102
103
103
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 ])
106
108
data = np .stack ([x , y ]).T
107
109
scat .set_offsets (data )
108
- return scat ,
110
+ return ( scat ,)
109
111
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 )
113
114
plt .show ()
114
115
115
116
116
117
###############################################################################
117
118
# Animating :class:`~matplotlib.image.AxesImage`
118
- # ---------------------------------------------------
119
+ # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
119
120
#
120
121
# When we plot an image using `.pyplot.imshow`, it returns an
121
122
# :class:`~matplotlib.image.AxesImage` object. The data in this object can also
@@ -130,11 +131,10 @@ def update(frame):
130
131
def update (frame ):
131
132
data = rng .uniform (low = 0 , high = 1 , size = (10 , 10 ))
132
133
aximg .set_data (data )
133
- return aximg ,
134
+ return ( aximg ,)
134
135
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 )
138
138
plt .show ()
139
139
140
140
###############################################################################
@@ -153,49 +153,56 @@ def update(frame):
153
153
x_frames = rng .uniform (low = 0 , high = 1 , size = (100 , 120 ))
154
154
y_frames = rng .uniform (low = 0 , high = 1 , size = (100 , 120 ))
155
155
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" )]
159
157
for i in range (x_frames .shape [- 1 ])
160
158
]
161
159
162
160
ani = animation .ArtistAnimation (fig = fig , artists = artists , repeat_delay = 1000 )
163
161
plt .show ()
164
162
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
-
171
163
###############################################################################
172
164
# Animation Writers
173
- # -----------------
165
+ # =================
174
166
#
175
167
# Animation objects can be saved to disk using various multimedia writers
176
168
# (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:
179
170
#
180
- # 1. :class:`~matplotlib.animation.PillowWriter` - Uses the Pillow library to
171
+ # - :class:`~matplotlib.animation.PillowWriter` - Uses the Pillow library to
181
172
# create the animation.
182
173
#
183
- # 2. :class:`~matplotlib.animation.HTMLWriter` - Used to create JS-based
174
+ # - :class:`~matplotlib.animation.HTMLWriter` - Used to create JS-based
184
175
# animations.
185
176
#
186
- # 3. Pipe-based writers - :class:`~matplotlib.animation.FFMpegWriter` and
177
+ # - Pipe-based writers - :class:`~matplotlib.animation.FFMpegWriter` and
187
178
# :class:`~matplotlib.animation.ImageMagickWriter` are pipe based writers.
188
179
# These writers pipe each frame to the utility (*ffmpeg* / *imagemagick*) which
189
180
# then stitches all of them together to create the animation.
190
181
#
191
- # 4. File-based writers - :class:`~matplotlib.animation.FFMpegFileWriter` and
182
+ # - File-based writers - :class:`~matplotlib.animation.FFMpegFileWriter` and
192
183
# :class:`~matplotlib.animation.ImageMagickFileWriter` are examples of
193
184
# file-based writers. These writers are slower than their standard writers but
194
185
# are more useful for debugging as they save each frame in a file before
195
186
# stitching them together into an animation.
196
187
#
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.
199
206
200
207
fig , ax = plt .subplots ()
201
208
ax .grid ()
@@ -204,7 +211,7 @@ def update(frame):
204
211
scat = ax .scatter (
205
212
rng .uniform (low = 0 , high = 1 , size = 100 ),
206
213
rng .uniform (low = 0 , high = 1 , size = 100 ),
207
- c = 'b'
214
+ c = "b"
208
215
)
209
216
210
217
@@ -213,11 +220,10 @@ def update(frame):
213
220
y = rng .uniform (low = 0 , high = 1 , size = 100 )
214
221
data = np .stack ([x , y ]).T
215
222
scat .set_offsets (data )
216
- return scat ,
223
+ return ( scat ,)
217
224
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 )
221
227
# ani.save(filename="/tmp/pillow_example.gif", writer="pillow")
222
228
# ani.save(filename="/tmp/pillow_example.apng", writer="pillow")
223
229
@@ -233,15 +239,3 @@ def update(frame):
233
239
234
240
# Imagemagick
235
241
# 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