使用 matplotlib 保存散点图动画

Posted

技术标签:

【中文标题】使用 matplotlib 保存散点图动画【英文标题】:Saving scatterplot animations with matplotlib 【发布时间】:2013-01-22 07:04:10 【问题描述】:

我一直在尝试使用 matplotlib 保存动画散点图,我希望它不需要完全不同的代码来查看动画图形和保存副本。该图完美显示了保存完成后的所有数据点。

此代码是Animating 3d scatterplot in matplotlib 上Giggi's 的修改版本,修复了Yann's answer 上Matplotlib 3D scatter color lost after redraw 上的颜色(因为颜色在我的视频中很重要,所以我想确保它们有效)。

import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
from mpl_toolkits.mplot3d import Axes3D

FLOOR = -10
CEILING = 10

class AnimatedScatter(object):
    def __init__(self, numpoints=5):
        self.numpoints = numpoints
        self.stream = self.data_stream()
        self.angle = 0

        self.fig = plt.figure()
        self.fig.canvas.mpl_connect('draw_event',self.forceUpdate)
        self.ax = self.fig.add_subplot(111,projection = '3d')
        self.ani = animation.FuncAnimation(self.fig, self.update, interval=100, 
                                       init_func=self.setup_plot, blit=True,frames=20)

    def change_angle(self):
        self.angle = (self.angle + 1)%360

    def forceUpdate(self, event):
        self.scat.changed()

    def setup_plot(self):
        X = next(self.stream)
        c = ['b', 'r', 'g', 'y', 'm']
        self.scat = self.ax.scatter(X[:,0], X[:,1], X[:,2] , c=c, s=200, animated=True)

        self.ax.set_xlim3d(FLOOR, CEILING)
        self.ax.set_ylim3d(FLOOR, CEILING)
        self.ax.set_zlim3d(FLOOR, CEILING)

        return self.scat,

    def data_stream(self):
        data = np.zeros(( self.numpoints , 3 ))
        xyz = data[:,:3]
        while True:
            xyz += 2 * (np.random.random(( self.numpoints,3)) - 0.5)
            yield data

    def update(self, i):
        data = next(self.stream)
        #data = np.transpose(data)

        self.scat._offsets3d = ( np.ma.ravel(data[:,0]) , np.ma.ravel(data[:,1]) , np.ma.ravel(data[:,2]) )

        plt.draw()
        return self.scat,

    def show(self):
        plt.show()

if __name__ == '__main__':
    a = AnimatedScatter()
    a.ani.save("movie.avi", codec='avi')
    a.show()

由此生成了一个完全有效的 .avi,但除了轴之外,它在所有四秒内都是空白的。实际的数字总是准确地显示我想看到的。如何像填充正常运行的动画一样填充保存函数的绘图,或者在 matplotlib 中是否可以?

编辑:在更新中使用分散调用(没有像在初始化程序中那样设置边界)导致 .avi 显示轴增长,表明每次都在运行数据,它是只是没有在视频本身上显示。 我正在将 matplotlib 1.1.1rc 与 Python 2.7.3 一起使用。

【问题讨论】:

你能修正你的缩进吗? 哎呀。我应该更小心地复制和粘贴。虽然我的来源有空格而不是制表符,所以我不太清楚发生了什么。 如果你在图中看这个看起来还可以吗?你用的是什么版本的 MPL? 数字不是问题,.avi 是。我确实注意到它在第一段中有效,但让我对其进行编辑以强调。我使用的是 1.1.1rc 版本。 如果你从update 中删除plt.draw() 它会在图中中断,我认为这是一个线索。 【参考方案1】:

FuncAnimation 中删除blit=True 和从scatter 中删除animated=True 并且它可以工作。我怀疑确保只有需要更新的艺术家在帧之间更新/重绘(而不是仅仅重绘所有内容)的逻辑出了问题。

下面正是我运行的内容,我得到了预期的输出电影:

import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
from mpl_toolkits.mplot3d import Axes3D

FLOOR = -10
CEILING = 10

class AnimatedScatter(object):
    def __init__(self, numpoints=5):
        self.numpoints = numpoints
        self.stream = self.data_stream()
        self.angle = 0

        self.fig = plt.figure()
        self.fig.canvas.mpl_connect('draw_event',self.forceUpdate)
        self.ax = self.fig.add_subplot(111,projection = '3d')
        self.ani = animation.FuncAnimation(self.fig, self.update, interval=100, 
                                       init_func=self.setup_plot, frames=20)

    def change_angle(self):
        self.angle = (self.angle + 1)%360

    def forceUpdate(self, event):
        self.scat.changed()

    def setup_plot(self):
        X = next(self.stream)
        c = ['b', 'r', 'g', 'y', 'm']
        self.scat = self.ax.scatter(X[:,0], X[:,1], X[:,2] , c=c, s=200)

        self.ax.set_xlim3d(FLOOR, CEILING)
        self.ax.set_ylim3d(FLOOR, CEILING)
        self.ax.set_zlim3d(FLOOR, CEILING)

        return self.scat,

    def data_stream(self):
        data = np.zeros(( self.numpoints , 3 ))
        xyz = data[:,:3]
        while True:
            xyz += 2 * (np.random.random(( self.numpoints,3)) - 0.5)
            yield data

    def update(self, i):
        data = next(self.stream)
        self.scat._offsets3d = ( np.ma.ravel(data[:,0]) , np.ma.ravel(data[:,1]) , np.ma.ravel(data[:,2]) )
        return self.scat,

    def show(self):
        plt.show()

if __name__ == '__main__':
    a = AnimatedScatter()
    a.ani.save("movie.avi", codec='avi')
    a.show()

【讨论】:

成功了!我的实际项目使用了该修复程序。谢谢! 有趣的是,删除 blit 会有所帮助,考虑到在调用 _draw_next_frame 时,animation.save 代码将 blit 设置为 false。有机会时,我会将完整的错误报告(或至少愿望清单请求)添加到 matplotlib 的站点。 使用 SciPy 0.12.0、Numpy 1.7.1 和 Matplotlib 1.2.1 我无法使其工作。它充其量会创建一个视频,其中大量旧点被新点或空白图覆盖。欢迎您尝试我视频中链接的代码:youtube.com/watch?v=dCIOOjJ1Sk0 我终于使用了 CamStudio,它工作得更好,视频输出质量更高。

以上是关于使用 matplotlib 保存散点图动画的主要内容,如果未能解决你的问题,请参考以下文章

matplotlib 中带有散点图和使用 set_offsets 的动画:图形的自动缩放不起作用

使用matplotlib将3D散点图动画为gif最终为空

在 matplotlib 中为旋转的 3D 图形制作动画

用matplotlib画散点图

Matplotlib:等高线图顶部的前景散点图

matplot绘图