【问题标题】:How to dynamically update a plot in a loop in IPython notebook (within one cell)如何在 IPython 笔记本中循环动态更新绘图(在一个单元格内)
【发布时间】:2014-02-17 01:57:29
【问题描述】:

环境: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() 函数时,就像我上面提到的打印语句输出一样,效果很好)。仅在单元格完全完成后显示最终图表不是我想要的。

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

【问题讨论】:

    标签: python pandas matplotlib jupyter-notebook ipython


    【解决方案1】:

    使用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)
    

    【讨论】:

    • 这不是平滑选项,情节是从头开始重新创建的,单元格在其间上下移动
    • 添加clear_output(wait=True) 解决了这个问题。请参阅下面的 wabu 答案。
    • 这些天你可以通过%matplotlib nbagg 做得更好,它给你一个活生生的人物来玩。
    • @tcaswell 我添加了一个新问题,询问如何使用nbagg 来实现这一目标。 (如果您有兴趣回答,请联系您。)stackoverflow.com/questions/34486642/…
    • 这可行,但也会破坏单元格中的任何其他内容,例如打印的度量值。有没有办法真的只是更新情节并保持其他一切到位?
    【解决方案2】:

    HYRY's answer 的一些改进:

    • clear_output 之前调用display,这样当单元格被打断时,您会得到一个情节,而不是两个情节。
    • 捕捉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
    

    【讨论】:

    • 确实,display.display(gcf()) 应该在之前 display.clear_output(wait=True)
    • 谢谢,@csta。已添加。
    • @herrlich10 为什么要在clear_output 之前调用display?你不应该先清除输出然后显示新数据,而不是反过来吗?
    • 图表更新时屏幕仍然闪烁,但并非一直如此。有解决办法吗?
    • 如果您还尝试在循环开始时打印文本,我发现这会导致图形消失,因此它仅在一瞬间可见。当display() 调用放在clear_output() 之后时,我没有这个问题。
    【解决方案3】:

    您可以通过将wait=True 添加到clear_output 来进一步改进:

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

    【讨论】:

    • +1。这个非常重要。我认为 HYRY 的答案应该用这个信息更新。
    • 这很好,但也有清除打印输出的烦人副作用。
    【解决方案4】:

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

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

    【讨论】:

      【解决方案5】:

      为此处发布的其他解决方案添加标签将在每个循环中不断添加新标签。要解决这个问题,请使用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())
      

      【讨论】:

      • 感谢plt.clf() 工作。但是有没有办法消除更新中的闪烁?
      【解决方案6】:

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

      【讨论】:

      • 谢谢。 gcf().show() 也可以。需要添加 HYRY 建议的 clear_output() 以在同一个图上显示内容
      • 这是除了“display.display(pl.gcf())”吗?
      【解决方案7】:

      你可以这样做。它接受 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)。下面是它的外观:

      【讨论】:

        猜你喜欢
        • 2015-11-10
        • 1970-01-01
        • 2016-02-04
        • 1970-01-01
        • 2015-03-20
        • 2013-11-13
        • 2013-10-24
        相关资源
        最近更新 更多