Matplotlib动画FuncAnimation帧参数

5

我正在使用matplotlib动画,调用方式如下:

plot = animation.FuncAnimation(fig, update, frames=data_gen(a), init_func=init, interval=10, blit=True)

这里,“a”是数据生成函数data_gen的初始值,它长这样:

data_gen(x)
    old_x = x
    while True:
        new_x = func(old_x)
        old_x = new_x
        yield new_x

这段代码的意图是,当动态图形绘制更新时,data_gen会生成一个新值来替换new_x变量。
但是......实际上发生了这样的情况:
animation.py文件在FuncAnimation类的init()方法中出现了错误。
问题发生在以下代码中:
elif iterable(frames):
    self._iter_gen = lambda: iter(frames)
    self.save_count = len(frames)

错误为"TypeError: object of type 'generator' has no len()"。

看起来data_gen可迭代,但没有长度。

下面是FuncAnimation类中init()方法的更多代码:

    # Set up a function that creates a new iterable when needed. If nothing
    # is passed in for frames, just use itertools.count, which will just
    # keep counting from 0. A callable passed in for frames is assumed to
    # be a generator. An iterable will be used as is, and anything else
    # will be treated as a number of frames.
    if frames is None:
        self._iter_gen = itertools.count
    elif isinstance(frames, collections.Callable):
        self._iter_gen = frames
    elif iterable(frames):
        self._iter_gen = lambda: iter(frames)
        self.save_count = len(frames)
    else:
        self._iter_gen = lambda: iter(list(range(frames)))
        self.save_count = frames

我不确定为什么我的data_gen不是collections.Callable。如果是这样的话,那么len(frames)就不会发生了。

如果您有任何关于我应该怎么做的建议,将不胜感激!


请查看:bug: github.com/matplotlib/matplotlib/issues/1769 PR: github.com/matplotlib/matplotlib/pull/2634(感谢@tcaswell) - Riccati
1个回答

2
解决方案是:要么A)预先生成所有数据并将其压入列表中(如果您确实有有限数量的框架),即data_list = list(data_gen);B)从我的分支安装源代码来解决这个问题:PR #2634 或者C)使用monkey patch matplotlib,在运行时仅用修复后的代码替换您安装中的有错误的代码。 C)是最有趣的;)
# copied directly from the proposed fix
def monkey_patch_init(self, fig, func, frames=None, init_func=None, fargs=None,
             save_count=None, **kwargs):
    if fargs:
        self._args = fargs
    else:
        self._args = ()
    self._func = func

    # Amount of framedata to keep around for saving movies. This is only
    # used if we don't know how many frames there will be: in the case
    # of no generator or in the case of a callable.
    self.save_count = save_count

    # Set up a function that creates a new iterable when needed. If nothing
    # is passed in for frames, just use itertools.count, which will just
    # keep counting from 0. A callable passed in for frames is assumed to
    # be a generator. An iterable will be used as is, and anything else
    # will be treated as a number of frames.
    if frames is None:
        self._iter_gen = itertools.count
    elif six.callable(frames):
        self._iter_gen = frames
    elif iterable(frames):
        self._iter_gen = lambda: iter(frames)
        if hasattr(frames, '__len__'):
            self.save_count = len(frames)
    else:
        self._iter_gen = lambda: xrange(frames).__iter__()
        self.save_count = frames

    # If we're passed in and using the default, set it to 100.
    if self.save_count is None:
        self.save_count = 100

    self._init_func = init_func

    # Needs to be initialized so the draw functions work without checking
    self._save_seq = []

    TimedAnimation.__init__(self, fig, **kwargs)

    # Need to reset the saved seq, since right now it will contain data
    # for a single frame from init, which is not what we want.
    self._save_seq = []

现在我们有一个名为monkey_patch_init的函数,这是我认为已经修复的代码。我们现在只需用这个函数替换有问题的__init__函数即可:
matplotlib.animation.FuncAnimation.__init__ = monkey_patch_init

并且你的动画应该正常工作。
ani = animation.FuncAnimation(fig, update, frames=data_gen(a), init_func=init, interval=10, blit=True)

作为一个附注,不要使用“plot”作为变量名。很多人会将“pyplot”导入其命名空间中(例如通过“ipython --pylab”),其中包含“plot” -> “matplotlib.pyplot.plot” -> “plt.gca().plot”,因此这使得你的代码更容易混淆他人。

谢谢@tcaswell。我做了更糟糕的事情?!我只是注释掉了这一行:self.save_count = len(frames)(而且我不再使用“plot”作为变量名)。 - Riccati
我的第一个猴子补丁。很丑,但对我有用。 :)我还不得不添加:import six; from matplotlib.cbook import iterable; from matplotlib.animation import TimedAnimation - Greg Allen
@gregallan,带有此修复程序的mpl稳定版本已经发布了一年多,您应该升级。 - tacaswell
@tcaswell 这是在完全更新的RHEL7.1上,该版本提供python-matplotlib-1.2.0-15.el7。我完全同意RH应该升级。在此期间,感谢您提供的解决方法!我想我不是唯一一个使用RHEL7的人。 - Greg Allen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接