From fcc5f305f5a3b5898263175285151be33902f51a Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Tue, 25 Oct 2022 15:50:31 -0500 Subject: [PATCH 01/29] First version of animation tutorial --- tutorials/introductory/animation_tutorial.py | 196 +++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 tutorials/introductory/animation_tutorial.py diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py new file mode 100644 index 000000000000..04b0f84ac5ae --- /dev/null +++ b/tutorials/introductory/animation_tutorial.py @@ -0,0 +1,196 @@ +""" +=========================== +Animations using matplotlib +=========================== + +Animations in matplotlib can be created using the `~matplotlib.Animation` +module. This module provides 2 main animation classes - +`~matplotlib.animation.FuncAnimation` and +`~matplotlib.animation.ArtistAnimation`. +""" + +import matplotlib.pyplot as plt +import matplotlib.animation as animation +import numpy as np + +############################################################################### +# :class:`~matplotlib.animation.FuncAnimation` +# -------------------------------------------- + +# :class:`~matplotlib.animation.FuncAnimation` uses a user-provided function by +# repeatedly calling the function with at a regular *interval*. This allows +# dynamic generation of data by using generators. +import itertools + + +def data_gen(): + for cnt in itertools.count(): + t = cnt/10 + yield t, np.sin(2 * np.pi * t) + + +def init(): + ax.set_ylim(-1, 1) + ax.set_xlim(0, 10) + xdata.clear() + ydata.clear() + line.set_data(xdata, ydata) + return line, + +fig, ax = plt.subplots() +line, = ax.plot([], [], lw=2) +ax.grid() +xdata, ydata = [], [] + + +def update(data): + t, y = data + xdata.append(t) + ydata.append(y) + xmin, xmax = ax.get_xlim() + + if t >= xmax: + ax.set_xlim(xmin, 2*xmax) + ax.figure.canvas.draw() + line.set_data(xdata, ydata) + + return line, + +ani = animation.FuncAnimation( + fig=fig, func=update, frames=data_gen, init_func=init, interval=20 +) +plt.show() + +############################################################################### +# Animating :class:`~matplotlib.lines.PathCollection` +# --------------------------------------------------- + +fig, ax = plt.subplots() +rng = np.random.default_rng() + +scat = ax.scatter( + rng.uniform(low=0, high=1, size=100), + rng.uniform(low=0, high=1, size=100), + c='b' +) + + +def data_gen(): + for cnt in itertools.count(): + x, y = ( + rng.uniform(low=0, high=1, size=100), + rng.uniform(low=0, high=1, size=100) + ) + yield cnt, x, y + + +def update(inputs): + frame, x, y = inputs + data = np.stack([x, y]).T + scat.set_offsets(data) + return scat, + +ani = animation.FuncAnimation( + fig=fig, func=update, frames=data_gen, interval=200 +) +plt.show() + + +############################################################################### +# Animating :class:`~matplotlib.image.AxesImage` +# --------------------------------------------------- + +fig, ax = plt.subplots() +rng = np.random.default_rng() + +aximg = ax.imshow(rng.uniform(low=0, high=1, size=(100, 100))) + + +def data_gen(): + for cnt in itertools.count(): + x = rng.uniform(low=0, high=1, size=(100, 100)) + yield cnt, x + + +def update(inputs): + frame, x = inputs + aximg.set_data(x) + return aximg, + +ani = animation.FuncAnimation( + fig=fig, func=update, frames=data_gen, interval=200 +) +plt.show() + +############################################################################### +# :class:`~matplotlib.animation.ArtistAnimation` +# ---------------------------------------------- +# +# :class:`~matplotlib.animation.ArtistAnimation` uses a list of artists to +# iterate over and animate. + + +fig, ax = plt.subplots() +rng = np.random.default_rng() + +x_frames = rng.uniform(low=0, high=1, size=(100, 120)) +y_frames = rng.uniform(low=0, high=1, size=(100, 120)) +artists = [ + [ + ax.scatter(x_frames[:, i], y_frames[:, i]) + ] + for i in range(x_frames.shape[-1]) +] + +ani = animation.ArtistAnimation(fig=fig, artists=artists, repeat_delay=1000) +plt.show() + +############################################################################### +# Animation Writers +# ------------------------------------------- + +fig, ax = plt.subplots() +rng = np.random.default_rng() + +scat = ax.scatter( + rng.uniform(low=0, high=1, size=100), + rng.uniform(low=0, high=1, size=100), + c='b' +) + + +def data_gen(): + for cnt in itertools.count(): + x, y = ( + rng.uniform(low=0, high=1, size=100), + rng.uniform(low=0, high=1, size=100) + ) + yield cnt, x, y + + +def update(inputs): + frame, x, y = inputs + data = np.stack([x, y]).T + scat.set_offsets(data) + return scat, + +ani = animation.FuncAnimation( + fig=fig, func=update, frames=data_gen, interval=200 +) +# ani.save(filename="/tmp/pillow_example.gif", writer="pillow") +# ani.save(filename="/tmp/pillow_example.apng", writer="pillow") + +# Why does HTMLWriter documentation have a variable named +# supported_formats = ['png', 'jpeg', 'tiff', 'svg'] +# 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") + +# Since the frames are piped out to ffmpeg, this supports all formats +# supported by ffmpeg +# 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 +# ani.save(filename="/tmp/imagemagick_example.gif", writer="imagemagick") From 88e3bcc86ee34dcd6a58a3f13ddc20f4282d5957 Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Thu, 27 Oct 2022 12:22:36 -0500 Subject: [PATCH 02/29] Simplify examples by removing data gen functions --- tutorials/introductory/animation_tutorial.py | 71 ++++++-------------- 1 file changed, 22 insertions(+), 49 deletions(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index 04b0f84ac5ae..fc7ac968b405 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -20,13 +20,6 @@ # :class:`~matplotlib.animation.FuncAnimation` uses a user-provided function by # repeatedly calling the function with at a regular *interval*. This allows # dynamic generation of data by using generators. -import itertools - - -def data_gen(): - for cnt in itertools.count(): - t = cnt/10 - yield t, np.sin(2 * np.pi * t) def init(): @@ -43,13 +36,12 @@ def init(): xdata, ydata = [], [] -def update(data): - t, y = data - xdata.append(t) - ydata.append(y) +def update(frame): + xdata.append(frame) + ydata.append(np.sin(2 * np.pi * frame / 30)) xmin, xmax = ax.get_xlim() - if t >= xmax: + if frame >= xmax: ax.set_xlim(xmin, 2*xmax) ax.figure.canvas.draw() line.set_data(xdata, ydata) @@ -57,7 +49,7 @@ def update(data): return line, ani = animation.FuncAnimation( - fig=fig, func=update, frames=data_gen, init_func=init, interval=20 + fig=fig, func=update, frames=240, init_func=init, interval=30 ) plt.show() @@ -73,25 +65,18 @@ def update(data): rng.uniform(low=0, high=1, size=100), c='b' ) +ax.grid() -def data_gen(): - for cnt in itertools.count(): - x, y = ( - rng.uniform(low=0, high=1, size=100), - rng.uniform(low=0, high=1, size=100) - ) - yield cnt, x, y - - -def update(inputs): - frame, x, y = inputs +def update(frame): + x = rng.uniform(low=0, high=1, size=100) + y = rng.uniform(low=0, high=1, size=100) data = np.stack([x, y]).T scat.set_offsets(data) return scat, ani = animation.FuncAnimation( - fig=fig, func=update, frames=data_gen, interval=200 + fig=fig, func=update, frames=240, interval=300 ) plt.show() @@ -103,22 +88,16 @@ def update(inputs): fig, ax = plt.subplots() rng = np.random.default_rng() -aximg = ax.imshow(rng.uniform(low=0, high=1, size=(100, 100))) - +aximg = ax.imshow(rng.uniform(low=0, high=1, size=(10, 10)), cmap="Blues") -def data_gen(): - for cnt in itertools.count(): - x = rng.uniform(low=0, high=1, size=(100, 100)) - yield cnt, x - -def update(inputs): - frame, x = inputs - aximg.set_data(x) +def update(frame): + data = rng.uniform(low=0, high=1, size=(10, 10)) + aximg.set_data(data) return aximg, ani = animation.FuncAnimation( - fig=fig, func=update, frames=data_gen, interval=200 + fig=fig, func=update, frames=240, interval=200 ) plt.show() @@ -131,13 +110,14 @@ def update(inputs): fig, ax = plt.subplots() +ax.grid() rng = np.random.default_rng() x_frames = rng.uniform(low=0, high=1, size=(100, 120)) y_frames = rng.uniform(low=0, high=1, size=(100, 120)) artists = [ [ - ax.scatter(x_frames[:, i], y_frames[:, i]) + ax.scatter(x_frames[:, i], y_frames[:, i], c="b") ] for i in range(x_frames.shape[-1]) ] @@ -150,6 +130,7 @@ def update(inputs): # ------------------------------------------- fig, ax = plt.subplots() +ax.grid() rng = np.random.default_rng() scat = ax.scatter( @@ -159,23 +140,15 @@ def update(inputs): ) -def data_gen(): - for cnt in itertools.count(): - x, y = ( - rng.uniform(low=0, high=1, size=100), - rng.uniform(low=0, high=1, size=100) - ) - yield cnt, x, y - - -def update(inputs): - frame, x, y = inputs +def update(frame): + x = rng.uniform(low=0, high=1, size=100) + y = rng.uniform(low=0, high=1, size=100) data = np.stack([x, y]).T scat.set_offsets(data) return scat, ani = animation.FuncAnimation( - fig=fig, func=update, frames=data_gen, interval=200 + fig=fig, func=update, frames=240, interval=200 ) # ani.save(filename="/tmp/pillow_example.gif", writer="pillow") # ani.save(filename="/tmp/pillow_example.apng", writer="pillow") From 76270ac1d6f80ce98f05d22dd4dc3fc29e3c6dfb Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Fri, 28 Oct 2022 00:52:23 -0500 Subject: [PATCH 03/29] Add tutorial text --- tutorials/introductory/animation_tutorial.py | 133 +++++++++++++++---- 1 file changed, 105 insertions(+), 28 deletions(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index fc7ac968b405..4692e6974663 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -3,10 +3,11 @@ Animations using matplotlib =========================== -Animations in matplotlib can be created using the `~matplotlib.Animation` -module. This module provides 2 main animation classes - -`~matplotlib.animation.FuncAnimation` and -`~matplotlib.animation.ArtistAnimation`. +Based on its plotting functionality, matplotlib also provides an interface to +generate animations using the :class:`~matplotlib.animation` module. An +animation is a sequence of frames where each frame corresponds to a plot on a +:class:`~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 @@ -14,26 +15,54 @@ import numpy as np ############################################################################### +# Animation Classes +# ================= +# +# The process of animation can be thought about in 2 different ways: +# +# 1. Generate data for first frame and then modify this data for each frame to +# create an animated plot. +# 2. Generate a list (iterable) of plots (/images) that will each be a frame in +# the animation. +# +# Both of these thought processes can be translated into matplotlib using the +# :class:`~matplotlib.animation`'s :class:`~matplotlib.animation.FuncAnimation` +# and :class:`~matplotlib.animation.ArtistAnimation` classes respectively. +# # :class:`~matplotlib.animation.FuncAnimation` # -------------------------------------------- +# +# :class:`~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 +# :class:`~matplotlib.artist.Artist` +# (examples: :class:`~matplotlib.line.Line2D`, +# :class:`~matplotlib.collections.PathCollection`, etc.). A usual +# :class:`~matplotlib.animation.FuncAnimation` object takes a +# :class:`~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. +# (This is different than *fps* in `.animation.save` that we will +# cover later). We will now look at some examples of using +# :class:`~matplotlib.animation.FuncAnimation` with different artists. -# :class:`~matplotlib.animation.FuncAnimation` uses a user-provided function by -# repeatedly calling the function with at a regular *interval*. This allows -# dynamic generation of data by using generators. - - -def init(): - ax.set_ylim(-1, 1) - ax.set_xlim(0, 10) - xdata.clear() - ydata.clear() - line.set_data(xdata, ydata) - return line, +############################################################################### +# Animating :class:`~matplotlib.lines.Line2D` +# --------------------------------------------------------- +# +# `.pyplot.plot` returns a :class:`~matplotlib.lines.Line2D` collection. The +# data on this collection can be modified by using the +# `.lines.Line2D.set_data` function. Therefore we can use this to modify the +# plot using the function for every frame. fig, ax = plt.subplots() -line, = ax.plot([], [], lw=2) -ax.grid() +rng = np.random.default_rng() + xdata, ydata = [], [] +line, = ax.plot(xdata, ydata, c='b') +ax.grid() +ax.set_ylim(-1, 1) def update(frame): @@ -45,17 +74,20 @@ def update(frame): ax.set_xlim(xmin, 2*xmax) ax.figure.canvas.draw() line.set_data(xdata, ydata) - return line, ani = animation.FuncAnimation( - fig=fig, func=update, frames=240, init_func=init, interval=30 + fig=fig, func=update, interval=30 ) plt.show() ############################################################################### -# Animating :class:`~matplotlib.lines.PathCollection` -# --------------------------------------------------- +# Animating :class:`~matplotlib.collections.PathCollection` +# --------------------------------------------------------- +# +# `.pyplot.scatter` returns a :class:`~matplotlib.collections.PathCollection` +# that can similarly be modified by using the +# `.collections.PathCollection.set_offsets` function. fig, ax = plt.subplots() rng = np.random.default_rng() @@ -84,6 +116,10 @@ def update(frame): ############################################################################### # Animating :class:`~matplotlib.image.AxesImage` # --------------------------------------------------- +# +# When we plot an image using `.pyplot.imshow`, it returns an +# :class:`~matplotlib.image.AxesImage` object. The data in this object can also +# similarly be modified by using the `.image.AxesImage.set_data` method. fig, ax = plt.subplots() rng = np.random.default_rng() @@ -97,7 +133,7 @@ def update(frame): return aximg, ani = animation.FuncAnimation( - fig=fig, func=update, frames=240, interval=200 + fig=fig, func=update, frames=None, interval=200 ) plt.show() @@ -105,8 +141,9 @@ def update(frame): # :class:`~matplotlib.animation.ArtistAnimation` # ---------------------------------------------- # -# :class:`~matplotlib.animation.ArtistAnimation` uses a list of artists to -# iterate over and animate. +# On the other hand, :class:`~matplotlib.animation.ArtistAnimation` can be used +# to generate animations if we have data on various different artists. This +# list of artists is then converted frame by frame into an animation. fig, ax = plt.subplots() @@ -125,9 +162,40 @@ def update(frame): ani = animation.ArtistAnimation(fig=fig, artists=artists, repeat_delay=1000) plt.show() +############################################################################### +# :class:`~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 :class:`~matplotlib.animation.ArtistAnimation` is flexible as it +# allows any iterable of artists to be animated in a sequence. + ############################################################################### # 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. A list of supported formats for each writer can be found at +# the end of this tutorial. There are 4 major types of writers: +# +# 1. :class:`~matplotlib.animation.PillowWriter` - Uses the Pillow library to +# create the animation. +# +# 2. :class:`~matplotlib.animation.HTMLWriter` - Used to create JS-based +# animations. +# +# 3. Pipe-based writers - :class:`~matplotlib.animation.FFMpegWriter` and +# :class:`~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. +# +# 4. File-based writers - :class:`~matplotlib.animation.FFMpegFileWriter` and +# :class:`~matplotlib.animation.ImageMagickFileWriter` are examples of +# file-based writers. These writers are slower than their standard writers but +# are more useful for debugging as they save each frame in a file before +# stitching them together into an animation. +# +# Below we see examples for how to use different writers with +# `.animation.Animation.save` fig, ax = plt.subplots() ax.grid() @@ -153,8 +221,6 @@ def update(frame): # ani.save(filename="/tmp/pillow_example.gif", writer="pillow") # ani.save(filename="/tmp/pillow_example.apng", writer="pillow") -# Why does HTMLWriter documentation have a variable named -# supported_formats = ['png', 'jpeg', 'tiff', 'svg'] # 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") @@ -167,3 +233,14 @@ def update(frame): # Imagemagick # ani.save(filename="/tmp/imagemagick_example.gif", writer="imagemagick") + +############################################################################### +# +# ================================================ ============================= +# Writer Supported Formats +# ================================================ ============================= +# :class:`~matplotlib.animation.PillowWriter` .gif, .apng +# :class:`~matplotlib.animation.HTMLWriter` .htm, .html, .png +# :class:`~matplotlib.animation.FFMpegWriter` All formats supported by *ffmpeg* +# :class:`~matplotlib.animation.ImageMagickWriter` .gif +# ================================================ ============================= From 986e8ce1a6d3ef9134a0f49470665df51c0b6431 Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Fri, 28 Oct 2022 01:06:27 -0500 Subject: [PATCH 04/29] Format to agree with flake8 --- tutorials/introductory/animation_tutorial.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index 4692e6974663..d0076e60d260 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -36,7 +36,7 @@ # animation by passing a function that iteratively modifies the data of a plot. # This is achieved by using the *setter* methods on various # :class:`~matplotlib.artist.Artist` -# (examples: :class:`~matplotlib.line.Line2D`, +# (examples: :class:`~matplotlib.lines.Line2D`, # :class:`~matplotlib.collections.PathCollection`, etc.). A usual # :class:`~matplotlib.animation.FuncAnimation` object takes a # :class:`~matplotlib.figure.Figure` that we want to animate and a function @@ -236,11 +236,12 @@ def update(frame): ############################################################################### # -# ================================================ ============================= +# ================================================ =========================== # Writer Supported Formats -# ================================================ ============================= +# ================================================ =========================== # :class:`~matplotlib.animation.PillowWriter` .gif, .apng # :class:`~matplotlib.animation.HTMLWriter` .htm, .html, .png -# :class:`~matplotlib.animation.FFMpegWriter` All formats supported by *ffmpeg* +# :class:`~matplotlib.animation.FFMpegWriter` All formats supported by +# *ffmpeg* # :class:`~matplotlib.animation.ImageMagickWriter` .gif -# ================================================ ============================= +# ================================================ =========================== From a15d2125945963619bc5e6f4021afe3408d13adc Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Sat, 29 Oct 2022 16:18:44 -0500 Subject: [PATCH 05/29] Update tutorial and scatter example --- tutorials/introductory/animation_tutorial.py | 138 +++++++++---------- 1 file changed, 66 insertions(+), 72 deletions(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index d0076e60d260..c983858ce32d 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -20,14 +20,16 @@ # # The process of animation can be thought about in 2 different ways: # -# 1. Generate data for first frame and then modify this data for each frame to -# create an animated plot. -# 2. Generate a list (iterable) of plots (/images) that will each be a frame in -# the animation. +# - :class:`~matplotlib.animation.FuncAnimation`: Generate data for first +# frame and then modify this data for each frame to create an animated plot. # -# Both of these thought processes can be translated into matplotlib using the -# :class:`~matplotlib.animation`'s :class:`~matplotlib.animation.FuncAnimation` -# and :class:`~matplotlib.animation.ArtistAnimation` classes respectively. +# - :class:`~matplotlib.animation.FuncAnimation`: Generate a list (iterable) +# of artists that will draw in each frame in the animation. +# +# :class:`~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 :class:`~matplotlib.animation.ArtistAnimation` is flexible as it +# allows any iterable of artists to be animated in a sequence. # # :class:`~matplotlib.animation.FuncAnimation` # -------------------------------------------- @@ -43,13 +45,12 @@ # *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. -# (This is different than *fps* in `.animation.save` that we will -# cover later). We will now look at some examples of using +# We will now look at some examples of using # :class:`~matplotlib.animation.FuncAnimation` with different artists. ############################################################################### # Animating :class:`~matplotlib.lines.Line2D` -# --------------------------------------------------------- +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # # `.pyplot.plot` returns a :class:`~matplotlib.lines.Line2D` collection. The # data on this collection can be modified by using the @@ -60,30 +61,28 @@ rng = np.random.default_rng() xdata, ydata = [], [] -line, = ax.plot(xdata, ydata, c='b') +(line,) = ax.plot(xdata, ydata, c="b") ax.grid() ax.set_ylim(-1, 1) +ax.set_xlim(0, 10) def update(frame): - xdata.append(frame) + # .set_data resets all the data for the line, so we add the new point to + # the existing line data and set that again. + xdata.append(frame / 30) ydata.append(np.sin(2 * np.pi * frame / 30)) - xmin, xmax = ax.get_xlim() - if frame >= xmax: - ax.set_xlim(xmin, 2*xmax) - ax.figure.canvas.draw() line.set_data(xdata, ydata) - return line, + return (line,) -ani = animation.FuncAnimation( - fig=fig, func=update, interval=30 -) + +ani = animation.FuncAnimation(fig=fig, func=update, interval=30) plt.show() ############################################################################### # Animating :class:`~matplotlib.collections.PathCollection` -# --------------------------------------------------------- +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # # `.pyplot.scatter` returns a :class:`~matplotlib.collections.PathCollection` # that can similarly be modified by using the @@ -91,31 +90,33 @@ def update(frame): fig, ax = plt.subplots() rng = np.random.default_rng() +t = np.linspace(-4, 4, 1000) +a, b = 3, 2 +delta = np.pi / 2 -scat = ax.scatter( - rng.uniform(low=0, high=1, size=100), - rng.uniform(low=0, high=1, size=100), - c='b' -) +scat = ax.scatter(np.sin(a * t[0] + delta), np.sin(b * t[0]), c="b", s=2) ax.grid() +ax.set_xlim(-1, 1) +ax.set_ylim(-1, 1) def update(frame): - x = rng.uniform(low=0, high=1, size=100) - y = rng.uniform(low=0, high=1, size=100) + # .set_offsets also resets the entire data for the collection. + # Therefore, we create the entire data in each frame to draw + 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, + return (scat,) -ani = animation.FuncAnimation( - fig=fig, func=update, frames=240, interval=300 -) + +ani = animation.FuncAnimation(fig=fig, func=update, interval=30) plt.show() ############################################################################### # Animating :class:`~matplotlib.image.AxesImage` -# --------------------------------------------------- +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ # # When we plot an image using `.pyplot.imshow`, it returns an # :class:`~matplotlib.image.AxesImage` object. The data in this object can also @@ -130,11 +131,10 @@ def update(frame): def update(frame): data = rng.uniform(low=0, high=1, size=(10, 10)) aximg.set_data(data) - return aximg, + return (aximg,) -ani = animation.FuncAnimation( - fig=fig, func=update, frames=None, interval=200 -) + +ani = animation.FuncAnimation(fig=fig, func=update, frames=None, interval=200) plt.show() ############################################################################### @@ -153,49 +153,56 @@ def update(frame): x_frames = rng.uniform(low=0, high=1, size=(100, 120)) y_frames = rng.uniform(low=0, high=1, size=(100, 120)) artists = [ - [ - ax.scatter(x_frames[:, i], y_frames[:, i], c="b") - ] + [ax.scatter(x_frames[:, i], y_frames[:, i], c="b")] for i in range(x_frames.shape[-1]) ] ani = animation.ArtistAnimation(fig=fig, artists=artists, repeat_delay=1000) plt.show() -############################################################################### -# :class:`~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 :class:`~matplotlib.animation.ArtistAnimation` is flexible as it -# allows any iterable of artists to be animated in a sequence. - ############################################################################### # 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. A list of supported formats for each writer can be found at -# the end of this tutorial. There are 4 major types of writers: +# by all writers. There are 4 major types of writers: # -# 1. :class:`~matplotlib.animation.PillowWriter` - Uses the Pillow library to +# - :class:`~matplotlib.animation.PillowWriter` - Uses the Pillow library to # create the animation. # -# 2. :class:`~matplotlib.animation.HTMLWriter` - Used to create JS-based +# - :class:`~matplotlib.animation.HTMLWriter` - Used to create JS-based # animations. # -# 3. Pipe-based writers - :class:`~matplotlib.animation.FFMpegWriter` and +# - Pipe-based writers - :class:`~matplotlib.animation.FFMpegWriter` and # :class:`~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. # -# 4. File-based writers - :class:`~matplotlib.animation.FFMpegFileWriter` and +# - File-based writers - :class:`~matplotlib.animation.FFMpegFileWriter` and # :class:`~matplotlib.animation.ImageMagickFileWriter` are examples of # file-based writers. These writers are slower than their standard writers but # are more useful for debugging as they save each frame in a file before # stitching them together into an animation. # -# Below we see examples for how to use different writers with -# `.animation.Animation.save` +# ================================================ =========================== +# Writer Supported Formats +# ================================================ =========================== +# :class:`~matplotlib.animation.PillowWriter` .gif, .apng +# :class:`~matplotlib.animation.HTMLWriter` .htm, .html, .png +# :class:`~matplotlib.animation.FFMpegWriter` All formats supported by +# *ffmpeg* +# :class:`~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. fig, ax = plt.subplots() ax.grid() @@ -204,7 +211,7 @@ def update(frame): scat = ax.scatter( rng.uniform(low=0, high=1, size=100), rng.uniform(low=0, high=1, size=100), - c='b' + c="b" ) @@ -213,11 +220,10 @@ def update(frame): y = rng.uniform(low=0, high=1, size=100) data = np.stack([x, y]).T scat.set_offsets(data) - return scat, + return (scat,) -ani = animation.FuncAnimation( - fig=fig, func=update, frames=240, interval=200 -) + +ani = animation.FuncAnimation(fig=fig, func=update, frames=240, interval=200) # ani.save(filename="/tmp/pillow_example.gif", writer="pillow") # ani.save(filename="/tmp/pillow_example.apng", writer="pillow") @@ -233,15 +239,3 @@ def update(frame): # Imagemagick # ani.save(filename="/tmp/imagemagick_example.gif", writer="imagemagick") - -############################################################################### -# -# ================================================ =========================== -# Writer Supported Formats -# ================================================ =========================== -# :class:`~matplotlib.animation.PillowWriter` .gif, .apng -# :class:`~matplotlib.animation.HTMLWriter` .htm, .html, .png -# :class:`~matplotlib.animation.FFMpegWriter` All formats supported by -# *ffmpeg* -# :class:`~matplotlib.animation.ImageMagickWriter` .gif -# ================================================ =========================== From 9a0e85e351a26ef08026977dca8a9cbe73a09994 Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Sat, 29 Oct 2022 17:44:25 -0500 Subject: [PATCH 06/29] 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. --- tutorials/introductory/animation_tutorial.py | 40 ++++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index c983858ce32d..043508389e58 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -21,10 +21,10 @@ # The process of animation can be thought about in 2 different ways: # # - :class:`~matplotlib.animation.FuncAnimation`: Generate data for first -# frame and then modify this data for each frame to create an animated plot. +# frame and then modify this data for each frame to create an animated plot. # # - :class:`~matplotlib.animation.FuncAnimation`: Generate a list (iterable) -# of artists that will draw in each frame in the animation. +# of artists that will draw in each frame in the animation. # # :class:`~matplotlib.animation.FuncAnimation` is more efficient in terms of # speed and memory as it draws an artist once and then modifies it. On the @@ -63,8 +63,15 @@ xdata, ydata = [], [] (line,) = ax.plot(xdata, ydata, c="b") ax.grid() -ax.set_ylim(-1, 1) -ax.set_xlim(0, 10) + + +def init(): + ax.set_ylim(-1.1, 1.1) + ax.set_xlim(0, 5) + del xdata[:] + del ydata[:] + line.set_data(xdata, ydata) + return line, def update(frame): @@ -77,7 +84,8 @@ def update(frame): return (line,) -ani = animation.FuncAnimation(fig=fig, func=update, interval=30) +ani = animation.FuncAnimation(fig=fig, func=update, + interval=30, init_func=init) plt.show() ############################################################################### @@ -90,7 +98,7 @@ def update(frame): fig, ax = plt.subplots() rng = np.random.default_rng() -t = np.linspace(-4, 4, 1000) +t = np.linspace(-4, 4, 400) a, b = 3, 2 delta = np.pi / 2 @@ -110,7 +118,7 @@ def update(frame): return (scat,) -ani = animation.FuncAnimation(fig=fig, func=update, interval=30) +ani = animation.FuncAnimation(fig=fig, func=update, frames=400, interval=30) plt.show() @@ -169,21 +177,21 @@ def update(frame): # by all writers. There are 4 major types of writers: # # - :class:`~matplotlib.animation.PillowWriter` - Uses the Pillow library to -# create the animation. +# create the animation. # # - :class:`~matplotlib.animation.HTMLWriter` - Used to create JS-based -# animations. +# animations. # # - Pipe-based writers - :class:`~matplotlib.animation.FFMpegWriter` and -# :class:`~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. +# :class:`~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 - :class:`~matplotlib.animation.FFMpegFileWriter` and -# :class:`~matplotlib.animation.ImageMagickFileWriter` are examples of -# file-based writers. These writers are slower than their standard writers but -# are more useful for debugging as they save each frame in a file before -# stitching them together into an animation. +# :class:`~matplotlib.animation.ImageMagickFileWriter` are examples of +# file-based writers. These writers are slower than their standard writers +# but are more useful for debugging as they save each frame in a file before +# stitching them together into an animation. # # ================================================ =========================== # Writer Supported Formats From 6c63bb3e915694463304bd0948a2d526aaea2e76 Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Wed, 2 Nov 2022 15:57:09 -0500 Subject: [PATCH 07/29] Change to simpler line animation example --- tutorials/introductory/animation_tutorial.py | 25 ++++++-------------- 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index 043508389e58..00aa484afc42 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -60,32 +60,21 @@ fig, ax = plt.subplots() rng = np.random.default_rng() -xdata, ydata = [], [] -(line,) = ax.plot(xdata, ydata, c="b") +xdata = np.arange(0, 2 * np.pi, 0.01) +(line,) = ax.plot(xdata, np.sin(xdata), c="b") ax.grid() - - -def init(): - ax.set_ylim(-1.1, 1.1) - ax.set_xlim(0, 5) - del xdata[:] - del ydata[:] - line.set_data(xdata, ydata) - return line, +ax.set_ylim(-1.1, 1.1) def update(frame): - # .set_data resets all the data for the line, so we add the new point to - # the existing line data and set that again. - xdata.append(frame / 30) - ydata.append(np.sin(2 * np.pi * frame / 30)) - - line.set_data(xdata, ydata) + # .set_ydata resets the y-data for the line, so we add the new point to + # the existing line x-data and calculate y again. + line.set_ydata(np.sin(xdata + frame / 50)) return (line,) ani = animation.FuncAnimation(fig=fig, func=update, - interval=30, init_func=init) + interval=30) plt.show() ############################################################################### From 2ebdcd091f934d9f662e3cde521f0db230e47ff5 Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Wed, 2 Nov 2022 16:02:20 -0500 Subject: [PATCH 08/29] Change headings and update set_offsets description --- tutorials/introductory/animation_tutorial.py | 25 +++++++++++--------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index 00aa484afc42..5ea59c281b32 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -18,7 +18,8 @@ # Animation Classes # ================= # -# The process of animation can be thought about in 2 different ways: +# The process of animation in matplotlib can be thought about in 2 different +# ways: # # - :class:`~matplotlib.animation.FuncAnimation`: Generate data for first # frame and then modify this data for each frame to create an animated plot. @@ -49,8 +50,8 @@ # :class:`~matplotlib.animation.FuncAnimation` with different artists. ############################################################################### -# Animating :class:`~matplotlib.lines.Line2D` -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# Animating Lines +# ^^^^^^^^^^^^^^^ # # `.pyplot.plot` returns a :class:`~matplotlib.lines.Line2D` collection. The # data on this collection can be modified by using the @@ -78,8 +79,8 @@ def update(frame): plt.show() ############################################################################### -# Animating :class:`~matplotlib.collections.PathCollection` -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# Animating Markers +# ^^^^^^^^^^^^^^^^^ # # `.pyplot.scatter` returns a :class:`~matplotlib.collections.PathCollection` # that can similarly be modified by using the @@ -93,13 +94,15 @@ def update(frame): scat = ax.scatter(np.sin(a * t[0] + delta), np.sin(b * t[0]), c="b", s=2) ax.grid() -ax.set_xlim(-1, 1) -ax.set_ylim(-1, 1) +ax.set_xlim(-1.5, 1.5) +ax.set_ylim(-1.5, 1.5) def update(frame): - # .set_offsets also resets the entire data for the collection. - # Therefore, we create the entire data in each frame to draw + # .set_offsets also resets 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 @@ -112,8 +115,8 @@ def update(frame): ############################################################################### -# Animating :class:`~matplotlib.image.AxesImage` -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# Animating Images +# ^^^^^^^^^^^^^^^^ # # When we plot an image using `.pyplot.imshow`, it returns an # :class:`~matplotlib.image.AxesImage` object. The data in this object can also From 5e2f4ef598065ec03cb0c83bc72f482224460d6b Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Wed, 2 Nov 2022 16:09:44 -0500 Subject: [PATCH 09/29] Change writer type to pipe-based from standard --- tutorials/introductory/animation_tutorial.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index 5ea59c281b32..7d1b9c79b375 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -181,9 +181,9 @@ def update(frame): # # - File-based writers - :class:`~matplotlib.animation.FFMpegFileWriter` and # :class:`~matplotlib.animation.ImageMagickFileWriter` are examples of -# file-based writers. These writers are slower than their standard writers -# but are more useful for debugging as they save each frame in a file before -# stitching them together into an animation. +# 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. # # ================================================ =========================== # Writer Supported Formats From eaa22e1e039f2a967ac60f2540e44d36b40b1128 Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Wed, 2 Nov 2022 16:50:59 -0500 Subject: [PATCH 10/29] Update saving animation section --- tutorials/introductory/animation_tutorial.py | 39 +++++++++++++------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index 7d1b9c79b375..1d8e5d8efb1c 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -185,6 +185,9 @@ def update(frame): # 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 # ================================================ =========================== @@ -224,18 +227,26 @@ def update(frame): ani = animation.FuncAnimation(fig=fig, func=update, frames=240, interval=200) -# ani.save(filename="/tmp/pillow_example.gif", writer="pillow") -# ani.save(filename="/tmp/pillow_example.apng", writer="pillow") - -# 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") - -# Since the frames are piped out to ffmpeg, this supports all formats -# supported by ffmpeg -# 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 -# ani.save(filename="/tmp/imagemagick_example.gif", writer="imagemagick") +############################################################################### +# 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 - Since the frames are piped out to ffmpeg, this option supports +# all formats supported by ffmpeg:: +# +# 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") From cfe596fd149257e9cd13e1ebf0ed9c19fd61c4b4 Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Thu, 3 Nov 2022 16:03:15 -0500 Subject: [PATCH 11/29] Add new example for images and ArtistAnimation --- tutorials/introductory/animation_tutorial.py | 44 +++++++++++++++----- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index 1d8e5d8efb1c..434a061f98d4 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -122,19 +122,33 @@ def update(frame): # :class:`~matplotlib.image.AxesImage` object. The data in this object can also # similarly be modified by using the `.image.AxesImage.set_data` method. + +def f(x, y, mean, cov): + dev_x = x - mean + dev_y = y - mean + maha = -0.5 * (((x-mean)/cov)**2 + ((y-mean)/cov)**2) + return (1/(np.pi * cov)) * np.exp(maha) + fig, ax = plt.subplots() -rng = np.random.default_rng() -aximg = ax.imshow(rng.uniform(low=0, high=1, size=(10, 10)), cmap="Blues") +x, y = np.meshgrid(np.arange(-1, 1, 0.01), np.arange(-1, 1, 0.01)) +mean = 0 +cov = 0.1 +data = f(x, y, mean, cov) +aximg = ax.imshow(data) def update(frame): - data = rng.uniform(low=0, high=1, size=(10, 10)) + x, y = np.meshgrid(np.arange(-1, 1, 0.01), np.arange(-1, 1, 0.01)) + mean = 0 + cov = 0.01 * frame + 1e-6 + data = f(x, y, mean, cov) + aximg.set_data(data) return (aximg,) -ani = animation.FuncAnimation(fig=fig, func=update, frames=None, interval=200) +ani = animation.FuncAnimation(fig=fig, func=update, frames=None, interval=100) plt.show() ############################################################################### @@ -146,18 +160,26 @@ def update(frame): # list of artists is then converted frame by frame into an animation. +def f(x, y, mean, cov): + dev_x = x - mean + dev_y = y - mean + maha = -0.5 * (((x-mean)/cov)**2 + ((y-mean)/cov)**2) + return (1/(np.pi * cov)) * np.exp(maha) + fig, ax = plt.subplots() -ax.grid() -rng = np.random.default_rng() -x_frames = rng.uniform(low=0, high=1, size=(100, 120)) -y_frames = rng.uniform(low=0, high=1, size=(100, 120)) +x, y = np.meshgrid(np.arange(-1, 1, 0.01), np.arange(-1, 1, 0.01)) +mean = 0 +cov = 0.1 +data = f(x, y, mean, cov) +aximg = ax.imshow(data) + artists = [ - [ax.scatter(x_frames[:, i], y_frames[:, i], c="b")] - for i in range(x_frames.shape[-1]) + [ax.imshow(f(x, y, mean, 0.01 * frame + 1e-6))] + for frame in range(120) ] -ani = animation.ArtistAnimation(fig=fig, artists=artists, repeat_delay=1000) +ani = animation.ArtistAnimation(fig=fig, artists=artists, interval=100) plt.show() ############################################################################### From c3c16b6bbc1cafe32392ed8f4683011a843bb17f Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Fri, 4 Nov 2022 21:20:45 -0500 Subject: [PATCH 12/29] Remove unused variables --- tutorials/introductory/animation_tutorial.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index 434a061f98d4..481659c335ab 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -63,7 +63,6 @@ xdata = np.arange(0, 2 * np.pi, 0.01) (line,) = ax.plot(xdata, np.sin(xdata), c="b") -ax.grid() ax.set_ylim(-1.1, 1.1) @@ -87,7 +86,6 @@ def update(frame): # `.collections.PathCollection.set_offsets` function. fig, ax = plt.subplots() -rng = np.random.default_rng() t = np.linspace(-4, 4, 400) a, b = 3, 2 delta = np.pi / 2 From 8a148eaafed2de7c9f530a18500a9c2cae47f832 Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Fri, 4 Nov 2022 21:22:26 -0500 Subject: [PATCH 13/29] Correct type of animation class --- tutorials/introductory/animation_tutorial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index 481659c335ab..e42dd695e86b 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -27,7 +27,7 @@ # - :class:`~matplotlib.animation.FuncAnimation`: Generate a list (iterable) # of artists that will draw in each frame in the animation. # -# :class:`~matplotlib.animation.FuncAnimation` is more efficient in terms of +# :class:`~matplotlib.animation.ArtistAnimation` is more efficient in terms of # speed and memory as it draws an artist once and then modifies it. On the # other hand :class:`~matplotlib.animation.ArtistAnimation` is flexible as it # allows any iterable of artists to be animated in a sequence. From b4137a3809bba18e7e049d910f850c7f9978d8bb Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Fri, 4 Nov 2022 21:23:55 -0500 Subject: [PATCH 14/29] Remove unused variable --- tutorials/introductory/animation_tutorial.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index e42dd695e86b..3eae186f150a 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -59,7 +59,6 @@ # plot using the function for every frame. fig, ax = plt.subplots() -rng = np.random.default_rng() xdata = np.arange(0, 2 * np.pi, 0.01) (line,) = ax.plot(xdata, np.sin(xdata), c="b") From b42966b917d432301f45cb2c4e9a6c6165363e96 Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Fri, 4 Nov 2022 21:24:49 -0500 Subject: [PATCH 15/29] Correct wrong correction --- tutorials/introductory/animation_tutorial.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index 3eae186f150a..9dd6c5313e11 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -24,10 +24,10 @@ # - :class:`~matplotlib.animation.FuncAnimation`: Generate data for first # frame and then modify this data for each frame to create an animated plot. # -# - :class:`~matplotlib.animation.FuncAnimation`: Generate a list (iterable) +# - :class:`~matplotlib.animation.ArtistAnimation`: Generate a list (iterable) # of artists that will draw in each frame in the animation. # -# :class:`~matplotlib.animation.ArtistAnimation` is more efficient in terms of +# :class:`~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 :class:`~matplotlib.animation.ArtistAnimation` is flexible as it # allows any iterable of artists to be animated in a sequence. From e645c627a4fdc9287bb1e6c10cbbf29b3bcebe87 Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Sun, 6 Nov 2022 16:30:15 -0600 Subject: [PATCH 16/29] Update ArtistAnimation example to use bar chart --- tutorials/introductory/animation_tutorial.py | 29 ++++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index 9dd6c5313e11..30c480fe4b13 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -90,7 +90,6 @@ def update(frame): delta = np.pi / 2 scat = ax.scatter(np.sin(a * t[0] + delta), np.sin(b * t[0]), c="b", s=2) -ax.grid() ax.set_xlim(-1.5, 1.5) ax.set_ylim(-1.5, 1.5) @@ -152,28 +151,22 @@ def update(frame): # :class:`~matplotlib.animation.ArtistAnimation` # ---------------------------------------------- # -# On the other hand, :class:`~matplotlib.animation.ArtistAnimation` can be used -# to generate animations if we have data on various different artists. This -# list of artists is then converted frame by frame into an animation. - +# :class:`~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.bar` 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. -def f(x, y, mean, cov): - dev_x = x - mean - dev_y = y - mean - maha = -0.5 * (((x-mean)/cov)**2 + ((y-mean)/cov)**2) - return (1/(np.pi * cov)) * np.exp(maha) fig, ax = plt.subplots() - -x, y = np.meshgrid(np.arange(-1, 1, 0.01), np.arange(-1, 1, 0.01)) -mean = 0 -cov = 0.1 -data = f(x, y, mean, cov) -aximg = ax.imshow(data) +data = np.array([10, 20, 20, 30]) +x = [1, 2, 3, 4] artists = [ - [ax.imshow(f(x, y, mean, 0.01 * frame + 1e-6))] - for frame in range(120) + list(ax.bar(x, data + i, color='b')) for i in range(10) ] ani = animation.ArtistAnimation(fig=fig, artists=artists, interval=100) From d05eaed8b5d1642789ad85255f6f1bb40fafafc7 Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Sun, 6 Nov 2022 20:39:16 -0600 Subject: [PATCH 17/29] Barchart race example for ArtistAnimation --- tutorials/introductory/animation_tutorial.py | 21 +++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index 30c480fe4b13..9b271daec8d9 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -158,18 +158,25 @@ def update(frame): # 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. +# individually and then stitched together to form an animation. A barchart race +# is a simple example for this. fig, ax = plt.subplots() -data = np.array([10, 20, 20, 30]) -x = [1, 2, 3, 4] +rng = np.random.default_rng() +data = np.array([20, 20, 20, 20]) +x = np.array([1, 2, 3, 4]) + +artists = [] +colors = {1: 'b', 2: 'r', 3: 'k', 4: 'g'} +for i in range(20): + data += rng.integers(low=0, high=10, size=data.shape) + order = data.argsort() + container = ax.barh(x, data[order], color=[colors[x[o]] for o in order]) + artists.append(container) -artists = [ - list(ax.bar(x, data + i, color='b')) for i in range(10) -] -ani = animation.ArtistAnimation(fig=fig, artists=artists, interval=100) +ani = animation.ArtistAnimation(fig=fig, artists=artists, interval=400) plt.show() ############################################################################### From 46e691dbf2202cc96c6171412469cd28b111e283 Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Thu, 10 Nov 2022 13:12:29 -0600 Subject: [PATCH 18/29] Change FuncAnimation to have one general guideline --- tutorials/introductory/animation_tutorial.py | 98 +++++--------------- 1 file changed, 25 insertions(+), 73 deletions(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index 9b271daec8d9..8a9314a4eee5 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -46,43 +46,32 @@ # *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. -# We will now look at some examples of using -# :class:`~matplotlib.animation.FuncAnimation` with different artists. - -############################################################################### -# Animating Lines -# ^^^^^^^^^^^^^^^ +# Animating using `.FuncAnimation` would usually follow the following +# structure: # -# `.pyplot.plot` returns a :class:`~matplotlib.lines.Line2D` collection. The -# data on this collection can be modified by using the -# `.lines.Line2D.set_data` function. Therefore we can use this to modify the -# plot using the function for every frame. - -fig, ax = plt.subplots() - -xdata = np.arange(0, 2 * np.pi, 0.01) -(line,) = ax.plot(xdata, np.sin(xdata), c="b") -ax.set_ylim(-1.1, 1.1) - - -def update(frame): - # .set_ydata resets the y-data for the line, so we add the new point to - # the existing line x-data and calculate y again. - line.set_ydata(np.sin(xdata + frame / 50)) - return (line,) - - -ani = animation.FuncAnimation(fig=fig, func=update, - interval=30) -plt.show() - -############################################################################### -# Animating Markers -# ^^^^^^^^^^^^^^^^^ +# - 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. +# - 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. # -# `.pyplot.scatter` returns a :class:`~matplotlib.collections.PathCollection` -# that can similarly be modified by using the -# `.collections.PathCollection.set_offsets` function. +# ============================= ========================================= +# Artist Set method +# ============================= ========================================= +# `.lines.Line2D` `.lines.Line2D.set_data` +# `.collections.PathCollection` `.collections.PathCollection.set_offsets` +# `.image.AxesImage` `.image.AxesImage.set_data` +# ============================= ========================================= +# +# An example for animating a `.Axes.scatter` plot is + fig, ax = plt.subplots() t = np.linspace(-4, 4, 400) @@ -110,43 +99,6 @@ def update(frame): plt.show() -############################################################################### -# Animating Images -# ^^^^^^^^^^^^^^^^ -# -# When we plot an image using `.pyplot.imshow`, it returns an -# :class:`~matplotlib.image.AxesImage` object. The data in this object can also -# similarly be modified by using the `.image.AxesImage.set_data` method. - - -def f(x, y, mean, cov): - dev_x = x - mean - dev_y = y - mean - maha = -0.5 * (((x-mean)/cov)**2 + ((y-mean)/cov)**2) - return (1/(np.pi * cov)) * np.exp(maha) - -fig, ax = plt.subplots() - -x, y = np.meshgrid(np.arange(-1, 1, 0.01), np.arange(-1, 1, 0.01)) -mean = 0 -cov = 0.1 -data = f(x, y, mean, cov) -aximg = ax.imshow(data) - - -def update(frame): - x, y = np.meshgrid(np.arange(-1, 1, 0.01), np.arange(-1, 1, 0.01)) - mean = 0 - cov = 0.01 * frame + 1e-6 - data = f(x, y, mean, cov) - - aximg.set_data(data) - return (aximg,) - - -ani = animation.FuncAnimation(fig=fig, func=update, frames=None, interval=100) -plt.show() - ############################################################################### # :class:`~matplotlib.animation.ArtistAnimation` # ---------------------------------------------- @@ -154,7 +106,7 @@ def update(frame): # :class:`~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.bar` to plot a bar-chart, it creates a number of +# 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 From 5f4e555e8ca1b1623c44c0c3031f1f94e33a535e Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Fri, 11 Nov 2022 12:09:51 -0600 Subject: [PATCH 19/29] Add a table for set methods for example artists --- tutorials/introductory/animation_tutorial.py | 22 +++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index 8a9314a4eee5..5aafd19da87e 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -62,15 +62,27 @@ # The update function uses the `set_*` function for different artists to modify # the data. # -# ============================= ========================================= +# ============================= =========================================== # Artist Set method -# ============================= ========================================= +# ============================= =========================================== # `.lines.Line2D` `.lines.Line2D.set_data` # `.collections.PathCollection` `.collections.PathCollection.set_offsets` # `.image.AxesImage` `.image.AxesImage.set_data` -# ============================= ========================================= -# -# An example for animating a `.Axes.scatter` plot is +# `.text.Annotation` `.text.Annotation.update_positions` +# `.patches.Rectangle` `.Rectangle.set_angle`, +# `.Rectangle.set_bounds`, +# `.Rectangle.set_height`, +# `.Rectangle.set_width`, +# `.Rectangle.set_x`, `.Rectangle.set_y` +# `.Rectangle.set_xy` +# `.patches.Polygon` `.Polygon.set_xy` +# `.patches.Ellipse` `.Ellipse.set_angle`, +# `.Ellipse.set_center`, +# `.Ellipse.set_height`, `.Ellipse.set_width` +# ============================= =========================================== +# +# Other such set methods can be looked up in the artist documentation. An +# example for animating a `.Axes.scatter` plot is fig, ax = plt.subplots() From 8fba3202868af347f5d8556e4b82bcc54b4f3251 Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Fri, 11 Nov 2022 21:26:45 -0600 Subject: [PATCH 20/29] Add plotting method, return artist type table --- tutorials/introductory/animation_tutorial.py | 52 +++++++++++--------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index 5aafd19da87e..2e4b68f1ec79 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -60,29 +60,35 @@ # animation. # # The update function uses the `set_*` function for different artists to modify -# the data. -# -# ============================= =========================================== -# Artist Set method -# ============================= =========================================== -# `.lines.Line2D` `.lines.Line2D.set_data` -# `.collections.PathCollection` `.collections.PathCollection.set_offsets` -# `.image.AxesImage` `.image.AxesImage.set_data` -# `.text.Annotation` `.text.Annotation.update_positions` -# `.patches.Rectangle` `.Rectangle.set_angle`, -# `.Rectangle.set_bounds`, -# `.Rectangle.set_height`, -# `.Rectangle.set_width`, -# `.Rectangle.set_x`, `.Rectangle.set_y` -# `.Rectangle.set_xy` -# `.patches.Polygon` `.Polygon.set_xy` -# `.patches.Ellipse` `.Ellipse.set_angle`, -# `.Ellipse.set_center`, -# `.Ellipse.set_height`, `.Ellipse.set_width` -# ============================= =========================================== -# -# Other such set methods can be looked up in the artist documentation. An -# example for animating a `.Axes.scatter` plot is +# the data. The following table shows a few example methods, the artist types +# they return and the 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.PathCollecti\ +# on.set_offsets` +# `.Axes.imshow` `.image.AxesImage` `.image.AxesImage.set_data` +# `.Axes.annotate` `.text.Annotation` `.text.Annotation.update_p\ +# ositions` +# `.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() From 67c4f5382adaaef792c5e248eb65791c1f918966 Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Sun, 13 Nov 2022 11:45:49 -0600 Subject: [PATCH 21/29] Reword to avoid confusion --- tutorials/introductory/animation_tutorial.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index 2e4b68f1ec79..aa668c5d3421 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -60,8 +60,8 @@ # animation. # # The update function uses the `set_*` function for different artists to modify -# the data. The following table shows a few example methods, the artist types -# they return and the methods that can be used to update them. +# 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 From 85913db750c2caf6d1a88c4980602b7a4f04cf15 Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Tue, 15 Nov 2022 17:43:45 -0600 Subject: [PATCH 22/29] Reposition note about pipe based writers --- tutorials/introductory/animation_tutorial.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index aa668c5d3421..7cdf9d18f02d 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -229,8 +229,7 @@ def update(frame): # ani.save(filename="/tmp/html_example.htm", writer="html") # ani.save(filename="/tmp/html_example.png", writer="html") # -# FFMpegWriter - Since the frames are piped out to ffmpeg, this option supports -# all formats supported by ffmpeg:: +# FFMpegWriter:: # # ani.save(filename="/tmp/ffmpeg_example.mkv", writer="ffmpeg") # ani.save(filename="/tmp/ffmpeg_example.mp4", writer="ffmpeg") @@ -239,3 +238,7 @@ def update(frame): # Imagemagick writers:: # # ani.save(filename="/tmp/imagemagick_example.gif", writer="imagemagick") +# +# **NOTE**: Since the frames are piped out to *ffmpeg* or *imagemagick*, +# *writer="ffmpeg"* and *writer="imagemagick"* support all formats supported by +# *ffmpeg* and *imagemagick*. From 57ae9ed887917c6bb5fe506eaac06e0c4a13cba2 Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Tue, 15 Nov 2022 17:49:24 -0600 Subject: [PATCH 23/29] Change color and don't sort bar chart --- tutorials/introductory/animation_tutorial.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index 7cdf9d18f02d..577106d46d72 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -138,11 +138,10 @@ def update(frame): x = np.array([1, 2, 3, 4]) artists = [] -colors = {1: 'b', 2: 'r', 3: 'k', 4: 'g'} +colors = ['tab:blue', 'tab:red', 'tab:green', 'tab:purple'] for i in range(20): data += rng.integers(low=0, high=10, size=data.shape) - order = data.argsort() - container = ax.barh(x, data[order], color=[colors[x[o]] for o in order]) + container = ax.barh(x, data, color=colors) artists.append(container) From b8e0e1d56d841f51f6c20a99679fbcd4c6af4c6f Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Wed, 16 Nov 2022 22:25:34 -0600 Subject: [PATCH 24/29] Remove animation code in save animation section --- tutorials/introductory/animation_tutorial.py | 30 ++++---------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index 577106d46d72..53822ca2b813 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -194,29 +194,11 @@ def update(frame): # `~.animation.ArtistAnimation` uses. *fps* determines the frame rate that the # **saved** animation uses, whereas *interval* determines the frame rate that # the **displayed** animation uses. - -fig, ax = plt.subplots() -ax.grid() -rng = np.random.default_rng() - -scat = ax.scatter( - rng.uniform(low=0, high=1, size=100), - rng.uniform(low=0, high=1, size=100), - c="b" -) - - -def update(frame): - x = rng.uniform(low=0, high=1, size=100) - y = rng.uniform(low=0, high=1, size=100) - data = np.stack([x, y]).T - scat.set_offsets(data) - return (scat,) - - -ani = animation.FuncAnimation(fig=fig, func=update, frames=240, interval=200) - -############################################################################### +# +# Following are a few examples showing how to save an animation with different +# writers. +# +# # Pillow writers:: # # ani.save(filename="/tmp/pillow_example.gif", writer="pillow") @@ -238,6 +220,6 @@ def update(frame): # # ani.save(filename="/tmp/imagemagick_example.gif", writer="imagemagick") # -# **NOTE**: Since the frames are piped out to *ffmpeg* or *imagemagick*, +# Since the frames are piped out to *ffmpeg* or *imagemagick*, # *writer="ffmpeg"* and *writer="imagemagick"* support all formats supported by # *ffmpeg* and *imagemagick*. From abbb0312eb6679c8696521d64a0434f02d9dd4a5 Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Thu, 17 Nov 2022 22:22:07 -0600 Subject: [PATCH 25/29] Change inline codeblocks to clear CI --- tutorials/introductory/animation_tutorial.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index 53822ca2b813..3ddeacb67371 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -59,9 +59,9 @@ # - 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. +# 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 @@ -69,7 +69,7 @@ # `.Axes.plot` `.lines.Line2D` `.lines.Line2D.set_data` # `.Axes.scatter` `.collections.PathCollection` `.collections.PathCollecti\ # on.set_offsets` -# `.Axes.imshow` `.image.AxesImage` `.image.AxesImage.set_data` +# `.Axes.imshow` `.image.AxesImage` ``AxesImage.set_data`` # `.Axes.annotate` `.text.Annotation` `.text.Annotation.update_p\ # ositions` # `.Axes.barh` `.patches.Rectangle` `.Rectangle.set_angle`, From c0ea38d9ac506ef3798c2fe6fd4f6b97af8c49e2 Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Sat, 19 Nov 2022 00:02:44 -0600 Subject: [PATCH 26/29] Make small corrections as per code review --- tutorials/introductory/animation_tutorial.py | 49 ++++++++++---------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index 3ddeacb67371..95244192f08c 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -1,9 +1,9 @@ """ =========================== -Animations using matplotlib +Animations using Matplotlib =========================== -Based on its plotting functionality, matplotlib also provides an interface to +Based on its plotting functionality, Matplotlib also provides an interface to generate animations using the :class:`~matplotlib.animation` module. An animation is a sequence of frames where each frame corresponds to a plot on a :class:`~matplotlib.figure.Figure`. This tutorial covers a general guideline on @@ -18,8 +18,7 @@ # Animation Classes # ================= # -# The process of animation in matplotlib can be thought about in 2 different -# ways: +# The animation process in Matplotlib can be thought of in 2 different ways: # # - :class:`~matplotlib.animation.FuncAnimation`: Generate data for first # frame and then modify this data for each frame to create an animated plot. @@ -32,17 +31,16 @@ # other hand :class:`~matplotlib.animation.ArtistAnimation` is flexible as it # allows any iterable of artists to be animated in a sequence. # -# :class:`~matplotlib.animation.FuncAnimation` -# -------------------------------------------- +# ``FuncAnimation`` +# ----------------- # -# :class:`~matplotlib.animation.FuncAnimation` class allows us to create an +# 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 -# :class:`~matplotlib.artist.Artist` -# (examples: :class:`~matplotlib.lines.Line2D`, -# :class:`~matplotlib.collections.PathCollection`, etc.). A usual -# :class:`~matplotlib.animation.FuncAnimation` object takes a -# :class:`~matplotlib.figure.Figure` that we want to animate and a function +# `~matplotlib.artist.Artist` (examples: :class:`~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. @@ -55,7 +53,8 @@ # - 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. +# function, along with the keyword arguments that determine the animation +# properties. # - Use `.animation.Animation.save` or `.pyplot.show` to save or show the # animation. # @@ -67,11 +66,11 @@ # Plotting method Artist Set method # ================= ============================= =========================== # `.Axes.plot` `.lines.Line2D` `.lines.Line2D.set_data` -# `.Axes.scatter` `.collections.PathCollection` `.collections.PathCollecti\ -# on.set_offsets` +# `.Axes.scatter` `.collections.PathCollection` `.collections.\ +# PathCollection.set_offsets` # `.Axes.imshow` `.image.AxesImage` ``AxesImage.set_data`` -# `.Axes.annotate` `.text.Annotation` `.text.Annotation.update_p\ -# ositions` +# `.Axes.annotate` `.text.Annotation` `.text.Annotation.\ +# update_positions` # `.Axes.barh` `.patches.Rectangle` `.Rectangle.set_angle`, # `.Rectangle.set_bounds`, # `.Rectangle.set_height`, @@ -102,7 +101,7 @@ def update(frame): - # .set_offsets also resets the offset data for the entire collection with + # .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. @@ -118,8 +117,8 @@ def update(frame): ############################################################################### -# :class:`~matplotlib.animation.ArtistAnimation` -# ---------------------------------------------- +# ``ArtistAnimation`` +# ------------------- # # :class:`~matplotlib.animation.ArtistAnimation` can be used # to generate animations if there is data stored on various different artists. @@ -133,7 +132,7 @@ def update(frame): fig, ax = plt.subplots() -rng = np.random.default_rng() +rng = np.random.default_rng(19680801) data = np.array([20, 20, 20, 20]) x = np.array([1, 2, 3, 4]) @@ -159,7 +158,7 @@ def update(frame): # - :class:`~matplotlib.animation.PillowWriter` - Uses the Pillow library to # create the animation. # -# - :class:`~matplotlib.animation.HTMLWriter` - Used to create JS-based +# - :class:`~matplotlib.animation.HTMLWriter` - Used to create JavaScript-based # animations. # # - Pipe-based writers - :class:`~matplotlib.animation.FFMpegWriter` and @@ -195,7 +194,7 @@ def update(frame): # **saved** animation uses, whereas *interval* determines the frame rate that # the **displayed** animation uses. # -# Following are a few examples showing how to save an animation with different +# Below are a few examples that show how to save an animation with different # writers. # # @@ -221,5 +220,5 @@ def update(frame): # 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*. +# ``writer="ffmpeg"`` and ``writer="imagemagick"`` support all formats +# supported by *ffmpeg* and *imagemagick*. From cbef186c5cb1ded046f3d4ab287cae06681826e6 Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Sat, 19 Nov 2022 01:08:21 -0600 Subject: [PATCH 27/29] Remove :class: from links --- tutorials/introductory/animation_tutorial.py | 44 ++++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index 95244192f08c..9eb1ede1140a 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -4,9 +4,9 @@ =========================== Based on its plotting functionality, Matplotlib also provides an interface to -generate animations using the :class:`~matplotlib.animation` module. An +generate animations using the `~matplotlib.animation` module. An animation is a sequence of frames where each frame corresponds to a plot on a -:class:`~matplotlib.figure.Figure`. This tutorial covers a general guideline on +`~matplotlib.figure.Figure`. This tutorial covers a general guideline on how to create such animations and the different options available. """ @@ -20,15 +20,15 @@ # # The animation process in Matplotlib can be thought of in 2 different ways: # -# - :class:`~matplotlib.animation.FuncAnimation`: Generate data for first +# - `~matplotlib.animation.FuncAnimation`: Generate data for first # frame and then modify this data for each frame to create an animated plot. # -# - :class:`~matplotlib.animation.ArtistAnimation`: Generate a list (iterable) +# - `~matplotlib.animation.ArtistAnimation`: Generate a list (iterable) # of artists that will draw in each frame in the animation. # -# :class:`~matplotlib.animation.FuncAnimation` is more efficient in terms of +# `~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 :class:`~matplotlib.animation.ArtistAnimation` is flexible as it +# other hand `~matplotlib.animation.ArtistAnimation` is flexible as it # allows any iterable of artists to be animated in a sequence. # # ``FuncAnimation`` @@ -37,7 +37,7 @@ # 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: :class:`~matplotlib.lines.Line2D`, +# `~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 @@ -120,7 +120,7 @@ def update(frame): # ``ArtistAnimation`` # ------------------- # -# :class:`~matplotlib.animation.ArtistAnimation` can be used +# `~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 @@ -155,19 +155,19 @@ def update(frame): # (ex: Pillow, *ffpmeg*, *imagemagick*). Not all video formats are supported # by all writers. There are 4 major types of writers: # -# - :class:`~matplotlib.animation.PillowWriter` - Uses the Pillow library to +# - `~matplotlib.animation.PillowWriter` - Uses the Pillow library to # create the animation. # -# - :class:`~matplotlib.animation.HTMLWriter` - Used to create JavaScript-based +# - `~matplotlib.animation.HTMLWriter` - Used to create JavaScript-based # animations. # -# - Pipe-based writers - :class:`~matplotlib.animation.FFMpegWriter` and -# :class:`~matplotlib.animation.ImageMagickWriter` are pipe based writers. +# - 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 - :class:`~matplotlib.animation.FFMpegFileWriter` and -# :class:`~matplotlib.animation.ImageMagickFileWriter` are examples of +# - 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. @@ -175,15 +175,15 @@ def update(frame): # Saving Animations # ----------------- # -# ================================================ =========================== -# Writer Supported Formats -# ================================================ =========================== -# :class:`~matplotlib.animation.PillowWriter` .gif, .apng -# :class:`~matplotlib.animation.HTMLWriter` .htm, .html, .png -# :class:`~matplotlib.animation.FFMpegWriter` All formats supported by +# ========================================== =========================== +# Writer Supported Formats +# ========================================== =========================== +# `~matplotlib.animation.PillowWriter` .gif, .apng +# `~matplotlib.animation.HTMLWriter` .htm, .html, .png +# `~matplotlib.animation.FFMpegWriter` All formats supported by # *ffmpeg* -# :class:`~matplotlib.animation.ImageMagickWriter` .gif -# ================================================ =========================== +# `~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 From 1def505be49e7663a477b39271c02b3e32c88d1a Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Sat, 19 Nov 2022 01:08:48 -0600 Subject: [PATCH 28/29] Set the animation range so that it loops --- tutorials/introductory/animation_tutorial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index 9eb1ede1140a..61a48352bc3c 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -91,7 +91,7 @@ fig, ax = plt.subplots() -t = np.linspace(-4, 4, 400) +t = np.linspace(-4, 2.3, 400) a, b = 3, 2 delta = np.pi / 2 From b5af4e6587a38db2504b7743dad2b8f3c492c392 Mon Sep 17 00:00:00 2001 From: Chahak Mehta Date: Sat, 19 Nov 2022 14:07:44 -0600 Subject: [PATCH 29/29] Update to better animation range for scatter --- tutorials/introductory/animation_tutorial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorials/introductory/animation_tutorial.py b/tutorials/introductory/animation_tutorial.py index 61a48352bc3c..a3996a82f14c 100644 --- a/tutorials/introductory/animation_tutorial.py +++ b/tutorials/introductory/animation_tutorial.py @@ -91,7 +91,7 @@ fig, ax = plt.subplots() -t = np.linspace(-4, 2.3, 400) +t = np.linspace(-np.pi, np.pi, 400) a, b = 3, 2 delta = np.pi / 2