Quelle est la manière actuellement correcte de mettre à jour dynamiquement les tracés dans Jupyter / iPython?


93

Dans les réponses à la mise à jour dynamique d'un tracé dans une boucle dans un bloc-notes ipython (dans une cellule) , un exemple est donné sur la façon de mettre à jour dynamiquement un tracé dans un bloc-notes Jupyter dans une boucle Python. Cependant, cela fonctionne en détruisant et en recréant l'intrigue à chaque itération, et un commentaire dans l'un des fils note que cette situation peut être améliorée en utilisant la %matplotlib nbaggmagie new-ish , qui fournit une figure interactive intégrée dans le cahier, plutôt qu'une image statique.

Cependant, cette merveilleuse nouvelle nbaggfonctionnalité semble être complètement non documentée pour autant que je sache, et je suis incapable de trouver un exemple de la façon de l'utiliser pour mettre à jour dynamiquement un tracé. Ma question est donc de savoir comment mettre à jour efficacement un tracé existant dans un notebook Jupyter / Python, en utilisant le backend nbagg? Étant donné que la mise à jour dynamique des graphiques dans matplotlib est une question délicate en général, un exemple de travail simple serait d'une grande aide. Un pointeur vers toute documentation sur le sujet serait également extrêmement utile.

Pour être clair ce que je demande: ce que je veux faire est d'exécuter du code de simulation pour quelques itérations, puis de dessiner un tracé de son état actuel, puis de l'exécuter pendant quelques itérations supplémentaires, puis de mettre à jour le tracé pour refléter l'état actuel, et ainsi de suite. L'idée est donc de dessiner un tracé puis, sans aucune interaction de l'utilisateur, de mettre à jour les données du tracé sans détruire et recréer le tout.

Voici un code légèrement modifié de la réponse à la question liée ci-dessus, qui y parvient en redessinant la figure entière à chaque fois. Je veux obtenir le même résultat, mais plus efficacement en utilisant 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)

Réponses:


62

Voici un exemple qui met à jour un tracé dans une boucle. Il met à jour les données de la figure et ne redessine pas la figure entière à chaque fois. Cela bloque l'exécution, mais si vous souhaitez exécuter un ensemble fini de simulations et enregistrer les résultats quelque part, cela peut ne pas être un problème pour vous.

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

J'ai mis ceci sur nbviewer ici.

Il existe une version de widget IPython nbaggqui est actuellement en cours de travail dans le référentiel Matplotlib . Lorsque cela sera disponible, ce sera probablement la meilleure façon d'utiliser nbagg.

EDIT: mis à jour pour afficher plusieurs tracés


1
Génial, cela semble bien fonctionner. Le manque d'interactivité pendant son exécution n'est pas un gros problème pour moi. Une chose un peu étrange: si je change la while True:en une boucle for, quand la boucle se termine, j'obtiens deux images statiques du dernier tracé, plutôt qu'une nbagg interactive. Une idée pourquoi c'est?
Nathaniel

J'ai changé le while en une boucle for et l'ai essayé sur tmpnb.org, mais je ne vois pas de seconde image ni de perte d'interactivité. Tourné dans le noir, mais vous pouvez essayer de déplacer la boucle autour de l'appel à la fonction, plutôt que d'avoir la boucle dans la fonction. pour f dans la plage (10): pltsin (ax) time.sleep (1)
pneumatics

3
@pneumatics Malheureusement, il a quelques problèmes avec Matplotlib 2.0 sur l'affichage Retina: dans la boucle, les tracés sont deux fois plus petits que d'habitude.
Alexander Rodin

1
Il semble que la figure n'ait pas le temps de se redimensionner correctement. J'ai donc eu une bien meilleure expérience en plaçant un plt.show()et en déplaçant la boucle for vers la cellule suivante.
ImportanceOfBeingErnest

2
Assurez-vous que vous avez le bloc-notes% matplotlib dans la même cellule de bloc-notes jupyter que votre intrigue - j'ai passé plus de 2 heures aujourd'hui à résoudre ce problème parce que j'avais le bloc-notes% matplotlib dans la première cellule avec les déclarations d'importation
aguazul le

12

J'utilise jupyter-lab et cela fonctionne pour moi (adaptez-le à votre cas):

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

Ensuite, dans une boucle, vous remplissez un dictionnaire et vous le transmettez à 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)

assurez-vous d'avoir quelques cellules sous le tracé, sinon la vue se met en place chaque fois que le tracé est redessiné.


1
cela crée un nouveau tracé à chaque fois plutôt que de mettre à jour le tracé existant
pneumatique

2
Correct. Je n'ai pas trouvé de meilleur moyen d'avoir un tracé dynamique dans jupyter-lab.
Ziofil

1
Existe-t-il un moyen de définir combien de temps il attend entre les itérations? plutôt que d'avoir simplement un 'wait = True'
Ahmad Moussa

1
Chaque fois que le tracé est redessiné, le graphique scintille. Existe-t-il un moyen de résoudre ce problème? J'ai quelques cellules vides sous l'intrigue, mais cela ne semble pas aider.
MasayoMusic

@MasayoMusic voir "Sortie scintillante
leo

0

J'ai adapté la réponse @Ziofil et l'ai modifiée pour accepter x, y comme liste et générer un nuage de points plus une tendance linéaire sur le même graphique.

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

il vous suffit d'appeler à l' live_plot(x, y)intérieur d'une boucle. voici à quoi ça ressemble: entrez la description de l'image ici

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.