Jak dynamicznie aktualizować wykres w pętli w notatniku Ipython (w obrębie jednej komórki)

93

Środowisko: Python 2.7, matplotlib 1.3, IPython notebook 1.1, linux, chrome. Kod znajduje się w jednej komórce wejściowej, używając--pylab=inline

Chcę używać notatnika IPython i pand, aby zużywać strumień i dynamicznie aktualizować wykres co 5 sekund.

Kiedy po prostu używam instrukcji print do drukowania danych w formacie tekstowym, działa to doskonale: komórka wyjściowa po prostu drukuje dane i dodaje nowe wiersze. Ale kiedy próbuję wykreślić dane (a następnie zaktualizować je w pętli), wykres nigdy nie pojawia się w komórce wyjściowej. Ale jeśli usunę pętlę, po prostu wykreśl ją raz. To działa dobrze.

Następnie zrobiłem prosty test:

i = pd.date_range('2013-1-1',periods=100,freq='s')
while True:
    plot(pd.Series(data=np.random.randn(100), index=i))
    #pd.Series(data=np.random.randn(100), index=i).plot() also tried this one
    time.sleep(5)

Wyjście nie pokaże niczego, dopóki ręcznie nie przerwę procesu (ctrl + m + i). Po przerwaniu wykres jest wyświetlany poprawnie jako wiele nakładających się linii. Ale to, czego naprawdę chcę, to wykres, który pojawia się i jest aktualizowany co 5 sekund (lub za każdym razem, gdy plot()funkcja zostanie wywołana, tak jak to, co wypisuje instrukcja print, o której wspomniałem powyżej, która działa dobrze). Tylko pokazanie ostatecznego wykresu po całkowitym zakończeniu komórki NIE jest tym, czego chcę.

Próbowałem nawet jawnie dodać funkcję draw () po każdym plot()itd. Żadna z nich nie działa. Zastanawiam się, jak dynamicznie aktualizować wykres za pomocą pętli for / while w jednej komórce w notatniku IPython.

user3236895
źródło

Odpowiedzi:

122

posługiwać się IPython.display modułu:

%matplotlib inline
import time
import pylab as pl
from IPython import display
for i in range(10):
    pl.plot(pl.randn(100))
    display.clear_output(wait=True)
    display.display(pl.gcf())
    time.sleep(1.0)
HYRY
źródło
4
to nie jest płynna opcja, fabuła jest odtwarzana od zera, a komórka przechodzi w górę iw dół pomiędzy
denfromufa
3
Dodanie clear_output(wait=True)rozwiązuje ten problem. Zobacz odpowiedź wabu poniżej.
ahwillia
3
W dzisiejszych czasach możesz zrobić lepiej, %matplotlib nbaggco daje ci żywą figurę do zabawy.
tacaswell
@tcaswell Dodałem nowe pytanie z pytaniem, w jaki sposób można nbaggto osiągnąć. (Pinguje, jeśli jesteś zainteresowany odpowiedzią.) Stackoverflow.com/questions/34486642/ ...
Nathaniel
3
to działa, ale także niszczy wszystko inne w komórce, jak wydrukowane miary. Czy istnieje sposób, aby po prostu zaktualizować fabułę i utrzymać wszystko inne na swoim miejscu?
KIC
31

Możesz to ulepszyć, dodając wait=Truedo clear_output:

display.clear_output(wait=True)
display.display(pl.gcf())
wabu
źródło
1
+1. To jest bardzo ważne. Myślę, że odpowiedź HYRY'ego powinna zostać zaktualizowana o te informacje.
ahwillia
5
Jest to dobre, ale ma irytujący efekt uboczny w postaci wyczyszczenia wydruku.
Peter
31

Kilka ulepszeń w odpowiedzi HYRY'ego :

  • zadzwoń displaywcześniej clear_output, abyś skończył z jedną fabułą zamiast dwóch, gdy komórka zostanie przerwana.
  • złapać KeyboardInterrupt, aby dane wyjściowe komórki nie były zaśmiecone śledzeniem.
import matplotlib.pylab as plt
import pandas as pd
import numpy as np
import time
from IPython import display
%matplotlib inline

i = pd.date_range('2013-1-1',periods=100,freq='s')

while True:
    try:
        plt.plot(pd.Series(data=np.random.randn(100), index=i))
        display.display(plt.gcf())
        display.clear_output(wait=True)
        time.sleep(1)
    except KeyboardInterrupt:
        break
Tom Phillips
źródło
7
Rzeczywiście, display.display(gcf())powinno iść PRZED display.clear_output(wait=True)
herrlich10
Dzięki, @csta. Dodałem to.
Tom Phillips
@ herrlich10 Dlaczego należy displaydzwonić wcześniej clear_output? Nie powinieneś najpierw wyczyścić danych wyjściowych, a następnie wyświetlić nowe dane, zamiast robić to na odwrót?
Jakub Arnold
1
Wciąż dostaję migotanie ekranu przy aktualizacjach wykresu, ale nie zawsze. Czy istnieje obejście tego problemu?
MasayoMusic
2

Spróbuj dodać funkcję show()lub gcf().show()po niej plot(). Wymusi to aktualizację bieżącej liczby (gcf () zwraca odniesienie do bieżącej figury).

Saullo GP Castro
źródło
2
dzięki. gcf (). show () również działa. Trzeba dodać metodę clear_output () zasugerowaną przez HYRY, aby pokazać rzeczy na tej samej figurze
user3236895
Czy jest to dodatek do „display.display (pl.gcf ())”?
MasayoMusic
2

Dodanie etykiety do innych zamieszczonych tutaj rozwiązań spowoduje ciągłe dodawanie nowych etykiet w każdej pętli. Aby sobie z tym poradzić, wyczyść fabułę za pomocąclf

for t in range(100)
   if t % refresh_rate == 0:

     plt.clf()
     plt.plot(history['val_loss'], 'r-', lw=2, label='val')
     plt.plot(history['training_loss'], 'b-', lw=1, label='training')
     plt.legend()
     display.clear_output(wait=True)
     display.display(plt.gcf())

mion
źródło
4
Dzięki plt.clf()działa. Czy jest jednak sposób, aby pozbyć się migotania z aktualizacji?
MasayoMusic
0

Możesz to zrobić w ten sposób. Przyjmuje x, y jako listę i wyświetla 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. Oto jak to wygląda: wprowadź opis obrazu tutaj

Miguel Silva
źródło