There are two ways to create animations with matplotlib: matplotlib.animation.ArtistAnimation
and matplotlib.animation.FuncAnimation
. ʻArtistAnimationis easier and you can create it as you wish. However, there was a problem that it was heavy because all the elements were created in advance and kept in memory.
FuncAnimation` seems to be lighter because it is generated sequentially, but I do not understand the behavior well and it is always troublesome. I wrote out the serial number images and converted them to mp4 later. So I studied with a lot of effort to understand and use them. This is a summary note.
If you pass the information needed to create the animation to matplotlib.animation.FuncAnimation
, it will create the animation, so what you need to understand is its arguments and the behavior of this function.
The first two arguments are required, and you will usually set the arguments frames
, ʻinterval` in addition to them.
--The first argument fig
is Figure
. This is something I use a lot, so I don't need to explain it.
--The second argument, func
, is the main subject. We will see this in detail in the next bar.
--The argument frames
is an iterator that produces the equivalent of the" name "of each frame, for example passingrange (128)
ornp.linspace (0, 2 * np.pi, 128)
. In this case, both create an animation with 128 frames.
--The argument ʻinterval` specifies the time to switch frames in milliseconds. The default is 200.
FuncAnimation
The idea of FuncAnimation
is summarized in the following code example. (Quoted from API doc)
for d in frames:
artists = func(d, *fargs)
fig.canvas.draw_idle()
fig.canvas.start_event_loop(interval)
That is, iterator frames
is turned, each" frame name " d
is passed to the function func
, and the returned ʻartist is drawn. After this time ʻinterval
, frames
ends. Repeat until you do. That's it.
So let's do it now. Let's create an animation of the point P that moves 0, 1, 2, ... on the x-axis. Will it be?
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig = plt.figure()
ax = fig.add_subplot(111)
def update(frame):
ax.plot(frame, 0, "o")
anim = FuncAnimation(fig, update, frames=range(8), interval=1000)
anim.save("c01.gif", writer="imagemagick") #If you want to display it on the screen plt.show()Is good
plt.close()
Click here for the results.
It's not what I intended. What's different is that the past position of the point P drawn in the previous frame is still drawn. To fix this, ʻupdate` It seems that all the elements written in axes should be cleared at the beginning of the function.
So here is the modified code and execution results.
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig = plt.figure()
ax = fig.add_subplot(111)
def update(frame):
ax.cla() #Clear ax
ax.plot(frame, 0, "o")
anim = FuncAnimation(fig, update, frames=range(8), interval=1000)
anim.save("c02.gif", writer="imagemagick")
plt.close()
The animation was as intended.
Note that calling ʻax.cla () clears the axis label ʻax.set_xlabel ()
, grid lines ʻax.grid () , drawing range ʻax.set_xlim ()
, etc. You need to reconfigure after reading ʻax.cla () `.
In the above example, all the drawing contents were erased at each frame, but I think that you often want to clear only some artists. For example, the following example is a point that moves on the unit circle P Although it is an animation of, it seems to be useless because I draw a grid and a unit circle every time.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig = plt.figure()
ax = fig.add_subplot(111, aspect=1)
theta = np.linspace(0, 2*np.pi, 128)
def update(f):
ax.cla() #Clear ax
ax.grid()
ax.plot(np.cos(theta), np.sin(theta), c="gray")
ax.plot(np.cos(f), np.sin(f), "o", c="red")
anim = FuncAnimation(fig, update, frames=np.pi*np.arange(0,2,0.25), interval=200)
anim.save("c03.gif", writer="imagemagick")
plt.close()
In this case, instead of clearing everything, you can achieve your goal by explicitly removing the Artist you want to erase.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig = plt.figure()
ax = fig.add_subplot(111, aspect=1)
theta = np.linspace(0, 2*np.pi, 128)
p = []
ax.grid()
ax.plot(np.cos(theta), np.sin(theta), c="gray")
def update(f):
if len(p) > 0:
item = p.pop(0)
ax.lines.remove(item)
p.append( ax.plot([np.cos(f)], [np.sin(f)], "o", c="red")[0] )
anim = FuncAnimation(fig, update, frames=np.pi*np.arange(0,2,0.25), interval=200)
anim.save("c03.gif", writer="imagemagick")
plt.close()
However, I'm not sure which of these two examples is better in terms of drawing speed, although it seems better to use it with blit = True
in the FuncAnimation argument.
If you use plt.cla ()
to clear the drawing content, this has the same meaning as ʻax.cla () (ʻax
is the Axes you touched immediately before), so multiple subplots. Animations with have some subplots that fail to clear what is displayed. For all Axes, let's do ʻax.cla () `.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
fig = plt.figure()
axs = [
fig.add_subplot(121),
fig.add_subplot(122, aspect=1),
]
theta = np.linspace(0, 4*np.pi, 128)
def update(f):
for ax in axs:
ax.cla() #Clear ax
ax.grid()
axs[0].set_xlim([0, 4*np.pi])
axs[0].plot(theta, np.cos(theta - f), c="blue")
axs[0].plot(0, np.cos(f), "o", c="blue")
axs[1].plot(np.cos(theta), np.sin(theta), c="gray")
axs[1].plot(np.cos(f), np.sin(f), "o", c="blue")
axs[1].plot(np.cos(f), 0 , "o", c="blue")
axs[1].plot([np.cos(f), np.cos(f)], [0, np.sin(f)], c="blue")
anim = FuncAnimation(fig, update, frames=np.pi*np.arange(0,2,0.1), interval=200)
anim.save("c04.gif", writer="imagemagick")
plt.close()
Recommended Posts