Ipython: %matplotlib inline 不能很好地与 ipywidgets 6.0 交互

创建于 2017-03-03  ·  46评论  ·  资料来源: ipython/ipython

似乎在 jupyter notebook 中, %matplotlib inline不再与新的 ipywidgets 6.0 @interact搭配使用。 据我所知,内联后端会等到单元完全完成后再发送带有图像的 display_data 消息(请参阅在 https://github.com/ipython/ipython/blob/7cde22957303ab53df8bd464ad5d7ed616197f31/ 注册的执行后挂钩) IPython/core/pylabtools.py#L383)。 在 ipywidgets 6.0 中,我们更改了交互的工作方式,使其更具体地了解我们捕获的输出 - 我们捕获在函数执行期间发送的输出消息,并在交互式输出中显示这些消息。 但是,由于 matplotlib 内联后端在函数执行后发送其图像,因此我们只会在单元格的输出区域中获得大量图像。

import matplotlib.pyplot as plt
from ipywidgets import interact

%matplotlib inline
x = [1,2,5,4,3]
@interact(n=(2,5))
def f(n):
    plt.plot(x[:n])

这个问题是关于开始讨论,看看我们是否有办法提出一个很好的解决方案,它可以很好地与内联后端(很有用)一起工作,并且也可以与 ipywidgets 交互很好地工作。

例如,如果有一种方法可以告诉内联后端立即刷新其图像,而不是在单元格执行完毕后刷新,这可能就足够了。 例如,此代码有效(启用内联后端)

import matplotlib.pyplot as plt
from ipywidgets import interact

x = [1,2,5,4,3]
@interact(n=(2,5))
def f(n):
    plt.plot(x[:n])
    plt.show()

抄送@SylvainCorlay ,@tacaswell。

最有用的评论

大家好,我没有关注这个的技术细节,但遇到了这个问题。 不管实现细节如何,我认为 matplotlib+interact 尽可能地“正常工作”是非常重要的。

所有46条评论

似乎有效,但有没有人有更好的解决方案?

import matplotlib.pyplot as plt
from ipywidgets import interact
from ipykernel.pylab.backend_inline import flush_figures

%matplotlib inline
x = [1,2,5,4,3]
@interact(n=(2,5))
def f(n):
    plt.plot(x[:n])
    flush_figures()

CC @minrk ,负责内联 mpl 后端。

我认为我们无能为力。

您将使用什么启发式方法来显示图形? 您根本无法更新图形,因为有很多代码重用图形,但是有动画是很常见的。

每次操作后要冲洗的特殊外壳@interact

每次操作后要刷新的特殊外壳@interact

好主意,谢谢你的建议。 这样做感觉不是很干净(对于特殊情况,matplotlib 的一个特定后端)。 也许我会等着看看用户是否像上面的例子那样显式刷新输出是一个大问题。 我的意思是,显式优于隐式,所有这些......

编辑:总而言之,我想我同意我们可以(或应该)做的事情不多......

事实证明,这比我想象的要容易得多 - 我之前尝试测试时一定做错了什么。 我们不需要调用flush_figures,调用标准的plt.show就够了:

import matplotlib.pyplot as plt
from ipywidgets import interact

%matplotlib inline
x = [1,2,5,4,3]
@interact(n=(2,5))
def f(n):
    plt.plot(x[:n])
    plt.show()

我认为要求人们在他们的交互中插入一个 show() 命令是完全合理的,所以我要结束这个了。

如果我们想保持这种行为(和其他潜在的执行后输出),也许可以在输出捕获完成之前在交互中显式调用 post-X 钩子? 一般来说,我对通过交互调用函数的期望是它的行为与我在单元格中调用过一次函数完全相同。

我希望人们会对所有与 matplotlib 交互的示例停止显示数字感到惊讶,因为“交互式”matplotlib 通常意味着 plt.show 是不必要的。 将它放在交互式功能中可能不应该改变这一点。

这让我想起在 IOThread 被引入之前需要sys.stdout.flush()来查看输出。 当您考虑事情如何运作时,为什么需要它是有道理的,但这并没有让它变得不那么令人惊讶,因为它的行为不像人们期望的那样。

我不认为我们应该自动执行所有的执行后处理程序(谁知道他们会做什么——也许很多东西!)。 有一个flush_display钩子怎么样? 我们会在开始交互之前刷新它,并在结束之前再次刷新它。 这样它就类似于刷新标准输出/标准输入。

好吧,或者另一方面,如果我们认为交互真的像一个单独的单元格一样运行,那么调用 post-execute 可能确实有意义。 但是我们可能想在运行交互之前和之后调用 post-execute,以防交互与其他东西在一个单元格中。

重新打开,因为对话尚未关闭...

我喜欢flush_display钩子的想法,理论上它可以工作,尽管它需要协调发布ipython、ipykernel和ipywidgets来定义一个新钩子并将后端切换到使用它。

如果我们认为交互真的像一个单独的细胞一样

我认为这是他们的关键,这是一个很大的“如果”。 这是我的想法,但我不是每个人(还)。

如果我们认为交互真的像一个单独的细胞一样
我认为这是他们的关键,这是一个很大的“如果”。 这是我的想法,但我不是每个人(还)。

关于这一点的一个令人困惑的事情是,如果我们有一个带有代码的单元格,然后进行交互运行,然后再运行一些代码,则单元格实际上会像三个单元格一样执行(即,执行后运行三个不同的时间)。 我认为在这种情况下 flush_display 更有意义。

附加问题:JLab 中的镜像输出会起作用吗?

@willingc小部件使用镜像输出。

@SylvainCorlay我知道他们现在工作,安娜堡的人们喜欢它。 我的问题是,如果按照上面讨论的那样进行更改,它会起作用。

对噪音感到抱歉。

从长远来看,我认为应该更好地工作的方法是将 ipympl 小部件后端到 matplotlib。

但是,我们需要使用 conf.d 等更轻松地安装所有这些...

是的,镜像输出应该与上面讨论的更改一起使用。 谢谢你的肯定!

关于这一点的一个令人困惑的事情是,如果我们有一个带有代码的单元格,然后进行交互运行,然后再运行一些代码,则单元格实际上会像三个单元格一样执行(即,执行后运行三个不同的时间)。

确实,那会有点奇怪。 不过不会是两次吧? 一个用于整个单元格,另一个用于第一次调用交互? 根据您的想法,这也是有道理的:对于@interact ,您有两次执行:一次用于声明,另一次用于第一次调用交互函数。

例如,如果您使用@interact_manual ,我认为您不会有多个(我不确定)。

不过不会是两次吧? 一个用于整个单元格,另一个用于第一次调用交互?

考虑这个代码:

plt.plot([1,2,3])
@interact(n=(1,10))
def f(n):
    plt.plot([1,n])
plt.plot([4,5,6])

由于绘图是累积的,并且您希望将交互视为一个单独的单元格,因此您需要在交互运行之前刷新图形,然后在交互内部刷新图形,然后在单元格的末尾再次刷新。 这也正是我们对刷新标准输出所做的。

实际上,我不确定用户在上面的示例中期望什么。 你会期待什么?

只是一个想法:上面的例子不是很合乎逻辑; 就我个人而言,我希望三个情节中只有一个动态变化,但如果我要做类似的事情,我会在@interact进行所有情节调用。

是否有任何理由不在每个.plot()之后调用flush_figures .plot()

@myyc这是行为的重大语义变化。 plot在情节上添加项目....

我的意思是在特定的ipython内联行为中,而不是 matplotlib。

编辑:我确信每个单元格有一个交互作为“唯一的绘图功能”是一个更常见的用例,而不是上面描述的情况......

我明白。 虽然我不认为这应该有如此不同的行为。

未来的正确方法可能是使用 notebook 后端或 ipympl 并编辑图形。

在 ipywidgets 的交互中为内联后端实现特定于 matplotlib 的解决方法似乎不正确。

在杰森给出的代码中:

plt.plot([1,2,3])
@interact(n=(1,10))
def f(n):
    plt.plot([1,n])
plt.plot([4,5,6])

预期的输出是一个有很多行的数字..

是的,我完全同意。 我在本地设置中临时以这种方式修复了它(覆盖熊猫绘图方法),“等待修复”:)无视这一点,它引入了一堆可怕的副作用。

