Как динамически обновить график в цикле в блокноте 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
заключает сделку.