Ipython: %matplotlib inline funktioniert nicht gut mit ipywidgets 6.0 interagieren

Erstellt am 3. MĂ€rz 2017  Â·  46Kommentare  Â·  Quelle: ipython/ipython

Es scheint, dass im Jupyter-Notebook %matplotlib inline mit den neuen ipywidgets 6.0 @interact nicht mehr schön zu spielen ist. Soweit ich das beurteilen kann, wartet das Inline-Backend, bis die Zelle vollstĂ€ndig fertig ist, bevor es die display_data-Nachricht mit dem Bild sendet (siehe den post-execute-Hook, der unter https://github.com/ipython/ipython/blob/7cde22957303ab53df8bd464ad5d7ed616197f31/ registriert ist. IPython/core/pylabtools.py#L383). In ipywidgets 6.0 haben wir die Funktionsweise von Interaktionen geĂ€ndert, um genauere Angaben zu den von uns erfassten Ausgaben zu machen - wir erfassen Ausgabenachrichten, die wĂ€hrend der AusfĂŒhrung der Funktion gesendet werden, und zeigen diese in der interaktiven Ausgabe an. Da das Matplotlib-Inline-Backend jedoch sein Bild sendet, nachdem die Funktion ausgefĂŒhrt wurde, erhalten wir nur eine riesige Reihe von Bildern im Ausgabebereich der Zelle.

import matplotlib.pyplot as plt
from ipywidgets import interact

%matplotlib inline
x = [1,2,5,4,3]
@interact(n=(2,5))
def f(n):
    plt.plot(x[:n])

In diesem Thema geht es darum, die Diskussion zu beginnen, um zu sehen, ob es eine Möglichkeit fĂŒr uns gibt, eine gute Lösung zu finden, die gut mit dem Inline-Backend (was nĂŒtzlich ist) funktioniert und auch gut mit der ipywidgets-Interaktion funktioniert.

Wenn es beispielsweise eine Möglichkeit gĂ€be, das Inline-Backend anzuweisen, sein Bild sofort zu leeren und nicht nach Abschluss der AusfĂŒhrung einer Zelle, könnte dies ausreichen. Dieser Code funktioniert zum Beispiel ( ohne aktiviertes Inline-Backend)

import matplotlib.pyplot as plt
from ipywidgets import interact

x = [1,2,5,4,3]
@interact(n=(2,5))
def f(n):
    plt.plot(x[:n])
    plt.show()

CC @SylvainCorlay , @tacaswell.

Hilfreichster Kommentar

Hallo zusammen, ich hatte die technischen Details nicht verfolgt, bin aber auf das Problem gestoßen. UnabhĂ€ngig von Implementierungsdetails halte ich es fĂŒr Ă€ußerst wichtig, dass matplotlib+interact so gut wie möglich "einfach funktioniert".

Alle 46 Kommentare

Das scheint zu funktionieren, aber hat jemand eine Lösung fĂŒr einen besseren Weg?

import matplotlib.pyplot as plt
from ipywidgets import interact
from ipykernel.pylab.backend_inline import flush_figures

%matplotlib inline
x = [1,2,5,4,3]
@interact(n=(2,5))
def f(n):
    plt.plot(x[:n])
    flush_figures()

CC @minrk , der am Inline-mpl-Backend gearbeitet hat.

Ich glaube nicht, dass wir viel tun können.

Welche Heuristik wĂŒrden Sie fĂŒr die Darstellung einer Figur verwenden? Sie können es ĂŒberhaupt nicht tun, wenn Figuren so viele Code-Wiederverwendungsfiguren aktualisieren, aber es ist ĂŒblich, Animationen zu verwenden.

SondergehĂ€use @interact zum SpĂŒlen nach jeder Operation ?

Spezielles GehÀuse @interact zum

Guter Punkt, danke fĂŒr den Vorschlag. Es fĂŒhlt sich nicht sehr sauber an, dies zu tun (um ein bestimmtes Backend fĂŒr Matplotlib zu spezialisieren). Vielleicht warte ich ab, ob es ein großes Problem fĂŒr Benutzer ist, die Ausgabe wie im obigen Beispiel explizit zu leeren. Ich meine, explizit ist besser als implizit und so...

Edit: Zusammenfassend denke ich, dass ich nicht viel tun kann (oder sollte) ...

Es stellt sich heraus, dass das alles viel einfacher ist, als ich dachte - ich muss etwas falsch gemacht haben, als ich das vorhin getestet habe. Wir mĂŒssen flush_figures nicht aufrufen, es reicht, einfach die Standard-plt.show aufzurufen:

import matplotlib.pyplot as plt
from ipywidgets import interact

%matplotlib inline
x = [1,2,5,4,3]
@interact(n=(2,5))
def f(n):
    plt.plot(x[:n])
    plt.show()

Ich denke, es ist völlig vernĂŒnftig, Leute zu bitten, einen show()-Befehl in ihre Interaktion einzufĂŒgen, also schließe ich dies.

Wenn wir dieses Verhalten (und andere potenzielle Ausgaben nach der AusfĂŒhrung) beibehalten möchten, könnten die Post-X-Hooks möglicherweise explizit in Interaktion aufgerufen werden, bevor die Ausgabeerfassung abgeschlossen ist? Im Allgemeinen erwarte ich beim Aufrufen einer Funktion ĂŒber Interaktion, dass sie sich genau so verhĂ€lt, als ob ich die Funktion einmal in einer Zelle aufgerufen hĂ€tte.

Ich wĂŒrde erwarten, dass die Leute von all den Interact-with-matplotlib-Beispielen ĂŒberrascht sind, dass sie keine Zahlen mehr anzeigen, da 'interactive' matplotlib im Allgemeinen bedeutet, dass plt.show unnötig ist. Das EinfĂŒgen in eine interaktive Funktion sollte das wahrscheinlich nicht Ă€ndern.

Es erinnert mich ein wenig daran, dass sys.stdout.flush() , um die Ausgabe zu sehen, bevor der IOThread eingefĂŒhrt wurde. Es machte Sinn, warum es erforderlich war, wenn man darĂŒber nachdachte, wie die Dinge funktionierten, aber das machte es nicht weniger ĂŒberraschend, weil es sich nicht so verhielt, wie die Leute es erwartet hatten.

Ich denke nicht, dass wir alle Post-Execute-Handler automatisch ausfĂŒhren sollten (wer weiß, was sie tun werden - vielleicht eine Menge Zeug!). Wie wĂ€re es mit einem flush_display Hook? Wir spĂŒlen es, bevor wir mit der Interaktion beginnen, und spĂŒlen es erneut, bevor wir es beenden. Auf diese Weise ist es genau analog zum SpĂŒlen von stdout/stdin.

Nun, oder andererseits macht es vielleicht Sinn, Post-Execute aufzurufen, wenn wir uns eine Interaktion vorstellen, die sich wirklich wie eine einzelne Zelle fĂŒr sich allein verhĂ€lt. Aber dann wĂŒrden wir wahrscheinlich post-execute aufrufen wollen, bevor die Interaktion ausgefĂŒhrt wurde und auch danach, nur fĂŒr den Fall, dass die Interaktion in einer Zelle mit anderen Dingen war.

Wiedereröffnung, da die Konversation noch nicht geschlossen ist...

Ich mag die Idee eines flush_display-Hooks, und es wĂŒrde theoretisch funktionieren, obwohl es koordinierte Releases von ipython und ipykernel und ipywidgets erfordern wĂŒrde, um einen neuen Hook zu definieren und das Backend auf seine Verwendung umzustellen.

wenn wir uns eine Interaktion vorstellen, die sich wirklich wie eine einzelne Zelle verhÀlt

Ich denke, das ist der SchlĂŒssel, und es ist ein großes „Wenn“. So stelle ich es mir vor, aber ich bin (noch) nicht jeder.

Wenn wir uns eine Interaktion vorstellen, die sich wirklich wie eine einzelne Zelle verhÀlt
Ich denke, das ist der SchlĂŒssel, und es ist ein großes „Wenn“. So stelle ich es mir vor, aber ich bin (noch) nicht jeder.

Eine verwirrende Sache dabei ist, dass, wenn wir eine Zelle mit Code hĂ€tten, dann eine InteraktionsausfĂŒhrung, dann noch etwas mehr Code, die Zelle tatsĂ€chlich wie drei Zellen ausgefĂŒhrt wĂŒrde (dh nach der AusfĂŒhrung dreimal ausgefĂŒhrt). Ich denke, das flush_display macht in diesem Fall mehr Sinn.

ZusĂ€tzliche Frage: WĂŒrde die gespiegelte Ausgabe in JLab funktionieren?

@willingc- Widgets arbeiten mit gespiegelter Ausgabe.

@SylvainCorlay Ich weiß, dass sie jetzt funktionieren, und die Leute in Ann Arbor lieben es. Meine Frage war, wĂŒrde es funktionieren, wenn Änderungen wie oben beschrieben vorgenommen werden.

Entschuldigung fĂŒr den LĂ€rm.

Auf lange Sicht denke ich, dass ein Ansatz, der besser funktionieren sollte, das ipympl-Widgets-Backend fĂŒr Matplotlib ist.

All dies mĂŒssen wir jedoch mit conf.d usw. einfacher installieren.

Ja, die gespiegelte Ausgabe sollte mit den oben besprochenen Änderungen funktionieren. Danke, dass Sie sich vergewissern!

Eine verwirrende Sache dabei ist, dass, wenn wir eine Zelle mit Code hĂ€tten, dann eine InteraktionsausfĂŒhrung, dann noch etwas mehr Code, die Zelle tatsĂ€chlich wie drei Zellen ausgefĂŒhrt wĂŒrde (dh nach der AusfĂŒhrung dreimal ausgefĂŒhrt).

Das wĂ€re in der Tat etwas seltsam. WĂ€re es nicht zwei Mal? Eine fĂŒr die Zelle als Ganzes und eine fĂŒr den ersten Aufruf von interagieren? Je nachdem, wie Sie es sich vorstellen, macht dies auch Sinn: Mit @interact haben Sie zwei AusfĂŒhrungen: eine fĂŒr die Deklaration und eine weitere AusfĂŒhrung fĂŒr den ersten Aufruf der interagierten Funktion.

Ich glaube nicht, dass Sie mehr als einen haben wĂŒrden, wenn Sie beispielsweise @interact_manual (ich bin mir nicht sicher).

WĂ€re es nicht zwei Mal? Eine fĂŒr die Zelle als Ganzes und eine fĂŒr den ersten Aufruf von interagieren?

Betrachten Sie diesen Code:

plt.plot([1,2,3])
@interact(n=(1,10))
def f(n):
    plt.plot([1,n])
plt.plot([4,5,6])

Da die Darstellung kumulativ ist und Sie die Interaktion als separate Zelle fĂŒr sich behandeln möchten, mĂŒssen Sie die Figur vor der AusfĂŒhrung der Interaktion spĂŒlen, dann die Figur innerhalb der Interaktion spĂŒlen und dann noch einmal am Ende der Zelle. Genau das machen wir auch mit der SpĂŒlung stdout.

Eigentlich bin ich mir nicht sicher, was der Benutzer im obigen Beispiel erwartet. Was wĂŒrden Sie erwarten?

Nur ein Gedanke: Das obige Beispiel ist nicht sehr logisch; Persönlich wĂŒrde ich drei Plots erwarten, von denen sich nur einer dynamisch Ă€ndert, aber wenn ich so etwas tun wĂŒrde, wĂŒrde ich alle Plotaufrufe innerhalb von @interact .

Gibt es einen Grund , nicht zu nennen flush_figures nach jedem .plot() ?

@myyc es ist eine signifikante semantische Änderung des Verhaltens. plot fĂŒgt Elemente zum Plot hinzu....

Ich meinte das spezifische Inline-Verhalten von ipython im Gegensatz zu Matplotlib.

Bearbeiten : Ich bin mir sicher, dass es ein hÀufigerer Anwendungsfall ist, eine Zelle pro Zelle als "die einzige Plotting-Funktion" zu verwenden, im Gegensatz zu dem oben beschriebenen Fall ...

Ich verstehe. Obwohl ich nicht denke, dass dies ein so anderes Verhalten haben sollte.

Der richtige Ansatz in Zukunft wird wahrscheinlich darin bestehen, das Notebook-Backend oder ipympl zu verwenden und die Figur zu bearbeiten.

Die Implementierung von matplotlib-spezifischen Problemumgehungen fĂŒr das Inline-Backend in der Interaktion von ipywidgets scheint nicht richtig zu sein.

Im Code von Jason:

plt.plot([1,2,3])
@interact(n=(1,10))
def f(n):
    plt.plot([1,n])
plt.plot([4,5,6])

die erwartete Ausgabe ist eine Zahl mit vielen Zeilen..

Ja, ich stimme voll und ganz zu. Ich habe es in meinem lokalen Setup vorĂŒbergehend auf diese Weise behoben (wobei Pandas-Plot-Methoden ĂŒberschrieben wurden), "auf eine Lösung warten" :) Ignorieren Sie das, es fĂŒhrt eine Reihe schrecklicher Nebenwirkungen ein.

