wie man ein Diagramm in einer Schleife in ipython notebook dynamisch aktualisiert (innerhalb einer Zelle)

91

Umgebung: Python 2.7, Matplotlib 1.3, IPython Notebook 1.1, Linux, Chrome. Der Code befindet sich in einer einzelnen Eingabezelle mit--pylab=inline

Ich möchte IPython-Notebook und Pandas verwenden, um einen Stream zu konsumieren und alle 5 Sekunden einen Plot dynamisch zu aktualisieren.

Wenn ich nur die print-Anweisung verwende, um die Daten im Textformat zu drucken, funktioniert das einwandfrei: Die Ausgabezelle druckt einfach weiter Daten und fügt neue Zeilen hinzu. Wenn ich jedoch versuche, die Daten zu zeichnen (und sie dann in einer Schleife zu aktualisieren), wird der Plot nie in der Ausgabezelle angezeigt. Aber wenn ich die Schleife entferne, zeichne sie einfach einmal. Es funktioniert gut.

Dann habe ich einen einfachen Test gemacht:

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)

Die Ausgabe zeigt nichts an, bis ich den Vorgang manuell unterbreche (Strg + m + i). Und nachdem ich es unterbrochen habe, wird das Diagramm korrekt als mehrere überlappende Linien angezeigt. Aber was ich wirklich will, ist ein Plot, der alle 5 Sekunden angezeigt und aktualisiert wird (oder wann immer die plot()Funktion aufgerufen wird, genau wie die oben erwähnten Druckanweisungsausgaben, was gut funktioniert). Nur das endgültige Diagramm zu zeigen, nachdem die Zelle vollständig fertig ist, ist NICHT das, was ich will.

Ich habe sogar versucht, nach jeder explizit die Funktion draw () plot()usw. hinzuzufügen . Keine davon funktioniert. Ich frage mich, wie ich einen Plot durch eine for / while-Schleife in einer Zelle im IPython-Notizbuch dynamisch aktualisieren kann.

user3236895
quelle

Antworten:

118

verwenden IPython.displayModul:

%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
quelle
4
Dies ist keine reibungslose Option, die Handlung wird von Grund auf neu erstellt, wobei die Zelle dazwischen auf und ab geht
denfromufa
3
Das Hinzufügen clear_output(wait=True)löst dieses Problem. Siehe Wabus Antwort unten.
Alex Williams
3
Sie können es heutzutage besser machen, mit %matplotlib nbaggdem Sie eine Live-Figur zum Spielen haben.
Tacaswell
@tcaswell Ich habe eine neue Frage hinzugefügt, wie man nbaggdies erreicht. (Pingen Sie an, falls Sie daran interessiert sind, darauf zu antworten.) Stackoverflow.com/questions/34486642/…
Nathaniel
3
Dies funktioniert, zerstört aber auch alles andere in der Zelle wie die gedruckten Maße. Gibt es eine Möglichkeit, die Handlung wirklich nur zu aktualisieren und alles andere an Ort und Stelle zu halten?
KIC
30

Sie können dies weiter verbessern durch Zugabe wait=Truezu clear_output:

display.clear_output(wait=True)
display.display(pl.gcf())
Wabu
quelle
1
+1. Dies ist sehr wichtig. Ich denke, HYRYs Antwort sollte mit diesen Informationen aktualisiert werden.
Alex Williams
5
Dies ist gut, hat aber den lästigen Nebeneffekt, dass auch die Druckausgabe gelöscht wird.
Peter
29

Ein paar Verbesserungen an HYRYs Antwort :

  • Rufen Sie displayvorher an, clear_outputdamit Sie einen Plot anstelle von zwei erhalten, wenn die Zelle unterbrochen wird.
  • fangen Sie das ab KeyboardInterrupt, damit die Zellenausgabe nicht mit dem Traceback übersät ist.
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
quelle
7
Tatsächlich display.display(gcf())sollte gehen VOR display.clear_output(wait=True)
herrlich10
Danke, @csta. Fügte es hinzu.
Tom Phillips
@ herrlich10 Warum sollte displayvorher angerufen werden clear_output? Sollten Sie nicht zuerst die Ausgabe löschen und dann die neuen Daten anzeigen, anstatt es umgekehrt zu tun?
Jakub Arnold
1
Ich bekomme immer noch ein Bildschirmflimmern mit den Grafikaktualisierungen, aber es ist nicht die ganze Zeit. Gibt es eine Problemumgehung?
MasayoMusic
2

Versuchen Sie, show()oder gcf().show()nach der plot()Funktion hinzuzufügen . Dadurch wird die Aktualisierung der aktuellen Abbildung erzwungen (gcf () gibt eine Referenz für die aktuelle Abbildung zurück).

Saullo GP Castro
quelle
2
Vielen Dank. gcf (). show () funktioniert auch. Müssen Sie die von HYRY vorgeschlagene clear_output () hinzufügen, um Sachen auf der gleichen fig zu zeigen
user3236895
Ist dies zusätzlich zu "display.display (pl.gcf ())"?
MasayoMusic
2

Durch Hinzufügen von Beschriftungen zu den anderen hier veröffentlichten Lösungen werden in jeder Schleife immer wieder neue Beschriftungen hinzugefügt. Um damit umzugehen, löschen Sie die Handlung mitclf

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())
Myon
quelle
4
Danke plt.clf()funktioniert. Gibt es jedoch trotzdem eine Möglichkeit, das Flimmern der Updates zu beseitigen?
MasayoMusic
0

Sie können es so machen. Es akzeptiert x, y als Liste und gibt ein Streudiagramm sowie einen linearen Trend auf demselben Diagramm aus.

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

Sie müssen nur live_plot(x, y)innerhalb einer Schleife aufrufen . So sieht es aus: Geben Sie hier die Bildbeschreibung ein

Miguel Silva
quelle