web-dev-qa-db-fra.com

Comment animer un nuage de points?

J'essaie de faire une animation d'un nuage de points où les couleurs et la taille des points changent à différentes étapes de l'animation. Pour les données, j'ai deux numpy ndarray avec une valeur x et une valeur y:

data.shape = (ntime, npoint)
x.shape = (npoint)
y.shape = (npoint)

Maintenant, je veux tracer un diagramme de dispersion du type

pylab.scatter(x,y,c=data[i,:])

et créez une animation sur l'index i. Comment puis-je faire cela?

40
Nicola Vianello

Voici un exemple rapide utilisant le nouveau module d'animation. 

C'est un peu plus complexe que cela ne devrait être, mais cela devrait vous donner un cadre pour faire des choses plus sophistiquées. 

Si vous utilisez OSX et utilisez le système d’exécution OSX, vous devrez remplacer blit=True par blit=False dans l’initialisation FuncAnimation ci-dessous. Le backend OSX ne supporte pas totalement le blitting. Les performances vont en souffrir, mais l'exemple doit fonctionner correctement sous OSX avec le blitting désactivé.

import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np

class AnimatedScatter(object):
    """An animated scatter plot using matplotlib.animations.FuncAnimation."""
    def __init__(self, numpoints=50):
        self.numpoints = numpoints
        self.stream = self.data_stream()

        # Setup the figure and axes...
        self.fig, self.ax = plt.subplots()
        # Then setup FuncAnimation.
        self.ani = animation.FuncAnimation(self.fig, self.update, interval=5, 
                                           init_func=self.setup_plot, blit=True)

    def setup_plot(self):
        """Initial drawing of the scatter plot."""
        x, y, s, c = next(self.stream)
        self.scat = self.ax.scatter(x, y, c=c, s=s, animated=True)
        self.ax.axis([-10, 10, -10, 10])

        # For FuncAnimation's sake, we need to return the artist we'll be using
        # Note that it expects a sequence of artists, thus the trailing comma.
        return self.scat,

    def data_stream(self):
        """Generate a random walk (brownian motion). Data is scaled to produce
        a soft "flickering" effect."""
        data = np.random.random((4, self.numpoints))
        xy = data[:2, :]
        s, c = data[2:, :]
        xy -= 0.5
        xy *= 10
        while True:
            xy += 0.03 * (np.random.random((2, self.numpoints)) - 0.5)
            s += 0.05 * (np.random.random(self.numpoints) - 0.5)
            c += 0.02 * (np.random.random(self.numpoints) - 0.5)
            yield data

    def update(self, i):
        """Update the scatter plot."""
        data = next(self.stream)

        # Set x and y data...
        self.scat.set_offsets(data[:2, :])
        # Set sizes...
        self.scat._sizes = 300 * abs(data[2])**1.5 + 100
        # Set colors..
        self.scat.set_array(data[3])

        # We need to return the updated artist for FuncAnimation to draw..
        # Note that it expects a sequence of artists, thus the trailing comma.
        return self.scat,

    def show(self):
        plt.show()

if __== '__main__':
    a = AnimatedScatter()
    a.show()

enter image description here

Pour un exemple plus simple, examinez les éléments suivants:

import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation

def main():
    numframes = 100
    numpoints = 10
    color_data = np.random.random((numframes, numpoints))
    x, y, c = np.random.random((3, numpoints))

    fig = plt.figure()
    scat = plt.scatter(x, y, c=c, s=100)

    ani = animation.FuncAnimation(fig, update_plot, frames=xrange(numframes),
                                  fargs=(color_data, scat))
    plt.show()

def update_plot(i, data, scat):
    scat.set_array(data[i])
    return scat,

main()
78
Joe Kington

Voici la chose. J'avais l'habitude d'utiliser un utilisateur de Qt et Matlab et je ne suis pas très familiarisé avec le système d'animation de matplotlib.

Mais j’ai trouvé un moyen de créer tout type d’animation que vous souhaitez, comme dans Matlab. C'est vraiment puissant. Pas besoin de vérifier les références des modules et vous êtes prêt à tracer ce que vous voulez. J'espère donc que cela peut aider.

