Wie animiere ich ein Streudiagramm?

80

Ich versuche, eine Animation eines Streudiagramms zu erstellen, bei der sich Farben und Größe der Punkte in verschiedenen Phasen der Animation ändern. Für Daten habe ich zwei numpy ndarray mit einem x-Wert und einem y-Wert:

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

Jetzt möchte ich ein Streudiagramm des Typs zeichnen

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

und erstellen Sie eine Animation über dem Index i. Wie mache ich das?

Nicola Vianello
quelle
1
Es gibt ein Beispiel in der matplotlib Dokumentation: Regen - Simulation .
ImportanceOfBeingErnest

Antworten:

134

Angenommen, Sie haben ein Streudiagramm scat = ax.scatter(...), dann können Sie

  • Ändern Sie die Positionen

        scat.set_offsets(array)
    

    wo arrayist eine N x 2geformte Anordnung von x- und y-Koordinaten.

  • Ändern Sie die Größen

        scat.set_sizes(array)
    

    Wo arrayist ein 1D-Array von Größen in Punkten.

  • ändere die Farbe

        scat.set_array(array)
    

    Dabei arrayhandelt es sich um ein 1D-Array von Werten, die farblich zugeordnet werden.

Hier ist ein kurzes Beispiel mit dem Animationsmodul .
Es ist etwas komplexer als es sein muss, aber dies sollte Ihnen einen Rahmen geben, um schickere Dinge zu tun.

