Wie aktualisiere ich ein Diagramm in matplotlib?


156

Ich habe Probleme beim Neuzeichnen der Figur hier. Ich erlaube dem Benutzer, die Einheiten in der Zeitskala (x-Achse) anzugeben, und dann berechne ich diese Funktion neu und rufe sie auf plots(). Ich möchte, dass der Plot einfach aktualisiert wird und kein weiterer Plot an die Figur angehängt wird.

def plots():
    global vlgaBuffSorted
    cntr()

    result = collections.defaultdict(list)
    for d in vlgaBuffSorted:
        result[d['event']].append(d)

    result_list = result.values()

    f = Figure()
    graph1 = f.add_subplot(211)
    graph2 = f.add_subplot(212,sharex=graph1)

    for item in result_list:
        tL = []
        vgsL = []
        vdsL = []
        isubL = []
        for dict in item:
            tL.append(dict['time'])
            vgsL.append(dict['vgs'])
            vdsL.append(dict['vds'])
            isubL.append(dict['isub'])
        graph1.plot(tL,vdsL,'bo',label='a')
        graph1.plot(tL,vgsL,'rp',label='b')
        graph2.plot(tL,isubL,'b-',label='c')

    plotCanvas = FigureCanvasTkAgg(f, pltFrame)
    toolbar = NavigationToolbar2TkAgg(plotCanvas, pltFrame)
    toolbar.pack(side=BOTTOM)
    plotCanvas.get_tk_widget().pack(side=TOP)

Antworten:


178

Sie haben im Wesentlichen zwei Möglichkeiten:

  1. Machen Sie genau das, was Sie gerade tun, aber rufen Sie an graph1.clear()und graph2.clear()bevor Sie die Daten neu plotten. Dies ist die langsamste, aber einfachste und robusteste Option.

  2. Anstatt neu zu zeichnen, können Sie einfach die Daten der Plotobjekte aktualisieren. Sie müssen einige Änderungen an Ihrem Code vornehmen, aber dies sollte viel, viel schneller sein, als jedes Mal Dinge neu zu zeichnen. Die Form der Daten, die Sie zeichnen, kann sich jedoch nicht ändern. Wenn sich der Bereich Ihrer Daten ändert, müssen Sie die Grenzwerte für die x- und y-Achse manuell zurücksetzen.

Um ein Beispiel für die zweite Option zu geben:

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 6*np.pi, 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-') # 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()

Ich habe versucht, "1" zu testen. und das Ergebnis war, nachdem ich die Daten neu aufgezeichnet hatte, dass ein weiterer Satz von Plots in meiner GUI gezeichnet wurde, so dass ich jetzt nach der Neuberechnung wie zuvor 4 Plots hatte.
thenickname

@thenickname - Wo genau in Ihrem Code rufen Sie an clear? Sie sollten in graph1.clear(); graph2.clear()Ihrer forSchleife anrufen, kurz bevor Sie anrufen graph1.plot(...), graph2.plot(...)etc ...
Joe Kington

Diese for-Schleife erzeugt N Aufrufe graphx.plot (...) N-mal und das Einfügen der klaren Anweisungen dort zeichnet nur die letzte. Ich habe den Canvas-Code tatsächlich herausgezogen und zusammen mit dem Figurencode in die Hauptprogrammschleife eingefügt, und jetzt wird meine Funktion über eine Schaltfläche aufgerufen. Wenn ich nur die Funktion aufrufe, werden die Diagramme aus irgendeinem Grund aktualisiert, aber wenn ich die Taste drücke, werden die Diagramme nicht aktualisiert. Es ist ziemlich interessantes Verhalten. Ich denke, das muss ein Fehler in Tkinter sein.
thenickname

Nur eine Ahnung ... Vermissen Sie einen Anruf fig.canvas.draw()oder plt.draw()nach dem Plotten jedes Frames? (Ie sollten Sie eine Folge von haben clear, plot, drawjedes Mal , wenn Sie wollen , um einen Rahmen zeigen) Ich nehme an , aber ich denke , das würde dazu führen , genau das Verhalten Sie beschreiben ... Viel Glück, auf jeden Fall!
Joe Kington

3
Es ist 2k14 und ich bin gestolpert, um so etwas zu erreichen ... es funktioniert wie erwartet, aber das Plotfenster dreht sich "reagiert nicht" .. irgendwelche Vorschläge?
DevC

39

Sie können auch Folgendes tun: Dadurch werden 10x1-Zufallsmatrixdaten für 50 Zyklen der for-Schleife auf dem Plot gezeichnet.

import matplotlib.pyplot as plt
import numpy as np

plt.ion()
for i in range(50):
    y = np.random.random([10,1])
    plt.plot(y)
    plt.draw()
    plt.pause(0.0001)
    plt.clf()

Dies scheint kein Diagramm auszugeben. Vermisse ich etwas Ich habe auch %matplotlib inlinein Jupyter Notizbuch.
MasayoMusic

haha, habe für mich gearbeitet, als ich das entfernt habe plt.clf(). Oh matplotlib, du Schlingel :)
AndyP

15

Das hat bei mir funktioniert. Ruft wiederholt eine Funktion auf, die das Diagramm jedes Mal aktualisiert.

import matplotlib.pyplot as plt
import matplotlib.animation as anim

