Heim > Artikel > Backend-Entwicklung > Plotten in Echtzeit mit Pyplot
Ich wollte einige Daten, die ich mit einer einfachen Umfrage-App generiert habe, grafisch darstellen. Ich habe in der Vergangenheit an Pyplot herumgebastelt, aber ich habe noch nicht versucht, etwas von Grund auf zu erstellen. Glücklicherweise ist es sehr beliebt und es gibt jede Menge Beispiele auf StackOverflow und anderswo.
Ich habe eine Suche durchgeführt und mit dieser SO-Antwort begonnen, die sich auf die Aktualisierung eines Diagramms im Laufe der Zeit bezieht.
import matplotlib.pyplot as plt import numpy as np # You probably won't need this if you're embedding things in a tkinter plot... plt.ion() x = np.linspace(0, 6*np.pi, 100) y = np.sin(x) fig = plt.figure() ax = fig.add_subplot(111) line1, = ax.plot(x, y, 'r-') # Returns a tuple of line objects, thus the comma for phase in np.linspace(0, 10*np.pi, 500): line1.set_ydata(np.sin(x + phase)) fig.canvas.draw() fig.canvas.flush_events()
Dieser Code animiert eine sich ändernde Phase einer Sinuswelle.
Die ersten beiden Zeilen importieren die Bibliotheken, die ich verwenden möchte: matplotlib.pyplot übernimmt das Plotten und die Handhabung der GUI.
Wenn ich das verstehe (auch wenn ich es vielleicht nicht verstehe), sorgt die ion()-Methode dafür, dass Pyplot die GUI steuert. Sie könnten es auch in einem tkinter-Programm verwenden oder es zum Generieren statischer Bilder verwenden, aber in unserem Fall ist es sinnvoll, es die GUI des Plots für uns übernehmen zu lassen. (Das ist es, was der Aufruf von „flush_events()“ später bewirkt: Ermöglichen der Interaktivität mit dem Figurenfenster.)
In diesem Beispiel wird die Numpy-Methode linspace() verwendet, um die x-Werte zu erstellen. Es gibt ein Numpy-Array zurück, bei dem es sich um eine schicke Python-Liste handelt.
Der Grund für die Verwendung von np.sin anstelle von math.sin ist die Übertragung. Das ist der Numpy-Begriff für die Anwendung der Funktion auf jedes Element in einer Liste. Tatsächlich kommt mir der Gedanke, dass das Gleiche auch ohne Numpy mit „map:
“ erreicht werden könnte
map(lambda n: math.sin(n), x)
Numpy Broadcasting ist jedoch bequem und einfach zu verwenden.
Jetzt kommt das Pyplot-Setup. Erstellen Sie zunächst eine neue „Figur“ (Abb.). Fügen Sie dieser Abbildung eine Nebenhandlung (ax) hinzu – es könnten viele sein. 111 hat die eher esoterische Interpretation: „Erstellen Sie ein 1x1-Raster und platzieren Sie diese Nebenhandlung in der ersten Zelle.“
In diesem Unterdiagramm (oder Satz von Achsen) wird eine Linie unter Verwendung der übergebenen x- und y-Werte eingezeichnet. (Punkte werden mit geraden Linien verbunden und fortlaufend dargestellt.) „r-“ ist die Abkürzung für eine durchgezogene rote Linie. Wir könnten mehrere Zeilen angeben, daher gibt plot() ein Tupel zurück; Der obige Code verwendet Tupel-Entpacken, um den einen Wert zu extrahieren, den wir wollen.
Es ist ein guter Anfang, aber ich muss die x-Achse im Laufe der Zeit erweitern. Dieser Code aktualisiert bei Bedarf auch nicht die Grenzen der Y-Achse – er ist an die Grenzen gebunden, die er für das erste Diagramm berechnet. Etwas mehr Suche führte mich zu dieser SO-Antwort. Um sie zu zitieren:
Sie müssen den dataLim der Achsen aktualisieren und anschließend den viewLim der Achsen basierend auf dem dataLim aktualisieren. Die entsprechenden Methoden sind axis.relim() und ax.autoscale_view() Methode.
Klar, hört sich gut an. Basierend auf ihrem Beispiel habe ich ein Demodiagramm erstellt, das sowohl in x- als auch in y-Richtung wächst.
import matplotlib.pyplot as plt import numpy as np from threading import Thread from time import sleep x = list(map(lambda x: x / 10, range(-100, 100))) x_next_max = 100 y = np.sin(x) # You probably won't need this if you're embedding things in a tkinter plot... plt.ion() fig = plt.figure() ax = fig.add_subplot(111) line1 = ax.plot(x, y, 'r-')[0] # Returns a tuple of line objects growth = 0 while True: x.append(x_next_max / 10) x_next_max += 1 line1.set_xdata(x) line1.set_ydata(np.sin(x) + np.sin(np.divide(x, 100)) + np.divide(x, 100)) ax.relim() ax.autoscale() fig.canvas.draw() fig.canvas.flush_events() sleep(0.1)
Jetzt komme ich voran. Dies ist jedoch eine Blockierungsschleife und ich benötige eine gelegentliche Aktualisierung meiner Daten. Wenn ich mehrere Threads hätte, müsste ich mir Sorgen machen, dass ich beim Aktualisieren meiner Variablen threadsicher bin. In diesem Fall kann ich faul sein, weil ich weiß, dass die Variable nur einmal alle 5 Minuten aktualisiert wird (oder wie oft auch immer die Abfragefunktion ausgeführt wird); Es besteht keine Gefahr, dass die Variable mitten in einer Codezeile überschrieben wird.
import matplotlib.pyplot as plt import numpy as np from threading import Timer from time import sleep x = list(map(lambda x: x / 10, range(-100, 100))) x_next_max = 100 y = np.sin(x) # You probably won't need this if you're embedding things in a tkinter plot... plt.ion() fig = plt.figure() ax = fig.add_subplot(111) line1 = ax.plot(x, y, 'r-')[0] # Plot returns a tuple of line objects growth = 0 new_x = None dT = 1 def grow(): global new_x, x_next_max while True: new_x = x + [x_next_max / 10] x_next_max += 1 sleep(dT) # grow every dT seconds t = Thread(target=grow) t.start() while True: if new_x: x = new_x new_x = None line1.set_xdata(x) line1.set_ydata(np.sin(x) + np.sin(np.divide(x, 100)) + np.divide(x, 100)) ax.relim() ax.autoscale() fig.canvas.draw() fig.canvas.flush_events() sleep(0.1)
Das Diagramm wird nur aktualisiert, wenn der Wachstumsthread new_x einen Wert zuweist. Beachten Sie, dass sich der Aufruf von „flush_events()“ außerhalb der „if“-Anweisung befindet, sodass er häufig aufgerufen wird.
Das obige ist der detaillierte Inhalt vonPlotten in Echtzeit mit Pyplot. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!