Come aggiornare dinamicamente un grafico in un ciclo in Ipython notebook (all'interno di una cella)


92

Ambiente: Python 2.7, matplotlib 1.3, IPython notebook 1.1, linux, chrome. Il codice è in una singola cella di input, utilizzando--pylab=inline

Voglio usare notebook e panda IPython per consumare un flusso e aggiornare dinamicamente un grafico ogni 5 secondi.

Quando uso l'istruzione print per stampare i dati in formato testo, funziona perfettamente: la cella di output continua a stampare i dati e ad aggiungere nuove righe. Ma quando provo a tracciare i dati (e quindi ad aggiornarli in un ciclo), il grafico non viene mai visualizzato nella cella di output. Ma se rimuovo il ciclo, lo stampo una volta. Funziona bene.

Poi ho fatto qualche semplice 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)

L'output non mostrerà nulla finché non interrompo manualmente il processo (ctrl + m + i). E dopo averlo interrotto, la trama viene visualizzata correttamente come più linee sovrapposte. Ma quello che voglio veramente è una trama che si presenti e venga aggiornata ogni 5 secondi (o ogni volta che la plot()funzione viene chiamata, proprio come l'istruzione print che ho citato sopra, che funziona bene). Mostrare solo il grafico finale dopo che la cella è stata completata NON è quello che voglio.

Ho anche provato ad aggiungere esplicitamente la funzione draw () dopo ciascuna plot(), ecc. Nessuna di esse funziona. Mi chiedo come aggiornare dinamicamente un grafico tramite un ciclo for / while all'interno di una cella nel notebook IPython.

Risposte:


118

IPython.displaymodulo di utilizzo :

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

4
questa non è un'opzione fluida, la trama è ricreata da zero con celle che vanno su e giù nel mezzo
denfromufa

3
L'aggiunta clear_output(wait=True)risolve questo problema. Vedi la risposta di Wabu di seguito.
Alex Williams

3
Puoi fare di meglio in questi giorni con %matplotlib nbaggcui ti dà una figura dal vivo con cui giocare.
tacaswell

@tcaswell ho aggiunto una nuova domanda chiedendo come si usa nbaggper ottenere questo risultato. (Pinging you se sei interessato a rispondere.) Stackoverflow.com/questions/34486642/…
Nathaniel

3
questo funziona ma distrugge anche qualsiasi altra cosa nella cella come le misure stampate. Esiste davvero un modo per aggiornare la trama e mantenere tutto il resto a posto?
KIC

30

Puoi migliorarlo ulteriormente aggiungendo wait=Truea clear_output:

display.clear_output(wait=True)
display.display(pl.gcf())

1
+1. Questo è molto importante. Penso che la risposta di HYRY dovrebbe essere aggiornata con queste informazioni.
Alex Williams

5
Questo è positivo, ma ha il fastidioso effetto collaterale di cancellare anche l'output di stampa.
Peter

30

Un paio di miglioramenti sono nella risposta di HYRY :

  • chiama displayprima in clear_outputmodo da finire con una trama, anziché due, quando la cella viene interrotta.
  • catch the KeyboardInterrupt, in modo che l'output della cella non sia disseminato di traceback.
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

7
In effetti, display.display(gcf())dovrebbe andare PRIMA display.clear_output(wait=True)
herrlich10

Grazie, @csta. Aggiunto.
Tom Phillips

@ herrlich10 Perché dovrebbe displayessere chiamato prima clear_output? Non dovresti prima cancellare l'output e poi visualizzare i nuovi dati, invece di fare il contrario?
Jakub Arnold

1
Sto ancora riscontrando uno sfarfallio dello schermo con gli aggiornamenti del grafico, tuttavia non è tutto il tempo. C'è una soluzione a questo?
MasayoMusic

2

Prova ad aggiungere show()o gcf().show()dopo la plot()funzione. Questi forzeranno l'aggiornamento della figura corrente (gcf () restituisce un riferimento per la figura corrente).


2
Grazie. gcf (). show () funziona anche. È necessario aggiungere clear_output () suggerito da HYRY per mostrare elementi sulla stessa figura
user3236895

È in aggiunta a "display.display (pl.gcf ())"?
MasayoMusic

2

L'aggiunta di etichette alle altre soluzioni pubblicate qui continuerà ad aggiungere nuove etichette in ogni ciclo. Per risolvere questo problema, cancella la trama usandoclf

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

4
Grazie plt.clf()funziona. Tuttavia c'è comunque da eliminare lo sfarfallio degli aggiornamenti?
MasayoMusic

0

Puoi farlo in questo modo. Accetta x, y come elenco e genera un grafico a dispersione più una tendenza lineare sullo stesso grafico.

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

devi solo chiamare live_plot(x, y)all'interno di un ciclo. Ecco come appare: inserisci qui la descrizione dell'immagine

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.