def plot_cont(fun, xmax):
    y = []
    fig = plt.figure()
    ax = fig.add_subplot(1,1,1)

    def update(i):
        yi = fun()
        y.append(yi)
        x = range(len(y))
        ax.clear()
        ax.plot(x, y)
        print i, ': ', yi

    a = anim.FuncAnimation(fig, update, frames=xmax, repeat=False)
    plt.show()

"fun" ist eine Funktion, die eine Ganzzahl zurückgibt. FuncAnimation ruft wiederholt "update" auf, dies geschieht "xmax" mal.


Können Sie ein Beispiel geben, wie Sie diese Funktion aufrufen (insbesondere wie Sie eine Funktion in einem Funktionsaufruf übergeben) und wie die fun () -Funktion aussieht?
Björnasm

1
Sicher. "fun ()" ist eine Funktion, die eine Ganzzahl zurückgibt. Sie können die Funktion als Argument an eine andere übergeben: "plot_cont (my_function, 123)". Dort rufe ich plot_cont in Zeile 86 an: github.com/vitobasso/audio-ml/blob/…
Vituel

1
Beachten Sie, dass "a =" erforderlich ist oder FuncAnimation durch Müll gesammelt wird und der Code nicht funktioniert!
Kiuhnm

8

Für den Fall, dass jemand auf diesen Artikel stößt und nach dem sucht, wonach ich gesucht habe, habe ich Beispiele unter gefunden

Wie visualisiere ich skalare 2D-Daten mit Matplotlib?

und

http://mri.brechmos.org/2009/07/automatically-update-a-figure-in-a-loop (auf web.archive.org)

Ändern Sie sie dann so, dass sie imshow mit einem Eingabestapel von Frames verwenden, anstatt Konturen im laufenden Betrieb zu generieren und zu verwenden.


Beginnend mit einem 3D-Array von Formbildern (nBins, nBins, nBins), genannt frames.

def animate_frames(frames):
    nBins   = frames.shape[0]
    frame   = frames[0]
    tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
    for k in range(nBins):
        frame   = frames[k]
        tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
        del tempCS1
        fig.canvas.draw()
        #time.sleep(1e-2) #unnecessary, but useful
        fig.clf()

fig = plt.figure()
ax  = fig.add_subplot(111)

win = fig.canvas.manager.window
fig.canvas.manager.window.after(100, animate_frames, frames)

Ich habe auch einen viel einfacheren Weg gefunden, um diesen ganzen Prozess durchzuführen, wenn auch weniger robust:

fig = plt.figure()

for k in range(nBins):
    plt.clf()
    plt.imshow(frames[k],cmap=plt.cm.gray)
    fig.canvas.draw()
    time.sleep(1e-6) #unnecessary, but useful

Beachten Sie, dass beide nur funktionieren ipython --pylab=tk, auch bekannt alsbackend = TkAgg

Vielen Dank für die Hilfe bei allem.


6

Ich habe ein Paket namens python-drawow veröffentlicht , das Funktionen zum Aktualisieren einer Figur bietet, die normalerweise in einer for-Schleife aufgerufen werden, ähnlich wie bei Matlabdrawnow .

Ein Anwendungsbeispiel:

from pylab import figure, plot, ion, linspace, arange, sin, pi
def draw_fig():
    # can be arbitrarily complex; just to draw a figure
    #figure() # don't call!
    plot(t, x)
    #show() # don't call!

N = 1e3
figure() # call here instead!
ion()    # enable interactivity
t = linspace(0, 2*pi, num=N)
for i in arange(100):
    x = sin(2 * pi * i**2 * t / 100.0)
    drawnow(draw_fig)

Dieses Paket funktioniert mit jeder matplotlib-Figur und bietet Optionen zum Warten nach jeder Figurenaktualisierung oder zum Ablegen im Debugger.


2
Wie ist es gleichzeitig robust und instabil?
BlueMoon93

2
Ich meinte robust wie in "Arbeitet mit jeder Matplotlib-Figur" und instabil wie in "Wochenendprojekt". Ich habe meine Antwort aktualisiert
Scott

3

All dies mag zutreffen, aber für mich funktioniert die "Online-Aktualisierung" von Zahlen nur mit einigen Backends, insbesondere wx. Sie könnten versuchen, dies zu ändern, z. B. indem Sie ipython / pylab mit starten ipython --pylab=wx! Viel Glück!


1
Vielen Dank für Ihre Nachricht. Ich habe den interaktiven Modus nie verwendet, da er mit dem von mir verwendeten Standard-Backend nie funktioniert hat. Es ist viel schöner, den interaktiven Modus zu verwenden, als die Ausführung jedes Mal anzuhalten, wenn Sie ein Diagramm sehen möchten!
PierreE

1
Keine der anderen Antworten hat in meinem Fall geholfen. Ich benutze Pycharm und das Problem war das Plotten und die Interaktivität der Konsole. Ich musste From pylab import * und dann ion () in den Code-Body einfügen, um interaktiv zu aktivieren. Es funktioniert jetzt reibungslos für mich.
Shelbypereira

2

Das hat bei mir funktioniert:

from matplotlib import pyplot as plt
from IPython.display import clear_output
import numpy as np
for i in range(50):
    clear_output(wait=True)
    y = np.random.random([10,1])
    plt.plot(y)
    plt.show()
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.