Как динамически обновить график в цикле в блокноте ipython (в пределах одной ячейки)

Среда: Python 2.7, matplotlib 1.3, IPython notebook 1.1, linux, chrome. Код находится в одной входной ячейке, используя --pylab=inline

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

Когда я просто использую оператор print для печати данных в текстовом формате, он отлично работает: выходная ячейка просто сохраняет данные печати и добавляет новые строки. Но когда я пытаюсь построить данные (а затем обновить их в цикле), график никогда не отображается в выходной ячейке. Но если я уберу петлю, просто нарисуй ее один раз. Работает нормально.

Затем я сделал простой тест:

i = pd.date_range('2013-1-1',periods=100,freq='s')
while True:
    plot(pd.Series(data=np.random.randn(100), index=i))
    #pd.Series(data=np.random.randn(100), index=i).plot() also tried this one
    time.sleep(5)

Вывод ничего не покажет, пока я не прерву процесс вручную (ctrl+m+i). И после того, как я прервал его, график правильно отображается в виде нескольких перекрывающихся линий. Но что я действительно хочу, так это сюжет, который появляется и обновляется каждые 5 секунд (или всякий раз, когда plot() Функция вызывается так же, как то, что выводит оператор print, о котором я упоминал выше, что хорошо работает Только показ окончательного графика после того, как ячейка полностью сделана, это НЕ то, что я хочу.

Я даже пытался явно добавить функцию draw() после каждого plot()и т.д. Ни один из них не работает. Хотите знать, как динамически обновлять график с помощью цикла for/while внутри одной ячейки в записной книжке IPython.

9 ответов

Решение

Использование IPython.display модуль:

%matplotlib inline
import time
import pylab as pl
from IPython import display
for i in range(10):
    pl.plot(pl.randn(100))
    display.clear_output(wait=True)
    display.display(pl.gcf())
    time.sleep(1.0)

Несколько улучшений в ответе HYRY:

  • вызов display до clear_output так что вы получите один график, а не два, когда ячейка будет прервана.
  • поймать KeyboardInterrupt, чтобы вывод ячейки не был засорен трассировкой.
import matplotlib.pylab as plt
import pandas as pd
import numpy as np
import time
from IPython import display
%matplotlib inline

i = pd.date_range('2013-1-1',periods=100,freq='s')

while True:
    try:
        plt.plot(pd.Series(data=np.random.randn(100), index=i))
        display.display(plt.gcf())
        display.clear_output(wait=True)
        time.sleep(1)
    except KeyboardInterrupt:
        break

Вы можете улучшить это, добавив wait=True в clear_output:

display.clear_output(wait=True)
display.display(pl.gcf())

Я пробовал много методов, но нашел это как самый простой и легкий способ -> добавить clear_output(wait=True), например,

      from IPython.display import clear_output

for i in range(n_iterations):
     clear_output(wait=True)
     x = some value
     y = some value
     plt.plot(x, y, '-r')
     plt.show()

Это перезаписывает тот же сюжет и дает иллюзию сюжетной анимации.

Добавление метки к другим решениям, размещенным здесь, будет продолжать добавлять новые метки в каждом цикле. Чтобы справиться с этим, очистите сюжет, используя clf

for t in range(100)
   if t % refresh_rate == 0:

     plt.clf()
     plt.plot(history['val_loss'], 'r-', lw=2, label='val')
     plt.plot(history['training_loss'], 'b-', lw=1, label='training')
     plt.legend()
     display.clear_output(wait=True)
     display.display(plt.gcf())

Вы можете сделать это так. Он принимает 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/wPrNh.png

Попробуй добавить show() или же gcf().show() после plot() функция. Это заставит текущий рисунок обновиться (gcf() возвращает ссылку на текущий рисунок).

Хорошее решение было предложено user854731 в соответствующем посте . Он заключается в использованиисdisplay_id=Trueчтобы получить IPython.display.displayдескриптор и использоватьupdate()метод на нем.

Например,

      import time

from IPython.display import display
from matplotlib import pyplot as plt

import numpy as np


hdisplay_img = display(display_id=True)
hdisplay_txt = display(display_id=True)

fig = plt.figure()
ax = fig.add_subplot(111)
im = ax.imshow(np.random.random((10,10,3)))
plt.close()

def update(i):
    im.set_data(np.random.random((10,10,3)))
    ax.add_image(im)
    hdisplay_img.update(fig)
    hdisplay_txt.update(f"update {i}")

for f in range(10):
    update(f)
    time.sleep(1)

Минимальное современное решение для ноутбуков в Jupyter Lab (версия 3.6.1):

      from matplotlib import pyplot as plt

fig, ax = plt.subplots(1)
ax.set(xlabel=f'Epochs', ylabel='Value',
       title='Dynamics')

for i in range(n_step):
   ...
   ax.plot(...)
   
   if i==0:
      ax.legend(labels, loc='upper right')
   else:
      display(fig, clear=True);

Уведомление! Нет необходимости ни в том, ни в другом%matplotlib inlineниfrom IPython.display import clear_output, displayпотому что по умолчанию в новом блокноте уже есть функция с подписью:

      display(
    *objs,
    include=None,
    exclude=None,
    metadata=None,
    transient=None,
    display_id=None,
    raw=False,
    clear=False,
    **kwargs,
)

Обратите внимание на аргумент ключевого словаclear! Установка его наTrueзаключает сделку.

Другие вопросы по тегам