IPython Notebook widgets for Matplotlib interactivity

后端 未结 3 699
悲&欢浪女
悲&欢浪女 2021-02-06 00:05

I would like to use the ipython notebook widgets to add some degree of interactivity to inline matplotlib plots.

In general the plot can be quite heavy and I want to on

相关标签:
3条回答
  • 2021-02-06 00:12

    I have a hacky workaround that will only display one figure. The problem seems to be that there are two points in the code that generate a figure and really, we only want the second one, but we can't get away with inhibiting the first. The workaround is to use the first one for the first execution and the second one for all subsequent ones. Here's some code that works by switching between the two depending on the initialized flag:

    %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));
    

    My opinion is that this should be considered a bug. I tried it with the object oriented interface and it has the same problem.

    0 讨论(0)
  • 2021-02-06 00:33

    You can do this in a very strait forward way using the new(ish) notebook backend

    %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()
    

    and in a separate cell:

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

    This will still re-draw the entire figure every time you move the cursor because notebook does not currently support blitting (which is being worked on https://github.com/matplotlib/matplotlib/pull/4290 )

    0 讨论(0)
  • 2021-02-06 00:37

    The solution turns out to be really simple. To avoid showing the first figure we just need to add a close() call before the interact call.

    Recalling the example of the question, a cell like this will correctly show a single interactive figure (instead of two):

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

    A cleaner approach is defining the function add_cursor (in a separate cell or script):

    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())
    

    and then call it whenever we want to add an interactive cursor:

    fig, ax = plt.subplots()
    ax.plot([3,1,2,4,0,5,3,2,0,2,4])
    add_cursor(fig, ax)
    
    0 讨论(0)
提交回复
热议问题