Ich habe gesehen, dass dieses Problem in einer Vielzahl von Github-Projekten aufgetreten ist, daher frage ich mich, was der beste Auflösungspfad sein sollte.

edit : warum schreibst du es dann nicht so, viel expliziter?

@interact(n=(1,10))
def f(n):
    plt.plot([1,2,3])
    plt.plot([1,n])
    plt.plot([4,5,6])

Sie können das ipympl-Paket (https://github.com/matplotlib/jupyter-matplotlib) ausprobieren, das das Matplotlib-Widget ist. ipympl befindet sich noch in einem ziemlich frĂŒhen Stadium, sollte aber einen schnelleren Release-Zeitplan haben als der Matplotlib-Kern und eine frĂŒhere UnterstĂŒtzung fĂŒr Jupyterlab usw.

OK stellt sich heraus, dass die Überschreibung eine schreckliche Idee ist. Ich freue mich auf einen Fix Upstream :)

Nur um eine ungebildete Benutzersicht beizutragen: Nach dem Update auf ipywidgets 6.0 stellte ich fest, dass ein Großteil meines Widget-Codes nicht funktionierte (aufgrund des hier besprochenen Problems). Ich konnte das Problem beheben, indem ich plt.show() Anrufe hinzufĂŒgte, aber ich habe auch festgestellt, dass ich sehr vorsichtig sein muss, wo ich solche Anrufe hinzufĂŒge; es scheint, dass dieser zu frĂŒhe Aufruf manchmal dazu fĂŒhrt, dass spĂ€tere Plots nicht angezeigt werden (auch wenn plt.show() erneut aufgerufen wird). Ich habe dies nicht genug aufgespĂŒrt, um ein minimales Beispiel zu erstellen, aber das aktuelle Verhalten scheint mir nicht intuitiv zu sein - ich programmiere durch Versuch und Irrtum, um es zu umgehen.