( - Code bearbeiten 2019 April mit dem aktuellen Versionen kompatibel zu sein. Für den älteren Code siehe Revisionsgeschichte )

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).T
        self.scat = self.ax.scatter(x, y, c=c, s=s, vmin=0, vmax=1,
                                    cmap="jet", edgecolor="k")
        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."""
        xy = (np.random.random((self.numpoints, 2))-0.5)*10
        s, c = np.random.random((self.numpoints, 2)).T
        while True:
            xy += 0.03 * (np.random.random((self.numpoints, 2)) - 0.5)
            s += 0.05 * (np.random.random(self.numpoints) - 0.5)
            c += 0.02 * (np.random.random(self.numpoints) - 0.5)
            yield np.c_[xy[:,0], xy[:,1], s, c]

    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.set_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,


if __name__ == '__main__':
    a = AnimatedScatter()
    plt.show()

Geben Sie hier die Bildbeschreibung ein

Wenn Sie auf OSX sind und die OSX - Backend verwenden, müssen Sie ändern , blit=Trueum blit=Falsein der FuncAnimationInitialisierung unten. Das OSX-Backend unterstützt das Blitting nicht vollständig. Die Leistung wird darunter leiden, aber das Beispiel sollte unter OSX mit deaktiviertem Blitting korrekt ausgeführt werden.


Ein einfacheres Beispiel, bei dem nur die Farben aktualisiert werden, finden Sie in den folgenden Abschnitten:

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()
Joe Kington
quelle
Hallo Joe, ich habe dein erstes Beispiel ausprobiert, aber es funktioniert nicht, während das zweite ja. Vielleicht werde ich versuchen, die erste Option zu debuggen, dies wird mir helfen, mein Python-Wissen zu verbessern. Vielen Dank
Nicola Vianello
1
Leider wird das erste Beispiel für mich auch nicht mit matplotlib 1.3.1 unter OS X angezeigt. Ich bekomme den Frame gesetzt, es werden keine Punkte angezeigt. Das zweite Beispiel funktioniert.
JoshAdel
6
Wie IN DER WELT haben Sie herausgefunden, dass .set_array()die Punktfarbe aktualisiert wird?!
Lucas
1
Das erste Beispiel funktioniert nicht. Sie müssen die Zeile self.Scat.set_offsets(data[:2, :]) auf self.scat.set_offsets(data[:2, :].reshape(self.numpoints, 2))
AN O'Nyme
2
Gibt es eine Funktion, die die Markierungen der Streupunkte ändert?
Constantinos
13

Ich habe Zelluloid geschrieben , um dies zu vereinfachen. Es ist wahrscheinlich am einfachsten, anhand eines Beispiels zu zeigen:

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

Geben Sie hier die Bildbeschreibung ein

Es wird ArtistAnimationunter der Haube verwendet. camera.snapErfasst den aktuellen Status der Figur, mit der die Frames in der Animation erstellt werden.

Bearbeiten: Um zu quantifizieren, wie viel Speicher dies verwendet, habe ich es durch memory_profiler ausgeführt .

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

Um dies zusammenzufassen:

  • Erstellen von 100 Plots verwendet 1,9 MiB.
  • Erstellen der verwendeten Animation 2,3 MiB.
  • Bei dieser Methode zum Erstellen von Animationen wurden insgesamt 4,2 MiB Speicher verwendet.
Jacques Kvam
quelle
Da dies ArtistAnimation verwendet, werden 100 Streudiagramme im Speicher erstellt, was ziemlich ineffizient ist. Verwenden Sie diese Option nur, wenn die Leistung für Sie nicht kritisch ist.
ImportanceOfBeingErnest
1
Die Speicherprofilerstellung ist eine gute Idee. Hast du das auch für das getan FuncAnimation? Was sind die Unterschiede?
ImportanceOfBeingErnest
1
Wie können Sie die Animation wiedergeben (um sie in einer Datei zu speichern)?
argentum2f
5

Hier ist das Ding. Ich war an einen Benutzer von Qt und Matlab gewöhnt und bin mit dem Animationssystem auf der matplotlib nicht ganz vertraut.

Aber ich habe einen Weg gefunden, mit dem Sie jede Art von Animation erstellen können, die Sie möchten, genau wie in Matlab. Es ist wirklich mächtig. Sie müssen die Modulreferenzen nicht überprüfen, und Sie können alles zeichnen, was Sie möchten. Also ich hoffe es kann helfen.

Die Grundidee ist, das Zeitereignis in PyQt zu verwenden (ich bin sicher, dass andere Gui-Systeme auf Python wie wxPython und TraitUi den gleichen inneren Mechanismus haben, um eine Ereignisantwort zu erstellen. Aber ich weiß einfach nicht wie). Jedes Mal, wenn ein PyQt-Timer-Ereignis aufgerufen wird, aktualisiere ich die gesamte Leinwand und zeichne das gesamte Bild neu. Ich weiß, dass Geschwindigkeit und Leistung langsam beeinflusst werden können, aber es ist nicht so viel.

Hier ist ein kleines Beispiel dafür:

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 __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    w = Monitor()
    w.setWindowTitle("Convergence")
    w.show()
    sys.exit(app.exec_())

Sie können die Aktualisierungsgeschwindigkeit im einstellen

        self.timer = self.startTimer(100)

Ich bin genau wie Sie, die das animierte Streudiagramm verwenden möchten, um eine Sortieranimation zu erstellen. Aber ich kann einfach keine sogenannte "Set" -Funktion finden. Also habe ich die ganze Leinwand aufgefrischt.

Ich hoffe es hilft..

tk_y1275963
quelle
Wirklich nett! Ich habe jedoch keine Änderung der Aktualisierungsrate durch Anpassen des self.startTimerWerts erhalten ... irgendwelche Tipps dazu? (Ja, ich weiß, es ist eine Weile her ...)
H. Arponen
0

Warum nicht versuchen?

import numpy as np
import matplotlib.pyplot as plt

x=np.random.random()
y=np.random.random()

fig, ax = plt.subplots()
ax.scatter(x,y,color='teal')
ax.scatter(y,x,color='crimson')
ax.set_xlim([0,1])
ax.set_ylim([0,1])

for i in np.arange(50):
    x=np.random.random()
    y=np.random.random()
    bha=ax.scatter(x,y)
    plt.draw()
    plt.pause(0.5)
    bha.remove()

plt.show()
Bharath C.
quelle