Каков в настоящее время правильный способ динамического обновления графиков в Jupyter/iPython?

В ответах на вопросы о том, как динамически обновлять график в цикле в записной книжке ipython (в пределах одной ячейки), приводится пример того, как динамически обновлять график внутри записной книжки Jupyter в цикле Python. Тем не менее, это работает путем уничтожения и воссоздания графика на каждой итерации, и комментарий в одном из потоков отмечает, что эту ситуацию можно улучшить с помощью new-ish %matplotlib nbagg магия, которая обеспечивает интерактивную фигуру, встроенную в ноутбук, а не статичное изображение.

Тем не менее, этот замечательный новый nbagg Насколько я могу судить, эта функция полностью недокументирована, и я не могу найти пример того, как использовать ее для динамического обновления графика. Таким образом, мой вопрос состоит в том, как эффективно обновить существующий график в блокноте Jupyter/Python, используя бэкэнд nbagg? Так как динамическое обновление графиков в matplotlib является сложной задачей в целом, простой рабочий пример был бы огромной помощью. Указатель на любую документацию по теме также был бы чрезвычайно полезен.

Чтобы было ясно, о чем я прошу: я хочу запустить код моделирования для нескольких итераций, затем нарисовать график его текущего состояния, затем запустить еще несколько итераций, а затем обновить график, чтобы отразить текущее состояние и тд. Таким образом, идея состоит в том, чтобы нарисовать график, а затем, без какого-либо взаимодействия с пользователем, обновить данные в графике, не разрушая и не создавая заново все это.

Вот некоторый слегка измененный код из ответа на связанный вопрос выше, который достигает этого, перерисовывая всю фигуру каждый раз. Я хочу добиться того же результата, но более эффективно используя 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)

5 ответов

Решение

Вот пример, который обновляет график в цикле. Он обновляет данные на рисунке и не перерисовывает всю фигуру каждый раз. Он блокирует выполнение, хотя, если вы заинтересованы в запуске конечного набора симуляций и сохранении результатов где-то, это может не быть проблемой для вас.

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

Я разместил это на nbviewer здесь.

Существует версия виджета IPython nbagg в настоящее время эта работа ведется в хранилище Matplotlib. Когда это будет доступно, это, вероятно, будет лучшим способом использования nbagg,

РЕДАКТИРОВАТЬ: обновлено, чтобы показать несколько графиков

Я использую jupyter-lab, и это работает для меня (адаптируйте его к вашему случаю):

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

Затем в цикле вы заполняете словарь и передаете его 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)

убедитесь, что у вас есть несколько ячеек под графиком, в противном случае представление фиксируется на месте каждый раз, когда график перерисовывается.

Если вы не хотите очищать все выходы, вы можете использовать display_id=True получить ручку и использовать .update() в теме:

      import numpy as np
import matplotlib.pyplot as plt
import time

from IPython import display

def pltsin(ax, *,hdisplay, 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)
    hdisplay.update(fig)


fig,ax = plt.subplots(1,1)
hdisplay = display.display("", display_id=True)

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, colors=['b', 'r'], hdisplay=hdisplay)
    time.sleep(1)
    
plt.close(fig)

(адаптировано из @pneumatics)

Я адаптировал ответ @Ziofil и изменил его, чтобы принимать x,y в качестве списка и выводить график рассеяния плюс линейный тренд на том же графике.

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

тебе просто нужно позвонить live_plot(x, y)внутри петли. вот как это выглядит: https://i.st ack.imgur.com/68I8z.png

The canvas.drawметод фигуры динамически обновляет свои графики для текущей фигуры:

      from matplotlib import pyplot as plt

plt.gcf().canvas.draw()
Другие вопросы по тегам