Jak zaktualizować działkę w matplotlib?


156

Mam problemy z przerysowaniem rysunku tutaj. Pozwalam użytkownikowi określić jednostki w skali czasu (oś x), a następnie ponownie obliczam i wywołuję tę funkcję plots(). Chcę, aby działka po prostu się zaktualizowała, a nie dodawała kolejną fabułę do figury.

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)

Odpowiedzi:


178

Zasadniczo masz dwie opcje:

  1. Zrób dokładnie to, co aktualnie robisz, ale zadzwoń graph1.clear()i graph2.clear()przed ponownym przesłaniem danych. To najwolniejsza, ale najprostsza i najbardziej niezawodna opcja.

  2. Zamiast przesuwać, możesz po prostu zaktualizować dane obiektów wydruku. Będziesz musiał wprowadzić pewne zmiany w swoim kodzie, ale powinno to być dużo, dużo szybsze niż ponowne przesyłanie rzeczy za każdym razem. Jednak kształt danych, które wykreślasz, nie może się zmienić, a jeśli zakres danych się zmienia, musisz ręcznie zresetować granice osi x i y.

Aby podać przykład drugiej opcji:

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

Próbowałem przetestować „1”. a wynik był taki, że po ponownym naniesieniu danych w moim GUI został narysowany kolejny zestaw wykresów, więc teraz miałem 4 wykresy po ponownym przeliczeniu, tak jak poprzednio.
thenickname

@thenickname - Gdzie dokładnie w kodzie dzwonisz clear? Powinieneś graph1.clear(); graph2.clear()sprawdzać w swojej forpętli, tuż przed tym, jak zadzwonisz graph1.plot(...), graph2.plot(...)itd ...
Joe Kington

Ta pętla for tworzy wywołania graphx.plot (...) N razy, a umieszczenie w nich wyraźnych instrukcji wykreśla tylko ostatnią. Wyciągnąłem kod płótna i umieściłem go w głównej pętli programu wraz z kodem rysunku i teraz moja funkcja jest wywoływana za pomocą przycisku. Z jakiegoś powodu, jeśli wywołam tę funkcję, wykresy zostaną zaktualizowane, ale jeśli naciśnę przycisk, wykresy nie. To całkiem interesujące zachowanie. Myślę, że to musi być błąd w Tkinter.
thenickname

Tylko przeczucie ... Brakuje Ci połączenia fig.canvas.draw()lub plt.draw()po wykreśleniu każdej klatki? (Tj trzeba mieć sekwencję clear, plot, drawza każdym razem chcesz pokazać ramkę) Zgaduję, ale myślę, że spowodowałoby dokładnie zachowanie jesteś opisujące ... powodzenia w każdym razie!
Joe Kington

3
Jest 2k14 i natknąłem się na coś takiego ... działa zgodnie z oczekiwaniami, ale okno kreślenia obraca się „nie odpowiada” .. jakieś sugestie ??
DevC

39

Możesz również wykonać następujące czynności: Spowoduje to narysowanie losowych danych macierzowych 10x1 na wykresie dla 50 cykli pętli for.

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

To nie wydaje się dawać wykresu. Czy coś mi brakuje? Mam też %matplotlib inlinezeszyt Jupyter.
MasayoMusic

haha, pracował dla mnie, kiedy usunąłem plt.clf(). Och matplotlib,
łajdaku

15

To zadziałało dla mnie. Wielokrotnie wywołuje funkcję aktualizującą wykres za każdym razem.

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” to funkcja zwracająca liczbę całkowitą. FuncAnimation będzie wielokrotnie wywoływać „update”, zrobi to „xmax” razy.


Czy mógłbyś podać przykład, jak wywołujesz tę funkcję (zwłaszcza jak przekazujesz funkcję w wywołaniu funkcji), a także jak wygląda funkcja fun ()?
bjornasm

1
Pewnie. „fun ()” to dowolna funkcja, która zwraca liczbę całkowitą. Możesz przekazać tę funkcję jako argument do innego, takiego jak: "plot_cont (moja_funkcja, 123)". Masz mnie dzwoniącego do plot_cont w linii 86: github.com/vitobasso/audio-ml/blob/ ...
Vituel

1
Zauważ, że "a =" jest konieczne lub FuncAnimation zostanie wyrzucone do śmieci i kod nie zadziała!
Kiuhnm

8

Na wypadek, gdyby ktoś natknął się na ten artykuł i szukał tego, czego szukałem, przykłady znalazłem pod adresem

Jak wizualizować skalarne dane 2D za pomocą Matplotlib?

i

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

następnie zmodyfikował je tak, aby używały imshow z wejściowym stosem ramek, zamiast generować i używać konturów w locie.


Zaczynając od tablicy 3D obrazów kształtu (nBins, nBins, nBins) o nazwie 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)

Znalazłem również znacznie prostszy sposób na wykonanie całego procesu, choć mniej solidny:

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

Zauważ, że oba z nich wydają się działać tylko z ipython --pylab=tk, akabackend = TkAgg

Dziękuję za pomoc we wszystkim.


6

Wydałem pakiet o nazwie python-drawnow, który zapewnia funkcjonalność umożliwiającą aktualizację figury, zwykle wywoływaną w pętli for, podobnie jak w Matlabie drawnow.

Przykładowe użycie:

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)

Ten pakiet działa z każdą figurą matplotlib i zapewnia opcje czekania po każdej aktualizacji figury lub umieszczenia w debugerze.


2
W jaki sposób jest jednocześnie wytrzymały i niestabilny?
BlueMoon93

2
Miałem na myśli solidne, jak w przypadku „działa z dowolną figurą matplotlib” i niestabilne, jak w „projektach weekendowych”. Zaktualizowałem moją odpowiedź
Scott,

3

Wszystko to może być prawdą, jednak dla mnie „aktualizacja online” danych działa tylko z niektórymi backendami, szczególnie wx. Możesz po prostu spróbować to zmienić, np. Uruchamiając ipython / pylab przez ipython --pylab=wx! Powodzenia!


1
Dziękuję za wiadomość, nigdy nie korzystałem z trybu interaktywnego, ponieważ nigdy nie działał z domyślnym zapleczem, którego używałem. O wiele przyjemniej jest korzystać z trybu interaktywnego niż zatrzymywanie wykonywania za każdym razem, gdy chcesz zobaczyć wykres!
PierreE

1
Żadna z pozostałych odpowiedzi nie pomogła w moim przypadku. Używam pycharm i problem polegał na kreśleniu i interaktywności konsoli. Musiałem dodać From pylab import *, a następnie ion () w treści kodu, aby włączyć interaktywność. Teraz dla mnie działa płynnie.
shelbypereira

2

To zadziałało dla mnie:

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()
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.