¿Cuál es la forma correcta actualmente de actualizar trazados dinámicamente en Jupyter / iPython?

93

En las respuestas sobre cómo actualizar dinámicamente una gráfica en un bucle en el cuaderno ipython (dentro de una celda) , se da un ejemplo de cómo actualizar dinámicamente una gráfica dentro de un cuaderno Jupyter dentro de un bucle Python. Sin embargo, esto funciona destruyendo y recreando la trama en cada iteración, y un comentario en uno de los hilos señala que esta situación se puede mejorar usando la %matplotlib nbaggmagia nueva , que proporciona una figura interactiva incrustada en el cuaderno, en lugar de que una imagen estática.

Sin embargo, esta maravillosa nbaggcaracterística nueva parece estar completamente indocumentada por lo que puedo decir, y no puedo encontrar un ejemplo de cómo usarla para actualizar dinámicamente una trama. Por lo tanto, mi pregunta es, ¿cómo se actualiza de manera eficiente una trama existente en un cuaderno Jupyter / Python, usando el backend nbagg? Dado que la actualización dinámica de gráficos en matplotlib es un problema complicado en general, un ejemplo de trabajo simple sería de gran ayuda. Un puntero a cualquier documentación sobre el tema también sería de gran ayuda.

Para que quede claro lo que estoy pidiendo: lo que quiero hacer es ejecutar un código de simulación para algunas iteraciones, luego dibujar un diagrama de su estado actual, luego ejecutarlo para algunas iteraciones más, luego actualizar el diagrama para reflejar el estado actual y así sucesivamente. Entonces, la idea es dibujar una trama y luego, sin ninguna interacción por parte del usuario, actualizar los datos en la trama sin destruir y volver a crear todo.

Aquí hay un código ligeramente modificado de la respuesta a la pregunta vinculada anterior, que logra esto al volver a dibujar la figura completa cada vez. Quiero lograr el mismo resultado, pero usando de manera más eficiente 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)
Nathaniel
fuente

Respuestas:

62

Aquí hay un ejemplo que actualiza un gráfico en un bucle. Actualiza los datos de la figura y no vuelve a dibujar la figura completa cada vez. Bloquea la ejecución, aunque si está interesado en ejecutar un conjunto finito de simulaciones y guardar los resultados en algún lugar, puede que no sea un problema para usted.

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

Puse esto en nbviewer aquí.

Hay una versión de Widget de IPython nbaggque actualmente se encuentra en progreso en el repositorio de Matplotlib . Cuando esté disponible, probablemente sea la mejor forma de utilizarlo nbagg.

EDITAR: actualizado para mostrar múltiples parcelas

neumática
fuente
1
Genial, eso parece funcionar muy bien. La falta de interactividad mientras se ejecuta no es un gran problema para mí. Una cosa un poco extraña: si cambio while True:a un bucle for, cuando el bucle finaliza, obtengo dos imágenes estáticas del último gráfico, en lugar de una nbagg interactiva. ¿Alguna idea de por qué es eso?
Nathaniel
Cambié el while a un bucle for y lo probé en tmpnb.org, pero no veo una segunda imagen ni una pérdida de interactividad. Filmado en la oscuridad, pero puede intentar mover el bucle alrededor de la llamada a la función, en lugar de tener el bucle en la función. para f en el rango (10): pltsin (ax) time.sleep (1)
neumática
3
@pneumatics Desafortunadamente, tiene algunos problemas con Matplotlib 2.0 en la pantalla Retina: en el bucle, los gráficos son dos veces más pequeños de lo habitual.
Alexander Rodin
1
Parece que a la figura no se le da tiempo para cambiar su tamaño correctamente. Así que tuve una experiencia mucho mejor al colocar plt.show()y mover el bucle for a la siguiente celda.
ImportanceOfBeingErnest
2
Asegúrese de tener el cuaderno% matplotlib en la misma celda del cuaderno jupyter que su trama. Pasé más de 2 horas resolviendo esto porque tenía% matplotlib notebook en la primera celda con las declaraciones de importación
aguazul
12

Estoy usando jupyter-lab y esto funciona para mí (adáptelo a su caso):

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

Luego, en un bucle, llena un diccionario y lo pasa a 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)

asegúrese de tener algunas celdas debajo del gráfico; de lo contrario, la vista se ajusta en su lugar cada vez que se vuelve a dibujar el gráfico.

Ziofil
fuente
1
esto crea una nueva trama cada vez en lugar de actualizar la trama existente
Neumática
2
Correcto. No he encontrado una mejor manera de tener una trama dinámica en jupyter-lab.
Ziofil
1
¿Hay alguna forma de establecer cuánto tiempo espera entre iteraciones? en lugar de simplemente tener un 'esperar = Verdadero'
Ahmad Moussa
1
Cada vez que se vuelve a dibujar el gráfico, el gráfico parpadea. ¿Existe alguna forma de solucionar este problema? Tengo algunas celdas vacías debajo de la trama, pero eso no parece ayudar.
MasayoMusic
@MasayoMusic ver "Salida parpadeante y saltando" en buildmedia.readthedocs.org/media/pdf/ipywidgets/latest/…
leo
0

He adaptado la respuesta de @Ziofil y la modifiqué para aceptar x, y como lista y generar un diagrama de dispersión más una tendencia lineal en el mismo diagrama.

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

solo necesitas llamar live_plot(x, y)dentro de un bucle. así es como se ve: ingrese la descripción de la imagen aquí

Miguel Silva
fuente