L'idée de base est d'utiliser l'événement time dans PyQt (je suis sûr que d'autres systèmes Gui sur le Python, tels que wxPython et TraitUi, ont le même mécanisme interne pour générer une réponse à un événement. Mais je ne sais pas comment. Chaque fois que l'on appelle un événement PyQt Timer, j'actualise l'ensemble de la toile et redessine l'image entière, je sais que la vitesse et les performances peuvent être lentement influencées, mais ce n'est pas beaucoup. 

En voici un petit exemple:

import sys
from PyQt4 import QtGui

from matplotlib.figure import Figure
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas

import numpy as np


class Monitor(FigureCanvas):
    def __init__(self):
        self.fig = Figure()
        self.ax = self.fig.add_subplot(111)

        FigureCanvas.__init__(self, self.fig)
        self.x = np.linspace(0,5*np.pi,400)
        self.p = 0.0
        self.y = np.sin(self.x+self.p)


        self.line = self.ax.scatter(self.x,self.y)

        self.fig.canvas.draw()

        self.timer = self.startTimer(100)


    def timerEvent(self, evt):
        # update the height of the bars, one liner is easier
        self.p += 0.1
        self.y = np.sin(self.x+self.p)
        self.ax.cla()
        self.line = self.ax.scatter(self.x,self.y)

        self.fig.canvas.draw()



if __== "__main__":
    app = QtGui.QApplication(sys.argv)
    w = Monitor()
    w.setWindowTitle("Convergence")
    w.show()
    sys.exit(app.exec_())

Vous pouvez régler la vitesse de rafraîchissement dans le 

        self.timer = self.startTimer(100)

Je suis comme vous qui souhaitez utiliser le diagramme de dispersion animé pour créer une animation de tri. Mais je ne peux tout simplement pas trouver une fonction dite "set". J'ai donc rafraîchi toute la toile.

J'espère que ça aide..

3
tk_y1275963

J'ai écrit celluloïd pour rendre cela plus facile. Il est probablement plus facile de montrer par l'exemple:

import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
from celluloid import Camera

numpoints = 10
points = np.random.random((2, numpoints))
colors = cm.Rainbow(np.linspace(0, 1, numpoints))
camera = Camera(plt.figure())
for _ in range(100):
    points += 0.1 * (np.random.random((2, numpoints)) - .5)
    plt.scatter(*points, c=colors, s=100)
    camera.snap()
anim = camera.animate(blit=True)
anim.save('scatter.mp4')

 enter image description here

Il utilise ArtistAnimation sous le capot. camera.snap capture l'état actuel de la figure utilisée pour créer les images dans l'animation.

Edit: Pour quantifier la quantité de mémoire utilisée, je l’ai exécutée dans memory_profiler .

Line #    Mem usage    Increment   Line Contents
================================================
    11     65.2 MiB     65.2 MiB   @profile
    12                             def main():
    13     65.2 MiB      0.0 MiB       numpoints = 10
    14     65.2 MiB      0.0 MiB       points = np.random.random((2, numpoints))
    15     65.2 MiB      0.1 MiB       colors = cm.Rainbow(np.linspace(0, 1, numpoints))
    16     65.9 MiB      0.6 MiB       fig = plt.figure()
    17     65.9 MiB      0.0 MiB       camera = Camera(fig)
    18     67.8 MiB      0.0 MiB       for _ in range(100):
    19     67.8 MiB      0.0 MiB           points += 0.1 * (np.random.random((2, numpoints)) - .5)
    20     67.8 MiB      1.9 MiB           plt.scatter(*points, c=colors, s=100)
    21     67.8 MiB      0.0 MiB           camera.snap()
    22     70.1 MiB      2.3 MiB       anim = camera.animate(blit=True)
    23     72.1 MiB      1.9 MiB       anim.save('scatter.mp4')

Pour résumer ceci:

  • Création de 100 parcelles utilisées 1,9 Mio.
  • Rendre l'animation utilisée 2,3 Mio.
  • Cette méthode de création d’animations utilise en tout 4.2 Mio de mémoire.
1
Jacques Kvam