目前在 Jupyter/iPython 中动态更新绘图的正确方法是啥?

Posted

技术标签:

【中文标题】目前在 Jupyter/iPython 中动态更新绘图的正确方法是啥?【英文标题】:What is the currently correct way to dynamically update plots in Jupyter/iPython?目前在 Jupyter/iPython 中动态更新绘图的正确方法是什么? 【发布时间】:2016-04-01 21:31:22 【问题描述】:

在how to dynamically update a plot in a loop in ipython notebook (within one cell) 的答案中,给出了一个示例,说明如何在 Python 循环中动态更新 Jupyter 笔记本中的绘图。然而,这是通过在每次迭代中破坏和重新创建情节来实现的,其中一个线程中的评论指出,可以通过使用新的%matplotlib nbagg 魔法来改善这种情况,它提供了一个嵌入在笔记本,而不是静态图像。

然而,据我所知,这个美妙的nbagg 新功能似乎完全没有文档记录,而且我找不到如何使用它来动态更新绘图的示例。因此我的问题是,如何使用 nbagg 后端有效地更新 Jupyter/Python 笔记本中的现有绘图? 由于在 matplotlib 中动态更新绘图通常是一个棘手的问题,一个简单的工作示例将是一个巨大的帮助。指向有关该主题的任何文档的指针也将非常有帮助。

要明确我的要求:我想做的是运行一些模拟代码进行几次迭代,然后绘制其当前状态图,然后再运行几次迭代,然后更新绘制以反映当前状态,依此类推。所以我们的想法是绘制一个绘图,然后在不与用户进行任何交互的情况下更新绘图中的数据,而不会破坏和重新创建整个事物。

这里是上面链接问题的答案中的一些稍微修改的代码,它通过每次重新绘制整个图形来实现这一点。我想获得相同的结果,但使用nbagg 更有效。

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

【问题讨论】:

【参考方案1】:

这是一个循环更新绘图的示例。它会更新图中的数据,并且不会每次都重绘整个图。它确实会阻止执行,但如果您有兴趣运行一组有限的模拟并将结果保存在某处,那么这对您来说可能不是问题。

%matplotlib notebook

import numpy as np
import matplotlib.pyplot as plt
import time

def pltsin(ax, colors=['b']):
    x = np.linspace(0,1,100)
    if ax.lines:
        for line in ax.lines:
            line.set_xdata(x)
            y = np.random.random(size=(100,1))
            line.set_ydata(y)
    else:
        for color in colors:
            y = np.random.random(size=(100,1))
            ax.plot(x, y, color)
    fig.canvas.draw()

fig,ax = plt.subplots(1,1)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_xlim(0,1)
ax.set_ylim(0,1)
for f in range(5):
    pltsin(ax, ['b', 'r'])
    time.sleep(1)

I put this up on nbviewer here.

有一个IPython Widget version of nbagg that is currently a work in progress at the Matplotlib repository。如果可用,那可能是使用nbagg 的最佳方式。

编辑:更新以显示多个图

【讨论】:

太好了,这似乎工作得很好。它运行时缺乏交互性对我来说不是一个大问题。一件有点奇怪的事情:如果我将 while True: 更改为 for 循环,当循环结束时,我会得到最后一个图的两个静态图像,而不是交互式 nbagg 图像。知道这是为什么吗? 我将 while 更改为 for 循环并在 tmpnb.org 上进行了尝试,但我没有看到第二张图像或失去交互性。在黑暗中拍摄,但您可以尝试在对函数的调用周围移动循环,而不是在函数中使用循环。对于范围内的 f (10):pltsin(ax) time.sleep(1) @pneumatics 不幸的是,Matplotlib 2.0 在 Retina 显示屏上存在一些问题:循环中的图比通常小两倍。 似乎没有给这个数字正确调整大小的时间。因此,在放置 plt.show() 并将 for 循环移动到下一个单元格时,我的体验要好得多。 确保您的 %matplotlib 笔记本与您的绘图在同一个 jupyter 笔记本单元格中 - 我今天花了 2 个多小时解决此问题,因为我在第一个单元格中有 %matplotlib 笔记本和导入语句 【参考方案2】:

我正在使用 jupyter-lab,这对我有用(根据您的情况调整):

from IPython.display import clear_output
from matplotlib import pyplot as plt
import numpy as np
import collections
%matplotlib inline

def live_plot(data_dict, figsize=(7,5), title=''):
    clear_output(wait=True)
    plt.figure(figsize=figsize)
    for label,data in data_dict.items():
        plt.plot(data, label=label)
    plt.title(title)
    plt.grid(True)
    plt.xlabel('epoch')
    plt.legend(loc='center left') # the plot evolves to the right
    plt.show();

然后在循环中填充字典并将其传递给live_plot()

data = collections.defaultdict(list)
for i in range(100):
    data['foo'].append(np.random.random())
    data['bar'].append(np.random.random())
    data['baz'].append(np.random.random())
    live_plot(data)

确保绘图下方有几个单元格,否则每次重绘绘图时视图都会对齐。

【讨论】:

这每次都会创建一个新图,而不是更新现有图 正确。我还没有找到在 jupyter-lab 中绘制动态图的更好方法。 有没有办法设置它在迭代之间等待多长时间?而不仅仅是“等待=真” 每次重绘绘图时,图形都会闪烁。有没有办法解决这个问题?我在情节下方有几个空单元格,但这似乎没有帮助。 @MasayoMusic 见buildmedia.readthedocs.org/media/pdf/ipywidgets/latest/…中的“闪烁和跳跃输出”【参考方案3】:

如果不想清除所有输出,可以使用display_id=True获取句柄,在上面使用.update()

import numpy as np
import matplotlib.pyplot as plt
import time

from IPython import display

def pltsin(ax, *,hdisplay, colors=['b']):
    x = np.linspace(0,1,100)
    if ax.lines:
        for line in ax.lines:
            line.set_xdata(x)
            y = np.random.random(size=(100,1))
            line.set_ydata(y)
    else:
        for color in colors:
            y = np.random.random(size=(100,1))
            ax.plot(x, y, color)
    hdisplay.update(fig)


fig,ax = plt.subplots(1,1)
hdisplay = display.display("", display_id=True)

ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_xlim(0,1)
ax.set_ylim(0,1)
for f in range(5):
    pltsin(ax, colors=['b', 'r'], hdisplay=hdisplay)
    time.sleep(1)
    
plt.close(fig)

(改编自@pneumatics)

【讨论】:

【参考方案4】:

我已经调整了@Ziofil 答案并将其修改为接受 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)。 这是它的外观:

【讨论】:

以上是关于目前在 Jupyter/iPython 中动态更新绘图的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

在笔记本中组合jupyter / ipython内核

如何在 jupyter/ipython 笔记本中启用 MathJax Upgreek?

在 jupyter/iPython notebook 脚本和类方法之间同步代码

Jupyter (IPython) Notebook 单元格中有多个 Audio 对象

如何将 python3 内核添加到 jupyter (IPython)

window下安装jupyter,ipython的方法