用于Matplotlib交互的IPython Notebook小部件

时间:2014-07-25 17:49:35

标签: python matplotlib ipython ipython-notebook

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

一般情况下,情节可能很重,我只想更新情节的特定元素。我知道小部件内置的限制功能有助于不会泛滥内核,但是当情节采取30秒时,我不想等待这么长时间来更新一行。

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

问题是图形显示两次。这是代码(单元格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. 是正确的方法还是有更好的方法?
  3. 修改

    根据this notebook,我发现了一个ipython配置旋钮,可以禁止数字显示

    %config InlineBackend.close_figures = False
    

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

    编辑2

    我找到InlineBackend.close_figures可配置的some documentation

    编辑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开发者可以对此发表评论吗?这是一个错误吗?

3 个答案:

答案 0 :(得分:5)

解决方案结果非常简单。为避免显示第一个数字,我们只需在close()来电之前添加interact来电。

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

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)

答案 1 :(得分: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上工作)

答案 2 :(得分: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));

我的意见是,这应该被视为一个错误。我尝试使用面向对象的界面,它也有同样的问题。

相关问题