Matplotlib ArtistAnimation: Plot entire figure in

2020-08-01 06:43发布

问题:

I have an existing function I use for plotting, which I call repeatedly in my program. I want to use matplotlib's ArtistAnimation to save each plot as an "artist" that is shown in one step of the animation.

I know how to use ArtistAnimation to show individual elements of the plot in the animation, but not the entire plot.

Here's a simplified example:

import random

def my_plot():
    fig, ax = plt.subplots()
    ax.plot([random.randrange(10), random.randrange(10)], [random.randrange(10), random.randrange(10)])
    ax.plot([random.randrange(10), random.randrange(10)], [random.randrange(10), random.randrange(10)])
    plt.show()
    return ax

ims = []
fig = plt.figure()
for _ in range(5):
    ax = my_plot()
    ims.append((ax,))
ani = animation.ArtistAnimation(fig, ims, repeat=False)
ani.save('im.mp4', metadata={'artist':'Guido'})

This runs without error, but the resulting video is just blank. The same happens if I return a list of the artists created by ax.plot().

I assume the problem is that I'm calling plt.figure/plt.subfigure multiple times. But I'm not sure how to avoid that. Do I need to create one figure up front and pass that to each call of my_plot? Seems a bit ugly.

回答1:

Instead of saving the axes, you need to save the plots as a list. (Or maybe you don't want to do this and want to save the axes? If that's the case, let me know and I'll delete this. I don't think saving the axes will work though, since the animation works by setting the saved items within a figure visible and invisible, and neither the axes nor the figure will hide/reveal a subset of the plots for each frame in this way.)

import matplotlib.pyplot as plt
from matplotlib import animation
import random

def my_plot(ax):
    p0, = ax.plot([random.randrange(10), random.randrange(10)], [random.randrange(10), random.randrange(10)])
    p1, = ax.plot([random.randrange(10), random.randrange(10)], [random.randrange(10), random.randrange(10)])
    return [p0, p1]   # return a list of the new plots

ims = []
fig = plt.figure()
ax = fig.add_subplot(111)  # fig and axes created once
for _ in range(10):
    ps = my_plot(ax)
    ims.append(ps)      # append the new list of plots
ani = animation.ArtistAnimation(fig, ims, repeat=False)
ani.save('im.mp4', metadata={'artist':'Guido'})

GIF below, but here is some vertical spacing so you can scroll the annoying flashing lines of the page while reading the code

.
.
.
.
.
.
.
.
.
.
.
.
.
.



回答2:

Thanks to tom's answer, I found the main reasons why my animations didn't work and only showed the first frame: I called plt.show() in each iteration. Apparently, after the first call, the animations stop working. Removing plt.show() and only creating one figure solved the problem:

import matplotlib.pyplot as plt
from matplotlib import animation
import random

def my_plot():
    patch = []
    patch.extend(plot([random.randrange(10), random.randrange(10)], [random.randrange(10), random.randrange(10)]))
    patch.extend(plt.plot([random.randrange(10), random.randrange(10)], [random.randrange(10), random.randrange(10)]))
    # no plt.show() here!
    return patch

ims = []
fig = plt.figure()   # fig created only once
for _ in range(10):
    patch = my_plot()
    ims.append(patch)  
ani = animation.ArtistAnimation(fig, ims, repeat=False)
ani.save('im.mp4', metadata={'artist':'Guido'})

Not sure how I could both plot and show the plots directly and create an animation. Maybe using plt.draw() instead? But that doesn't show anything in my PyCharm IDE... Anyways, I can live with either or.