Jak ożywić wykres punktowy?

83

Próbuję zrobić animację wykresu punktowego, w którym kolory i rozmiar punktów zmieniają się na różnych etapach animacji. Dla danych mam dwa numpy ndarray z wartością x i wartością y:

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

Teraz chcę wykreślić wykres punktowy tego typu

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

i utwórz animację na indeksie i. Jak mam to zrobic?

Nicola Vianello
źródło
2
W dokumentacji matplotlib znajduje się przykład: Symulacja deszczu .
ImportanceOfBeingErnest

Odpowiedzi:

142

Załóżmy, że masz wykres punktowy scat = ax.scatter(...), wtedy możesz

  • zmienić pozycje

          scat.set_offsets(array)
    

gdzie arrayjest N x 2ukształtowaną tablicą współrzędnych x i y.

  • zmienić rozmiary

          scat.set_sizes(array)
    

gdzie arrayjest tablicą 1D rozmiarów w punktach.

  • zmienić kolor

          scat.set_array(array)
    

gdzie arrayjest tablicą wartości 1D, które zostaną odwzorowane kolorem.

Oto krótki przykład użycia modułu animacji .
Jest nieco bardziej skomplikowany, niż powinien, ale powinien dać ci ramy do robienia bardziej wyszukanych rzeczy.

(Kod zmieniony w kwietniu 2019 r., Aby był zgodny z aktualnymi wersjami. W przypadku starszego kodu zobacz historię wersji )

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

wprowadź opis obrazu tutaj

Jeśli jesteś na OSX i korzystania z zaplecza OSX, trzeba zmienić blit=True, aby blit=Falsew FuncAnimationinicjalizacji poniżej. Backend OSX nie obsługuje w pełni blittingu. Wydajność ucierpi, ale przykład powinien działać poprawnie na OSX z wyłączonym blittingiem.


Aby uzyskać prostszy przykład, który po prostu aktualizuje kolory, spójrz na następujące:

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=range(numframes),
                                  fargs=(color_data, scat))
    plt.show()

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

main()
Joe Kington
źródło
Cześć Joe Próbowałem twojego pierwszego przykładu, ale to nie działa, podczas gdy drugi tak. Może spróbuję debugować pierwszą opcję, to pomoże mi poprawić moją wiedzę o Pythonie. Dziękuję
Nicola Vianello
1
Niestety pierwszy przykład nie wyświetla się dla mnie również przy użyciu matplotlib 1.3.1 na OS X. Dostaję ramkę, która nie wyświetla żadnych punktów. Drugi przykład działa.
JoshAdel
9
Jak NA ŚWIECIE doszliście do wniosku, że .set_array()zmieni kolor kropek ?!
Lucas
1
Pierwszy przykład nie działa, musisz zmienić linię self.Scat.set_offsets(data[:2, :]) na self.scat.set_offsets(data[:2, :].reshape(self.numpoints, 2))
AN O'Nyme
2
Czy jest jakaś funkcja, która zmienia znaczniki punktów rozrzutu?
Constantinos,
13

Napisałem celuloid, żeby to ułatwić. Prawdopodobnie najłatwiej to pokazać na przykładzie:

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

wprowadź opis obrazu tutaj

Używa ArtistAnimationpod maską. camera.snapprzechwytuje bieżący stan figury, która jest używana do tworzenia klatek w animacji.

Edycja: Aby oszacować, ile pamięci to wykorzystuje, przepuściłem go przez 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')

Podsumowując:

  • Tworząc 100 działek zużyto 1,9 MiB.
  • Przy tworzeniu animacji wykorzystano 2,3 MiB.
  • Ta metoda tworzenia animacji zużywała łącznie 4,2 MB pamięci.
Jacques Kvam
źródło
1
Ponieważ używa ArtistAnimation, utworzy w pamięci 100 wykresów punktowych, co jest raczej nieefektywne. Używaj tego tylko wtedy, gdy wydajność nie jest dla Ciebie krytyczna.
ImportanceOfBeingErnest
1
Profilowanie pamięci to dobry pomysł. Czy zrobiłeś to samo dla FuncAnimation? Jakie są różnice?
ImportanceOfBeingErnest
1
Jak odtwarza się animację (w odniesieniu do zapisywania jej do pliku)?
argentum2f
5

Oto rzecz. Kiedyś korzystałem z Qt i Matlaba i nie znam systemu animacji w matplotlib.

Ale znalazłem sposób, aby stworzyć dowolną animację, tak jak w Matlabie. Jest naprawdę potężny. Nie musisz sprawdzać odniesień do modułów i możesz nanieść wszystko, co chcesz. Mam więc nadzieję, że to pomoże.

Podstawowym pomysłem jest użycie zdarzenia czasowego wewnątrz PyQt (jestem pewien, że inny system Gui w Pythonie, taki jak wxPython i TraitUi, ma ten sam wewnętrzny mechanizm do reagowania na zdarzenie. Ale po prostu nie wiem jak). Za każdym razem, gdy wywoływane jest zdarzenie Timer PyQt, odświeżam całe płótno i przerysowuję cały obraz, wiem, że może to powoli wpływać na szybkość i wydajność, ale nie jest to aż tak dużo.

Oto mały przykład:

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

Możesz dostosować prędkość odświeżania w

        self.timer = self.startTimer(100)

Jestem jak ty, który chce użyć animowanego wykresu punktowego, aby wykonać animację sortowania. Ale po prostu nie mogę znaleźć tak zwanej funkcji „ustaw”. Więc odświeżyłem całe płótno.

Mam nadzieję, że to pomoże..

tk_y1275963
źródło
Bardzo miły! Jednak nie dostałem żadnej zmiany w częstotliwości odświeżania, dostosowując self.startTimerwartość ... jakieś wskazówki na ten temat? (Tak, wiem, że minęło trochę czasu ...)
H. Arponen
-1

Dlaczego nie spróbować tego

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.
źródło
Dlaczego ten głos został odrzucony? Czy to nie działa?
eric