我已经看到这个问题在各种 github 项目中被反弹,所以我想知道最好的解决路径应该是什么。

编辑:那么为什么不这样写,更明确的方式呢?

@interact(n=(1,10))
def f(n):
    plt.plot([1,2,3])
    plt.plot([1,n])
    plt.plot([4,5,6])

您可以查看 ipympl 包 (https://github.com/matplotlib/jupyter-matplotlib),它是 matplotlib 小部件。 ipympl 仍处于早期阶段,但应该有比 matplotlib 核心更快的发布时间表,以及对 jupyterlab 等的早期支持......

好吧,事实证明覆盖是一个糟糕的主意。 我期待上游的修复:)

只是为了贡献一个未受过教育的用户观点:更新到 ipywidgets 6.0 后,我发现我的大部分小部件代码不起作用(由于这里讨论的问题)。 我能够通过向plt.show()添加调用来修复它,但我也发现我必须非常小心添加此类调用的位置; 似乎有时过早调用它会导致后面的图不会出现(即使再次调用plt.show() )。 我还没有找到足够的方法来创建一个最小的例子,但当前的行为对我来说似乎不直观——我正在通过反复试验来编程来解决它。

ipywidgets 的哪些确切变化实际上导致了新行为? 执行后挂钩是否根本没有被调用,或者它们仍然被调用但它们的输出现在被丢弃,因为小部件特别关注小部件拥有哪些输出? 如果是后者,那么在函数体期间隔离捕获的输出似乎不是正确的做法。

