110

环境:Python 2.7、Matplotlib 1.3、IPython notebook 1.1、Linux 和 Chrome。代码位于一个输入单元格中,使用--pylab=inline.

我想使用 IPython notebook 和 Pandas 来消费一个流并每五秒动态更新一个图。

当我只使用打印语句以文本格式打印数据时,它工作得非常好:输出单元格只是保持打印数据并添加新行。但是当我尝试绘制数据(然后循环更新)时,该图永远不会出现在输出单元格中。但是,如果我删除循环,并且只绘制一次,它就可以正常工作。

然后我做了一些简单的测试:

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。在我打断它之后,该图正确显示为多条重叠线。但我真正想要的是每五秒显示并更新一次的图(或者每当plot()调用函数时,就像我上面提到的 print 语句输出一样,效果很好)。仅在单元格完全完成后显示最终图表不是我想要的。

我什至尝试在每个plot()等之后显式添加 draw() 函数。它们都不起作用。如何在 IPython 笔记本的一个单元格中通过for / while循环动态更新绘图?

4

7 回答 7

135

使用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)
于 2014-01-26T09:51:06.590 回答
36

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
于 2015-04-16T13:09:09.800 回答
33

您可以通过添加以下内容wait=True来进一步改进clear_output

display.clear_output(wait=True)
display.display(pl.gcf())
于 2014-07-17T13:09:07.913 回答
5

我尝试了很多方法,但我发现这是最简单和最简单的方法 -> 添加 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()

这会覆盖同一个情节,并给出情节动画的错觉

于 2021-01-14T05:56:54.173 回答
4

向此处发布的其他解决方案添加标签将在每个循环中不断添加新标签。为了解决这个问题,请使用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())
于 2019-07-07T16:03:52.203 回答
2

尝试在函数之后添加show()或。这些将强制当前图形更新(gcf() 返回当前图形的引用)。gcf().show()plot()

于 2014-01-26T06:36:52.373 回答
2

你可以这样做。它接受 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)在循环内调用。这是它的外观:

在此处输入图像描述

于 2020-08-03T19:06:08.990 回答