Welche genaue Änderung in ipywidgets hat das neue Verhalten tatsĂ€chlich verursacht? Werden die Post-Execute-Hooks ĂŒberhaupt nicht aufgerufen oder werden sie immer noch aufgerufen, aber ihre Ausgabe wird jetzt verworfen, weil das Widget genau bestimmt, welche Ausgaben dem Widget gehören? Wenn letzteres der Fall ist, scheint es nicht richtig zu sein, die erfasste Ausgabe wĂ€hrend des Hauptteils der Funktion zu isolieren.

Die Post-Execute-Hooks werden aufgerufen (ich bin mir ziemlich sicher) - Sie können dies sehen, indem Sie Folgendes tun:

import matplotlib.pyplot as plt
from ipywidgets import interact
%matplotlib inline

@interact(n=(0,10))
def f(n):
    plt.plot([0,n])

und Bewegen des Schiebers. Die Plots werden an die Zelle angehĂ€ngt - die Post-Execute-Hooks senden die Anzeigedatennachrichten. Die InteraktionsausfĂŒhrungen sind in der Tat speziell darauf ausgerichtet, nur die Ausgabe zu erfassen, die wĂ€hrend des Funktionsaufrufs erfolgt. Ich denke, das ist das Richtige. Beispielsweise können zwei verschiedene Interaktionen genau dieselbe Schiebereglerinstanz verwenden:

import matplotlib.pyplot as plt
from ipywidgets import interact, IntSlider
%matplotlib inline

x = IntSlider()
@interact(n=x)
def f(n):
    plt.plot([0,n])
    plt.show()
@interact(n=x)
def f(n):
    plt.plot([0,n,0,n])
    plt.show()

Wenn der einzelne Schieberegler (in einer der BenutzeroberflĂ€chen) optimiert wird, wĂŒrden beide Interaktionen zeigen, dass sich der Schieberegler bewegt, und beide Interaktionsfunktionen wĂŒrden im selben AusfĂŒhrungsaufruf ausgefĂŒhrt. Es wĂ€re falsch, wenn beide Interaktionen versuchen, die Ausgabe eines Post-Execute-Hooks zu erfassen. Ich denke, der Fehler hier ist, dass wir Post-Execute verwenden, um eine Ausgabe zu generieren, und das ist einfach ein zu grober Hammer. Wir sollten eine feinkörnigere Methode zum Leeren der Ausgabe haben, die in einem Codeblock generiert wurde und der in einer einzigen AusfĂŒhrung mehrmals aufgerufen werden kann.

Wir sollten eine feinkörnigere Methode zum Flushen der Ausgabe haben, die in einem Codeblock generiert wurde

Beim Plotten der Matplotlib geschieht dieses Leeren, wenn plt.show