正在调用执行后挂钩(我很确定)-您可以通过以下方式看到这一点:

import matplotlib.pyplot as plt
from ipywidgets import interact
%matplotlib inline

@interact(n=(0,10))
def f(n):
    plt.plot([0,n])

并移动滑块。 绘图将附加到单元格 - 执行后挂钩正在发送显示数据消息。 交互执行确实特别关注仅捕获函数调用期间发生的输出。 我认为这是正确的做法。 例如,两个不同的交互可能共享完全相同的滑块实例:

import matplotlib.pyplot as plt
from ipywidgets import interact, IntSlider
%matplotlib inline

x = IntSlider()
@interact(n=x)
def f(n):
    plt.plot([0,n])
    plt.show()
@interact(n=x)
def f(n):
    plt.plot([0,n,0,n])
    plt.show()

当调整单个滑块(在任一 UI 中)时,两个交互都会显示滑块移动,并且两个交互函数将在同一个执行调用中运行。 两个交互都试图从执行后挂钩捕获任何输出是错误的。 我认为这里的缺陷是我们使用后执行来生成输出,这太粗糙了。 我们应该有一个更细粒度的方法来刷新在代码块中生成的输出,它可能在一次执行中被多次调用。

我们应该有一个更细粒度的方法来刷新代码块中生成的输出

对于 matplotlib 绘图,执行plt.show时会发生这种刷新

我必须非常小心在何处添加此类调用; 似乎有时过早地调用它会导致后面的情节不出现

真的很想看到发生这种情况的例子。 我们希望使行为直观且易于理解。

我必须非常小心在何处添加此类调用; 似乎有时过早地调用它会导致后面的情节不出现

我真的很想看到发生这种情况的例子。 我们希望使行为直观且易于理解。

详细说明,我认为您应该能够通过将plt.show()为任何进行绘图的交互函数的最后一行来获得与先前行为等效的行为。 我很想看到这不起作用的例子。

@jasongrout感谢您查看此内容。 我遇到这个问题的代码相当复杂,而且我可能做错了其他事情。 如果我再次看到它,我会尝试隔离它并提供一个示例。

