2013-02-06 14 views
5

Próbowałem zapisać animowany wykres rozrzutu za pomocą matplotlib, a ja wolałbym, żeby nie wymagał zupełnie innego kodu do oglądania jako animowana figura i do zapisywania kopii. Rysunek pokazuje wszystkie punkty danych idealnie po zakończeniu składowania.Zapisywanie animacji scatterplot z matplotlib

Kod ten jest zmodyfikowaną wersją Giggi's na Animating 3d scatterplot in matplotlib, z poprawką na kolory z Yann's answer na Matplotlib 3D scatter color lost after redraw (bo kolory będą ważne w moim filmie, więc chcę, aby upewnić się, że działa).

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() 

Generowane jest właśnie prawidłowe .avi, ale jest puste przez wszystkie cztery sekundy z wyjątkiem osi. Rzeczywista liczba zawsze pokazuje dokładnie to, co chcę zobaczyć. Jak mogę wypełnić wykresy funkcji składowania w ten sam sposób, w jaki zapełniam normalnie działającą animację, czy jest to możliwe w matplotlib?

EDIT: Korzystanie połączenia punktowego w aktualizacji (bez wyznaczania granic jak w inicjatora) powoduje .avi pokazać osie rośnie, pokazując, że dane są uruchamiane za każdym razem, to po prostu nie jest wyświetlana na sam film. Używam matplotlib 1.1.1rc z Pythonem 2.7.3.

+0

można naprawić wgniecenie? – tacaswell

+0

Whoops. Powinienem ostrożniej kopiować i wklejać. Chociaż moje źródło ma spacje zamiast kart, więc nie bardzo wiem, co się stało. – Poik

+0

i to wygląda dobrze, jeśli oglądasz go na rysunku? Jakiej wersji MPL używasz? – tacaswell

Odpowiedz

2

Usuń blit=True z FuncAnimation i animated=True z scatter i to działa. Podejrzewam, że coś jest nie tak z logiką, która sprawia, że ​​tylko artyści, którzy muszą być zaktualizowani, są uaktualniani/przerysowani między klatkami (zamiast tylko ponownie rysować wszystko).

Poniżej jest dokładnie to, co wpadłem i mam oczekiwany film wyjściowy:

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() 
+0

To załatwiło sprawę! Mój prawdziwy projekt pracował z tą poprawką. Dzięki! – Poik

+0

Ciekawe, że usunięcie 'blit' pomaga, biorąc pod uwagę, że kod animacji.save ustawia' blit' na false podczas wywoływania '_draw_next_frame'. Dodam pełne raporty o błędach (lub co najmniej prośby o listę życzeń) do witryny matplotlib, kiedy będę miał okazję. – Poik

+0

Z SciPy 0.12.0, Numpy 1.7.1 i Matplotlib 1.2.1 Nie mogłem sprawić, żeby to działało. W najlepszym przypadku powstał film z mnóstwem starych punktów wykreślonych nadpisanymi nowymi punktami lub pustym spisem. Zapraszamy do wypróbowania kodu, który jest połączony z moim wideo: https://www.youtube.com/watch?v=dCIOOjJ1Sk0 W końcu użyłem CamStudio i działało lepiej i miało wyższą jakość wideo. – Demolishun