【问题标题】:IPython Notebook widgets for Matplotlib interactivity用于 Matplotlib 交互性的 IPython Notebook 小部件
【发布时间】:2014-09-17 15:49:05
【问题描述】:

我想使用 ipython notebook 小部件为内联 matplotlib 图添加一定程度的交互性。

一般来说,情节可能很繁重,我只想更新情节的特定元素。我知道小部件具有内置的节流功能,有助于不淹没内核,但是当情节需要 30 秒时,我不想等待这么长时间来更新一行。

通过阅读example notebooks,我能够创建一个基本示例,其中我向 mpl 轴添加了一个十字光标(由 2 个滑块驱动)。

问题是图形显示了两次。这是代码(单元格 1):

fig, ax = plt.subplots() 
ax.plot([3,1,2,4,0,5,3,2,0,2,4])

...显示的图...,单元格 2(编辑:感谢 Thomas K 的改进):

vline = ax.axvline(1)
hline = ax.axhline(0.5)

def set_cursor(x, y):
    vline.set_xdata((x, x))
    hline.set_ydata((y, y))
    display(fig)

最后(单元格 3):

interact(set_cursor, x=(1, 9, 0.01), y=(0, 5, 0.01))

再次显示带有小部件的图形。

所以问题是:

  1. 如何禁止第一个数字显示?
  2. 这是正确的方法还是有更好的方法?

编辑

我发现了一个 ipython 配置旋钮,根据this notebook,它允许禁止图形显示

%config InlineBackend.close_figures = False

虽然示例笔记本有效,但我不知道如何单独使用此选项(没有链接示例中提供的上下文管理器类)来隐藏图形显示。

编辑 2

我发现some documentation 中的InlineBackend.close_figures 是可配置的。

编辑 3

由@shadanan 回答触发,我想澄清一下,我的目的是将光标添加到现有图形中,而不是在每次光标移动时从头开始重新绘制绘图。将 3 个单元格合并为一个单元格:

fig, ax = plt.subplots()
ax.plot([3,1,2,4,0,5,3,2,0,2,4])

vline = ax.axvline(1)
hline = ax.axhline(0.5)

def set_cursor(x, y):
    vline.set_xdata((x, x))
    hline.set_ydata((y, y))
    display(fig)

interact(set_cursor, x=(1, 9, 0.01), y=(0, 5, 0.01))

它“应该”工作,但它没有。第一次执行单元格时,它显示 2 个数字。小部件交互后,仅显示 1 个图形。这是“奇怪的行为”,需要像@shadanan 答案中所示的解决方法。 ipython 开发人员可以对此发表评论吗?它是一个错误吗?

【问题讨论】:

  • 对于interact,你应该做一个同时接受两个参数的函数,并且只调用一次display。
  • 已更正感谢@ThomasK。我还是有抑制第一个数字显示的问题...

标签: python matplotlib ipython ipython-notebook


【解决方案1】:

解决方案非常简单。为了避免显示第一个图,我们只需要在interact 调用之前添加一个close() 调用。

回顾问题的例子,像这样的单元格将正确显示一个交互式图形(而不是两个):

fig, ax = plt.subplots()
ax.plot([3,1,2,4,0,5,3,2,0,2,4])
plt.close(fig)

vline = ax.axvline(1)
hline = ax.axhline(0.5)

def set_cursor(x, y):
    vline.set_xdata((x, x))
    hline.set_ydata((y, y))
    display(fig)

interact(set_cursor, x=(1, 9, 0.01), y=(0, 5, 0.01))

更简洁的方法是定义函数add_cursor(在单独的单元格或脚本中):

def add_cursor(fig, ax):
    plt.close(fig)

    vline = ax.axvline(1, color='k')
    hline = ax.axhline(0.5, color='k')

    def set_cursor(x, y):
        vline.set_xdata((x, x))
        hline.set_ydata((y, y))
        display(fig)

    interact(set_cursor, x=ax.get_xlim(), y=ax.get_ylim())

然后在我们想要添加交互式光标时调用它:

fig, ax = plt.subplots()
ax.plot([3,1,2,4,0,5,3,2,0,2,4])
add_cursor(fig, ax)

【讨论】:

    【解决方案2】:

    我有一个只显示一个数字的 hacky 解决方法。问题似乎是代码中有两个点会生成一个图形,实际上,我们只想要第二个点,但我们无法阻止第一个点。解决方法是将第一个用于第一次执行,将第二个用于所有后续执行。下面是一些根据初始化标志在两者之间切换的代码:

    %matplotlib inline
    import matplotlib.pyplot as plt
    from IPython.html.widgets import interact, interactive, fixed
    from IPython.html import widgets
    from IPython.display import clear_output, display, HTML
    
    class InteractiveCursor(object):
        initialized = False
        fig = None
        ax = None
        vline = None
        hline = None
    
        def initialize(self):
            self.fig, self.ax = plt.subplots()
            self.ax.plot([3,1,2,4,0,5,3,2,0,2,4])
            self.vline = self.ax.axvline(1)
            self.hline = self.ax.axhline(0.5)
    
        def set_cursor(self, x, y):
            if not self.initialized:
                self.initialize()
    
            self.vline.set_xdata((x, x))
            self.hline.set_ydata((y, y))
    
            if self.initialized:
                display(self.fig)
            self.initialized = True
    
    ic = InteractiveCursor()
    def set_cursor(x, y):
        ic.set_cursor(x, y)
    
    interact(set_cursor, x=(1, 9, 0.01), y=(0, 5, 0.01));
    

    我的意见是这应该被认为是一个错误。我用面向对象的接口试过了,也有同样的问题。

    【讨论】:

    • 谢谢。你是对的,我编辑了我的问题并添加了你的评论。您的解决方案的问题是您需要将绘图嵌入到类中。您不能(轻松)修改现有绘图。
    【解决方案3】:

    您可以使用新的(ish)notebook 后端以非常严格的方式执行此操作

    %matplotlib notebook
    import matplotlib.pyplot as plt
    from IPython.html.widgets import interactive
    
    fig, ax = plt.subplots()
    ax.plot(range(5))
    
    
    vline = ax.axvline(1, color='k')
    hline = ax.axhline(0.5, color='k')
    
    def set_cursor(x, y):
        vline.set_xdata((x, x))
        hline.set_ydata((y, y))
        ax.figure.canvas.draw_idle()
    

    在一个单独的单元格中:

    interactive(set_cursor, x=ax.get_xlim(), y=ax.get_ylim())
    

    这仍然会在您每次移动光标时重新绘制整个图形,因为 notebook 当前不支持 blitting(正在处理 https://github.com/matplotlib/matplotlib/pull/4290

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-11-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-18
      • 1970-01-01
      • 2018-03-10
      • 2017-11-03
      相关资源
      最近更新 更多