@jasongrout经过更多的挖掘,我认为我遇到的是 Jupyter 笔记本本身的属性,而不是小部件的属性(但它现在与小部件交互,因为我们被迫调用plt.show() )。

如果在笔记本单元格中创建绘图并调用plt.show() ,则不会显示稍后对该图的修改。 这是一个最小的例子:

%matplotlib inline
import matplotlib.pyplot as plt

def plotfun(t):
    fig, ax = plt.subplots(1,1)
    ax.plot([0,t],[0,t])
    plt.show()
    ax.plot([t,0],[0,t])
    plt.show()

plotfun(2)

仅出现第一条线图。

这是一个更简单的例子。 两个单元格,没有%matplotlib inline后端。 也许@tacaswell可以说明为什么第二个plt.show没有显示情节。

import matplotlib.pyplot as plt
fig, ax = plt.subplots(1,1)
ax.plot([0,1],[0,1])
plt.show()

显示情节

ax.plot([1,0],[0,1])
plt.show()

不显示任何东西

在我的机器上使用最新的稳定版 jupyter、ipython、ipywidgets、matplotlib、python 3 进行了确认。

这里也确认了。

@jasongrout这是预期的行为,因为在第一个单元格中您创建了一个图形(以及画布和轴)然后显示它。 然后在第二个单元格中向现有轴(在现有图中)添加一条线。 使用%matplotlib qtipympl (或任何完整的 GUI),情节_应该_用第二行更新。 我不希望这能与内联一起使用,因为你只有一次更新它的机会(这就是为什么我非常不喜欢内联,它抛出了_所有_的交互功能)。

@tacaswell这种内联行为是否也适用于我之前给出的一个单元格中的示例? 又来了:

%matplotlib inline
import matplotlib.pyplot as plt

def plotfun(t):
    fig, ax = plt.subplots(1,1)
    ax.plot([0,t],[0,t])
    plt.show()
    ax.plot([t,0],[0,t])
    plt.show()

plotfun(2)

我认为在plt.show ,内联后端将其自身呈现为一个 png,然后从 pyplot 中取消注册该图,因此它不会在下一个节目中再次呈现,即使它在同一个单元格中,但是对没有额外show的轴方法的多次调用应该可以工作。

为了清楚起见,这里是内联后端代码: https :

是否有ipywidgets和/或matplotlib我可以回滚以便让我的交互式小部件再次工作的版本?

@jasongrout从代码看来,我可能想将InlineBackend.close_figuresFalse 。 但我不确定如何设置该属性——我需要使用某种魔法吗?

(这个问题听起来很有趣,但我的意思是字面意思)

https://github.com/jupyter-widgets/ipywidgets/issues/1457再次提出了这个问题。 @ellisonbg - 如果我们实施我上面提到的flush_figures 解决方法会怎样 - 在交互运行之前和之后调用flush_figures,在交互之前清除任何绘图,并清除交互期间绘制的任何内容? 我想我们可能需要检查一下我们是否在内联 mpl 后端运行。

这是一个巨大的麻烦,但也许无论如何它都足够重要。 也许我们还可以检测到我们是否真的在刷新图形并发出警告,要求人们进行明确的 show() 调用?

大家好,我没有关注这个的技术细节,但遇到了这个问题。 不管实现细节如何,我认为 matplotlib+interact 尽可能地“正常工作”是非常重要的。

使用plt.show()flush_figures() ,如果刷新笔记本(不重新启动内核),即使使用带小部件的保存笔记本,数字也会消失。 如果没有这些缓解措施,情况并非如此,尽管仍然存在多个绘图输出的问题。 这是故意的吗? 如果内核关闭,同样适用,而对于旧版本的 ipywidgets,图形的最终状态将保存到笔记本中,类似于非交互式绘图,这非常方便。

对不起,如果这超出了范围,或者我做错了什么。 运行 ipywidgets 6.0.0、matplotlib 2.0.2、notebook 5.0.0 和 python 3.6.1。

此页面是否有帮助?
0 / 5 - 0 等级