Ich muss sehr vorsichtig sein, wo ich solche Anrufe hinzufĂŒge; es scheint, dass wenn dies manchmal zu frĂŒh aufgerufen wird, spĂ€tere Plots nicht angezeigt werden

Ich bin wirklich daran interessiert, Beispiele zu sehen, wo dies passiert. Wir wollen das Verhalten intuitiv und verstÀndlich machen.

Ich muss sehr vorsichtig sein, wo ich solche Anrufe hinzufĂŒge; es scheint, dass wenn dies manchmal zu frĂŒh aufgerufen wird, spĂ€tere Plots nicht angezeigt werden

Ich bin wirklich daran interessiert, Beispiele zu sehen, wo dies passiert. Wir wollen das Verhalten intuitiv und verstÀndlich machen.

Um das auszuarbeiten, ich denke, Sie sollten in der Lage sein, das Äquivalent des vorherigen Verhaltens zu erhalten, indem Sie einfach plt.show() als letzte Zeile jeder Interaktionsfunktion hinzufĂŒgen, die Plotten durchfĂŒhrt. Ich bin sehr daran interessiert, Beispiele zu sehen, bei denen dies nicht funktioniert.

@jasongrout Danke, dass

@jasongrout Nach etwas mehr gestoßen bin, nicht auf Widgets (aber es interagiert jetzt mit Widgets, da wir gezwungen sind, plt.show() aufzurufen).

Wenn man ein Diagramm in einer Notebookzelle erstellt und plt.show() aufruft, dann werden spĂ€tere Änderungen an der Figur nicht angezeigt. Hier ein Minimalbeispiel:

%matplotlib inline
import matplotlib.pyplot as plt

def plotfun(t):
    fig, ax = plt.subplots(1,1)
    ax.plot([0,t],[0,t])
    plt.show()
    ax.plot([t,0],[0,t])
    plt.show()

plotfun(2)

Nur das erste Liniendiagramm wird angezeigt.

Hier ist ein noch einfacheres Beispiel. Zwei Zellen, ohne das %matplotlib inline Backend. Vielleicht könnte @tacaswell etwas Licht ins Dunkel bringen, warum das zweite plt.show keine Handlung zeigt.

import matplotlib.pyplot as plt
fig, ax = plt.subplots(1,1)
ax.plot([0,1],[0,1])
plt.show()

zeigt Handlung

ax.plot([1,0],[0,1])
plt.show()

zeigt nichts an

BestÀtigt auf meinem Computer mit dem neuesten stabilen Jupyter, Ipython, Ipywidgets, Matplotlib, Python 3.

Auch hier bestÀtigt.

