Jaki jest obecnie prawidłowy sposób dynamicznej aktualizacji wykresów w Jupyter / iPython?

93

W odpowiedziach na to, jak dynamicznie aktualizować wykres w pętli w notatniku ipython (w obrębie jednej komórki) , podano przykład dynamicznego aktualizowania wykresu w notatniku Jupyter w pętli Pythona. Działa to jednak poprzez niszczenie i ponowne tworzenie fabuły w każdej iteracji, a komentarz w jednym z wątków zauważa, że ​​tę sytuację można poprawić za pomocą nowej %matplotlib nbaggmagii, która zapewnia raczej interaktywną figurę osadzoną w notatniku, niż statyczny obraz.

Jednak ta wspaniała nowa nbaggfunkcja wydaje się być całkowicie nieudokumentowana, o ile wiem, i nie mogę znaleźć przykładu, jak jej użyć do dynamicznej aktualizacji fabuły. Dlatego moje pytanie brzmi, jak skutecznie zaktualizować istniejącą działkę w notatniku Jupyter / Python, używając zaplecza nbagg? Ponieważ dynamiczne aktualizowanie wykresów w matplotlib jest ogólnie trudnym problemem, prosty przykład roboczy byłby ogromną pomocą. Niezwykle pomocny byłby również wskaźnik do dowolnej dokumentacji na ten temat.

Aby było jasne, o co proszę: chcę uruchomić kod symulacji na kilka iteracji, następnie narysować wykres jego obecnego stanu, a następnie uruchomić go przez kilka kolejnych iteracji, a następnie zaktualizować wykres, aby odzwierciedlić aktualny stan i tak dalej. Chodzi więc o to, aby narysować fabułę, a następnie, bez żadnej interakcji ze strony użytkownika, zaktualizować dane na wykresie bez niszczenia i ponownego tworzenia całości.

Oto nieco zmodyfikowany kod z odpowiedzi na powyższe pytanie, do którego prowadzi łącze, w którym za każdym razem rysuje się od nowa całą figurę. Chcę osiągnąć ten sam efekt, ale wydajniej używając nbagg.

%matplotlib inline
import time
import pylab as pl
from IPython import display
for i in range(10):
    pl.clf()
    pl.plot(pl.randn(100))
    display.display(pl.gcf())
    display.clear_output(wait=True)
    time.sleep(1.0)
Nathaniel
źródło

Odpowiedzi:

62

Oto przykład, który aktualizuje wykres w pętli. Aktualizuje dane na rysunku i nie przerysowuje za każdym razem całej figury. Blokuje wykonywanie, ale jeśli jesteś zainteresowany uruchomieniem skończonego zestawu symulacji i zapisaniem gdzieś wyników, może to nie być dla Ciebie problemem.

%matplotlib notebook

import numpy as np
import matplotlib.pyplot as plt
import time

def pltsin(ax, colors=['b']):
    x = np.linspace(0,1,100)
    if ax.lines:
        for line in ax.lines:
            line.set_xdata(x)
            y = np.random.random(size=(100,1))
            line.set_ydata(y)
    else:
        for color in colors:
            y = np.random.random(size=(100,1))
            ax.plot(x, y, color)
    fig.canvas.draw()

fig,ax = plt.subplots(1,1)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_xlim(0,1)
ax.set_ylim(0,1)
for f in range(5):
    pltsin(ax, ['b', 'r'])
    time.sleep(1)

Umieściłem to na nbviewer tutaj.

Istnieje wersja ipython widget z nbaggktóra jest obecnie w toku w repozytorium matplotlib . Kiedy to będzie dostępne, prawdopodobnie będzie to najlepszy sposób użycia nbagg.

EDYCJA: zaktualizowano w celu wyświetlenia wielu wykresów

