LineCollection animation

放肆的年华 提交于 2021-01-29 09:56:15

问题


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)

回答1:


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)

The above now even allows to use an interactive backend, matplotlib.use('TkAgg') and showing the figure via plt.show().



来源:https://stackoverflow.com/questions/52723813/linecollection-animation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!