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

93

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

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

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

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

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

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

user3236895
источник

Ответы:

122

использовать 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
источник
4
это не гладкий вариант, сюжет воссоздается с нуля с
перемещением
3
Добавление clear_output(wait=True)решает эту проблему. См. Ответ Вабу ниже.
Ахвиллия 03 окт.'14
3
В наши дни вы можете добиться большего, и это %matplotlib nbaggдаст вам живую фигуру, с которой можно поиграть.
tacaswell
@tcaswell Я добавил новый вопрос, спрашивающий, как это можно использовать nbagg. (Пингует вас, если вы хотите ответить на него.) Stackoverflow.com/questions/34486642/…
Натаниэль
3
это работает, но также уничтожает все остальное в ячейке, например, напечатанные меры. Есть ли способ просто обновить сюжет и оставить все остальное на месте?
KIC
31

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

display.clear_output(wait=True)
display.display(pl.gcf())
вабу
источник
1
+1. Это очень важно. Я думаю, что ответ HYRY следует обновить с помощью этой информации.
Ахвиллия 02
5
Это хорошо, но имеет неприятный побочный эффект очистки вывода на печать.
Питер
31

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

  • call displaybefore, clear_outputчтобы получить один график, а не два, когда ячейка прерывается.
  • catch 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
Том Филлипс
источник
7
Действительно, display.display(gcf())надо идти ДО display.clear_output(wait=True)
herrlich10
Спасибо, @csta. Добавил.
Том Филлипс
@ herrlich10 Почему надо звонить displayраньше clear_output? Разве вы не должны сначала очистить вывод, а затем отобразить новые данные, вместо того, чтобы делать это наоборот?
Якуб Арнольд
1
У меня все еще мерцает экран при обновлении графика, но это не всегда. Есть ли обходной путь?
MasayoMusic
3

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

мюон
источник
4
Спасибо plt.clf()работает. Однако есть ли способ избавиться от мерцания обновлений?
MasayoMusic
2

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

Саулло Г. П. Кастро
источник
2
Спасибо. gcf (). show () также работает. Необходимо добавить функцию clear_output (), предложенную HYRY, чтобы отображать все на том же
фигуре
Это дополнение к "display.display (pl.gcf ())"?
MasayoMusic
0

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

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