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

93

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

Ответы:

62

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

%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 Widget, работа над nbaggкоторой в настоящее время ведется в репозитории Matplotlib . Когда это будет доступно, это, вероятно, будет лучшим способом использования nbagg.

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

пневматика
источник
1
Отлично, похоже, неплохо работает. Отсутствие интерактивности во время работы для меня не проблема. Одна немного странная вещь: если я изменю while True:на цикл for, по окончании цикла я получу два статических изображения последнего сюжета, а не интерактивное изображение nbagg. Есть идеи, почему это так?
Натаниэль
Я изменил while на цикл for и попробовал его на tmpnb.org, но я не вижу второго изображения или потери интерактивности. Снято в темноте, но вы можете попробовать переместить цикл вокруг вызова функции, вместо того, чтобы иметь цикл в функции. для f в диапазоне (10): pltsin (ax) time.sleep (1)
пневматика
3
@pneumatics К сожалению, у него есть проблемы с Matplotlib 2.0 на дисплее Retina: в цикле графики в два раза меньше, чем обычно.
Александр Родин
1
Похоже, фигуре не дается время правильно изменить размер. Так что у меня был гораздо лучший опыт, когда я помещал plt.show()и перемещал цикл for в следующую ячейку.
ImportanceOfBeingErnest
2
Убедитесь, что у вас есть блокнот% matplotlib в той же ячейке блокнота jupyter, что и ваш график - сегодня я потратил более 2 часов на устранение неполадок, потому что у меня был блокнот% matplotlib в первой ячейке со
статусами
12

Я использую 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)

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

Зиофил
источник
1
это создает новый сюжет каждый раз, а не обновляет существующий сюжет
пневматика
2
Верный. Я не нашел лучшего способа создания динамического сюжета в jupyter-lab.
Ziofil
1
Есть ли способ установить время ожидания между итерациями? вместо того, чтобы просто "wait = True"
Ахмад Мусса
1
Каждый раз, когда график перерисовывается, график мерцает. Есть ли способ решить эту проблему? У меня под графиком есть несколько пустых ячеек, но это, похоже, не помогает.
MasayoMusic
@MasayoMusic см. «Мерцающий и скачущий результат» в buildmedia.readthedocs.org/media/pdf/ipywidgets/latest/…
leo
0

Я адаптировал ответ @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)внутри цикла. вот как это выглядит: введите описание изображения здесь

Мигель Сильва
источник