pneumatyka
źródło
1
Świetnie, wygląda na to, że dobrze działa. Brak interaktywności podczas działania nie jest dla mnie dużym problemem. Jedna trochę dziwna rzecz: jeśli zmienię while True:pętlę na for, kiedy pętla się kończy, otrzymuję dwa statyczne obrazy ostatniej działki, zamiast interaktywnego nbagg. Masz jakiś pomysł, dlaczego tak jest?
Nathaniel
Zmieniłem chwilę na pętlę for i wypróbowałem ją na tmpnb.org, ale nie widzę drugiego obrazu ani utraty interaktywności. Ujęcie w ciemności, ale możesz spróbować przesunąć pętlę wokół wywołania funkcji, zamiast mieć pętlę w funkcji. dla f w zakresie (10): pltsin (ax) czas. sen (1)
pneumatyka
3
@pneumatics Niestety ma pewne problemy z Matplotlib 2.0 na wyświetlaczu Retina: w pętli wykresy są dwa razy mniejsze niż zwykle.
Alexander Rodin
1
Wygląda na to, że figurka nie ma czasu na poprawną zmianę rozmiaru. Więc miałem znacznie lepsze wrażenia podczas wstawiania plt.show()i przenoszenia pętli for do następnej komórki.
ImportanceOfBeingErnest
2
Upewnij się, że masz notatnik% matplotlib w tej samej komórce notatnika jupyter, co Twoja działka - spędziłem dziś ponad 2 godziny na rozwiązywaniu tego problemu, ponieważ miałem notatnik% matplotlib w pierwszej komórce ze statystykami importu
aguazul
12

Używam jupyter-lab i to działa dla mnie (dostosuj to do swojego przypadku):

from IPython.display import clear_output
from matplotlib import pyplot as plt
import collections
%matplotlib inline

def live_plot(data_dict, figsize=(7,5), title=''):
    clear_output(wait=True)
    plt.figure(figsize=figsize)
    for label,data in data_dict.items():
        plt.plot(data, label=label)
    plt.title(title)
    plt.grid(True)
    plt.xlabel('epoch')
    plt.legend(loc='center left') # the plot evolves to the right
    plt.show();

Następnie w pętli wypełniasz słownik i przekazujesz go do live_plot():

data = collections.defaultdict(list)
for i in range(100):
    data['foo'].append(np.random.random())
    data['bar'].append(np.random.random())
    data['baz'].append(np.random.random())
    live_plot(data)

upewnij się, że masz kilka komórek pod wykresem, w przeciwnym razie widok zostanie zatrzymany za każdym razem, gdy wykres jest przerysowywany.

Ziofil
źródło
1
to tworzy nową działkę za każdym razem, zamiast aktualizować istniejącą
pneumatyka
2
Poprawny. Nie znalazłem lepszego sposobu na dynamiczną fabułę w jupyter-lab.
Ziofil
1
Czy istnieje sposób ustawienia czasu oczekiwania między iteracjami? zamiast po prostu „czekać = prawda”
Ahmad Moussa
1
Za każdym razem, gdy wykres jest przerysowywany, wykres migocze. Czy istnieje sposób na rozwiązanie tego problemu? Mam kilka pustych komórek pod działką, ale to nie pomaga.
MasayoMusic
@MasayoMusic patrz „Migotanie i przeskakiwanie danych wyjściowych” w buildmedia.readthedocs.org/media/pdf/ipywidgets/latest/ ...
leo
0

Zaadaptowałem odpowiedź @Ziofil i zmodyfikowałem ją tak, aby akceptowała x, y jako listę i wyświetlała wykres punktowy oraz trend liniowy na tym samym wykresie.

from IPython.display import clear_output
from matplotlib import pyplot as plt
%matplotlib inline
    
def live_plot(x, y, figsize=(7,5), title=''):
    clear_output(wait=True)
    plt.figure(figsize=figsize)
    plt.xlim(0, training_steps)
    plt.ylim(0, 100)
    x= [float(i) for i in x]
    y= [float(i) for i in y]
    
    if len(x) > 1:
        plt.scatter(x,y, label='axis y', color='k') 
        m, b = np.polyfit(x, y, 1)
        plt.plot(x, [x * m for x in x] + b)

    plt.title(title)
    plt.grid(True)
    plt.xlabel('axis x')
    plt.ylabel('axis y')
    plt.show();

wystarczy zadzwonić live_plot(x, y)w pętli. tak to wygląda: wprowadź opis obrazu tutaj

Miguel Silva
źródło