@jasongrout Das ist das erwartete Verhalten, denn in der ersten Zelle erstellen Sie eine Figur (und Leinwand und Achsen) und zeigen sie dann an. In der zweiten Zelle fĂŒgen Sie dann eine Linie zu den vorhandenen Achsen (in der vorhandenen Abbildung) hinzu. Mit %matplotlib qt oder ipympl (oder einer der vollstĂ€ndigen GUIs) _sollte_ der Plot mit der zweiten Zeile aktualisiert werden. Ich erwarte nicht, dass dies mit Inline funktioniert, da Sie nur eine Chance haben, es zu aktualisieren (weshalb ich kein Fan von Inline bin, es wirft _alle_ interaktiven Funktionen ein).

@tacaswell Ist dieses Verhalten von Inline auch fĂŒr das frĂŒhere Beispiel sinnvoll, das ich gegeben habe, das sich alles in einer Zelle befindet? Hier ist es wieder:

%matplotlib inline
import matplotlib.pyplot as plt

def plotfun(t):
    fig, ax = plt.subplots(1,1)
    ax.plot([0,t],[0,t])
    plt.show()
    ax.plot([t,0],[0,t])
    plt.show()

plotfun(2)

Ich denke, dass bei plt.show das Inline-Backend sich selbst in ein PNG rendert und dann die Figur von pyplot abmeldet, damit sie bei der nÀchsten Show nicht erneut gerendert wird, auch wenn sie sich in derselben Zelle befindet mehrere Aufrufe von Achsenmethoden ohne zusÀtzliche show s sollten funktionieren.

Um es klarzustellen, hier ist der Inline-Backend-Code: https://github.com/ipython/ipykernel/blob/master/ipykernel/pylab/backend_inline.py

Gibt es eine Version von ipywidgets und/oder matplotlib ich zurĂŒckkehren kann, damit meine interaktiven Widgets wieder funktionieren?

@jasongrout Es sieht so aus, als ob ich InlineBackend.close_figures auf False setzen möchte. Aber ich bin mir nicht sicher, wie ich dieses Attribut einstellen soll – muss ich irgendeine Art von Magie anwenden?

(die Frage klingt komisch, aber ich meine es wörtlich)

https://github.com/jupyter-widgets/ipywidgets/issues/1457 hat dieses Problem erneut aufgeworfen. @ellisonbg - Was ist, wenn wir die oben erwĂ€hnte Problemumgehung von flush_figures implementieren - rufen Sie flush_figures vor und nach dem AusfĂŒhren einer Interaktion auf, um alle Plots vor der Interaktion und alles, was wĂ€hrend der Interaktion gezeichnet wurde, auszuspĂŒlen? Ich denke, wir mĂŒssen möglicherweise ĂŒberprĂŒfen, ob wir im Inline-mpl-Backend ausgefĂŒhrt werden.

Es ist ein riesiger Fehler, aber vielleicht ist es wichtig genug, um es trotzdem zu tun. Vielleicht können wir auch erkennen, ob wir tatsĂ€chlich eine Zahl spĂŒlen und eine Warnung ausgeben, in der die Leute aufgefordert werden, einen expliziten show()-Aufruf zu tĂ€tigen?

Hallo zusammen, ich hatte die technischen Details nicht verfolgt, bin aber auf das Problem gestoßen. UnabhĂ€ngig von Implementierungsdetails halte ich es fĂŒr Ă€ußerst wichtig, dass matplotlib+interact so gut wie möglich "einfach funktioniert".

Mit plt.show() oder flush_figures() , wenn das Notebook aktualisiert wird (ohne den Kernel neu zu starten), ist die Zahl auch mit Save Notebook with Widgets verschwunden. Ohne diese AbschwÀchungen ist dies nicht der Fall, obwohl dann immer noch das Problem mehrerer Plotausgaben besteht. Ist das beabsichtigt? Gleiches gilt, wenn der Kernel heruntergefahren wird, wÀhrend bei Àlteren Versionen von ipywidgets der Endzustand der Figur Àhnlich einem nicht interaktiven Plot im Notebook gespeichert wurde, was recht praktisch war.

Entschuldigung, wenn dies außerhalb des Rahmens liegt oder ich etwas falsch mache. AusfĂŒhren von ipywidgets 6.0.0, Matplotlib 2.0.2, Notebook 5.0.0 und Python 3.6.1.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen