LineCollection 动画 [英] LineCollection animation
问题描述
对于以下代码(MWE),如果有一行
For the following code (MWE), if the line
ax.set_axis_off()
被注释掉,然后动画什么也不显示.否则,动画将按预期工作.我的问题是:为什么?
is commented out, then the animation shows nothing. Otherwise, the animation works as expected. My question is: why?
from itertools import tee
import numpy as np
import matplotlib
matplotlib.use('Agg') # noqa
from matplotlib.animation import FuncAnimation, FFMpegWriter
from matplotlib.collections import LineCollection
from matplotlib.colors import ListedColormap, BoundaryNorm
import matplotlib.pyplot as plt
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return zip(a, b)
def make_segs(x, y):
xs = np.array(list(pairwise(x))) # (n,2)
ys = np.array(list(pairwise(y))) # (n,2)
segs = np.stack((xs, ys), axis=-1) # (n,2,2)
return segs
class _AnimationHelper():
def __init__(self, xs, ys, z, colors=['r', 'g']):
self.segs = np.array([make_segs(x, y) for x, y in zip(xs, ys)])
self.z = z
self.colors = colors
self.fig = plt.figure()
self.lns = []
self.ani = FuncAnimation(
self.fig,
self.update,
interval=200,
init_func=self.init,
frames=self.segs.shape[1],
blit=True)
def init(self):
m, n = self.segs.shape[:2]
self.fig.set_size_inches(m*5, 5)
self.axes = self.fig.subplots(
nrows=1, ncols=m, sharey='row')
self.fig.tight_layout()
cmap = ListedColormap(self.colors)
norm = BoundaryNorm([0, 0.5, 1], cmap.N)
zero = np.zeros(n, dtype=np.int32)
for i in range(m):
inds = (zero + i) == self.z
ln = LineCollection([], cmap=cmap, norm=norm, lw=2, animated=True)
ln.set_array(inds)
self.lns.append(ln)
ax = self.axes[i]
ax.add_collection(ln)
ax.set_xlim(0, n)
ax.set_axis_off() # <---- HERE
self.axes[0].set_ylim(0, 1.1)
for i in range(1, m):
self.axes[i].tick_params(left=False)
return self.lns
def update(self, ind):
print(ind)
m = self.segs.shape[0]
for i in range(m):
self.lns[i].set_segments(self.segs[i, :(ind + 1)])
return self.lns
N = 20
M = 3
x = np.arange(N)
xs = np.repeat(x.reshape([1, -1]), M, axis=0)
ys = np.exp(-xs) + 0.05*np.random.random((M, N))
z = np.array([2]*4 + [1]*5 + [0]*5 + [2]*5)
helper = _AnimationHelper(xs, ys, z)
writer = FFMpegWriter(fps=5, codec='mpeg4')
helper.ani.save('out/test_saveani.mp4', writer=writer, dpi=80)
推荐答案
为了保存图形,不需要使用 blit
.如果不加说明,则该图将正确保存.但是,未考虑图形尺寸的更改,因为动画是在更改之前创建的.在创建动画之前创建图形和所有轴都可以正常工作,即使使用了 blitting.
For saving a figure, the use of blit
is not necessary. If that is left out, the figure is saved correctly. However, the figure size change is not taken into account, because the animation is created before that change. Creating the figure and all the axes before creating the animation works perfectly fine though, even with blitting being used.
from itertools import tee
import numpy as np
import matplotlib
matplotlib.use('Agg') # noqa
from matplotlib.animation import FuncAnimation, FFMpegWriter
from matplotlib.collections import LineCollection
from matplotlib.colors import ListedColormap, BoundaryNorm
import matplotlib.pyplot as plt
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return zip(a, b)
def make_segs(x, y):
xs = np.array(list(pairwise(x))) # (n,2)
ys = np.array(list(pairwise(y))) # (n,2)
segs = np.stack((xs, ys), axis=-1) # (n,2,2)
return segs
class _AnimationHelper():
def __init__(self, xs, ys, z, colors=['r', 'g']):
self.segs = np.array([make_segs(x, y) for x, y in zip(xs, ys)])
self.z = z
self.colors = colors
self.fig = plt.figure()
self.lns = []
m, n = self.segs.shape[:2]
self.fig.set_size_inches(m*5, 5, forward=True)
self.axes = self.fig.subplots(
nrows=1, ncols=m, sharey='row')
self.fig.tight_layout()
self.ani = FuncAnimation(
self.fig,
self.update,
interval=200,
init_func=self.init,
frames=self.segs.shape[1],
blit=True)
def init(self):
m, n = self.segs.shape[:2]
cmap = ListedColormap(self.colors)
norm = BoundaryNorm([0, 0.5, 1], cmap.N)
zero = np.zeros(n, dtype=np.int32)
for i in range(m):
inds = (zero + i) == self.z
ln = LineCollection([], cmap=cmap, norm=norm, lw=2, animated=True)
ln.set_array(inds)
self.lns.append(ln)
ax = self.axes[i]
ax.add_collection(ln)
ax.set_xlim(0, n)
#ax.set_axis_off() # <---- HERE
self.axes[0].set_ylim(0, 1.1)
for i in range(1, m):
self.axes[i].tick_params(left=False)
return self.lns
def update(self, ind):
print(ind)
m = self.segs.shape[0]
for i in range(m):
self.lns[i].set_segments(self.segs[i, :(ind + 1)])
return self.lns
N = 20
M = 3
x = np.arange(N)
xs = np.repeat(x.reshape([1, -1]), M, axis=0)
ys = np.exp(-xs) + 0.05*np.random.random((M, N))
z = np.array([2]*4 + [1]*5 + [0]*5 + [2]*5)
helper = _AnimationHelper(xs, ys, z)
writer = FFMpegWriter(fps=5, codec='mpeg4')
helper.ani.save('test_saveani.mp4', writer=writer, dpi=80)
以上内容甚至允许使用交互式后端 matplotlib.use('TkAgg')
并通过 plt.show()
显示该图.
The above now even allows to use an interactive backend, matplotlib.use('TkAgg')
and showing the figure via plt.show()
.
这篇关于LineCollection 动画的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!