代码之家  ›  专栏  ›  技术社区  ›  Axle Max

在Jupyter-Python中有两个ipywidget在一个matplotlib绘图上执行操作

  •  1
  • Axle Max  · 技术社区  · 7 年前

    下面的代码模拟了一个机器学习、线性回归过程。

    函数的第一部分(x,y)生成一个图来执行回归。

    import numpy as np
    import ipywidgets as widgets
    from ipywidgets import interactive
    import matplotlib.pyplot as plt    
    
    def scatterplt(rand=3, num_points=20, slope=1):
    
        x = np.linspace(3, 9, num_points)
        y = np.linspace(3, 9, num_points)
    
        #add randomness to scatter
        pcent_rand = rand
        pcent_decimal = pcent_rand/100
        x = [n*np.random.uniform(low=1-pcent_decimal, high=1+ pcent_decimal) for n in x]
        y = [n*np.random.uniform(low=1-pcent_decimal, high=1+ pcent_decimal) for n in y]
    
        #plot regression line
        a = np.linspace(0, 9, num_points)
        b = [(slope * n) for n in a]
    
        #format & plot the figure
        plt.figure(figsize=(9, 9), dpi=80)
        plt.ylim(ymax=max(x)+1)
        plt.xlim(xmax=max(x)+1)
    
        plt.scatter(x, y)
    
        plt.plot(a, b)
    
        plt.show()
    
    
    #WIDGETS    
    
    interactive_plot = interactive(scatterplt, 
                     rand = widgets.FloatSlider(
                                    value=3,
                                    min=0,
                                    max=50,
                                    step=3,
                                    description='Randomness:', num_points=(10, 50, 5)
                                    ),
                     num_points = widgets.IntSlider(
                                    value=20,
                                    min=10,
                                    max=50,
                                    step=5,
                                    description='Number of points:'
                                    ),
                     slope=widgets.FloatSlider(
                                    value=1,
                                    min=-1,
                                    max=5,
                                    step=0.1,
                                    description='Slope'
                                    )
    
                    )
    
    interactive_plot
    
    3 回复  |  直到 7 年前
        1
  •  2
  •   Pascal Bugnion    7 年前

    这个 interactive 函数并不能真正让您访问这个粒度级别。它总是运行整个过程 scatterplt 回拨。基本上 交互式 就是让一类问题变得非常简单——一旦你摆脱了这类问题,它就不适用了。

    交互式

    当你打电话的时候 interactive(func, widget) widget 并将回调绑定到 小装置 变化。回调将运行 func Output 小工具( docs 输出 小部件捕获 功能 . 然后打包 将输出小部件 VBox

    回到你现在想做的事情。您的应用程序具有以下条件:

    1. 我们需要保持某种形式的内部状态:应用程序需要记住随机变量的x和y位置

    为了满足(1),我们可能应该创建一个类来维护状态。为了满足(2),我们需要根据slider的调用运行不同的回调。

    像这样的东西似乎可以满足你的需要:

    import numpy as np
    import ipywidgets as widgets
    import matplotlib.pyplot as plt
    
    class LinRegressDisplay:
    
        def __init__(self, rand=3.0, num_points=20, slope=1.0):
            self.rand = rand
            self.num_points = num_points
            self.slope = slope
            self.output_widget = widgets.Output()  # will contain the plot
            self.container = widgets.VBox()  # Contains the whole app
            self.redraw_whole_plot()
            self.draw_app()
    
        def draw_app(self):
            """
            Draw the sliders and the output widget
    
            This just runs once at app startup.
            """
            self.num_points_slider = widgets.IntSlider(
                value=self.num_points,
                min=10,
                max=50,
                step=5,
                description='Number of points:'
            )
            self.num_points_slider.observe(self._on_num_points_change, ['value'])
            self.slope_slider = widgets.FloatSlider(
                value=self.slope,
                min=-1,
                max=5,
                step=0.1,
                description='Slope:'
            )
            self.slope_slider.observe(self._on_slope_change, ['value'])
            self.rand_slider = widgets.FloatSlider(
                value=self.rand,
                min=0,
                max=50,
                step=3,
                description='Randomness:', num_points=(10, 50, 5)
            )
            self.rand_slider.observe(self._on_rand_change, ['value'])
            self.container.children = [
                self.num_points_slider,
                self.slope_slider,
                self.rand_slider ,
                self.output_widget
            ]
    
        def _on_num_points_change(self, _):
            """
            Called whenever the number of points slider changes.
    
            Updates the internal state, recomputes the random x and y and redraws the plot.
            """
            self.num_points = self.num_points_slider.value
            self.redraw_whole_plot()
    
        def _on_slope_change(self, _):
            """
            Called whenever the slope slider changes.
    
            Updates the internal state, recomputes the slope and redraws the plot.
            """
            self.slope = self.slope_slider.value
            self.redraw_slope()
    
        def _on_rand_change(self, _):
            self.rand = self.rand_slider.value
            self.redraw_whole_plot()
    
        def redraw_whole_plot(self):
            """
            Recompute x and y random variates and redraw whole plot
    
            Called whenever the number of points or the randomness changes.
            """
            pcent_rand = self.rand
            pcent_decimal = pcent_rand/100
            self.x = [
                n*np.random.uniform(low=1-pcent_decimal, high=1+pcent_decimal) 
                for n in np.linspace(3, 9, self.num_points)
            ]
            self.y = [
                n*np.random.uniform(low=1-pcent_decimal, high=1+pcent_decimal)
                for n in np.linspace(3, 9, self.num_points)
            ]
            self.redraw_slope()
    
        def redraw_slope(self):
            """
            Recompute slope line and redraw whole plot
    
            Called whenever the slope changes.
            """
            a = np.linspace(0, 9, self.num_points)
            b = [(self.slope * n) for n in a]
    
            self.output_widget.clear_output(wait=True)
            with self.output_widget as f:
                plt.figure(figsize=(9, 9), dpi=80)
                plt.ylim(ymax=max(self.y)+1)
                plt.xlim(xmax=max(self.x)+1)
    
                plt.scatter(self.x, self.y)
                plt.plot(a, b)
                plt.show()
    
    app = LinRegressDisplay()
    app.container  # actually display the widget
    

    bqplot . 特别是,查克里切鲁库里有一个伟大的 example of linear regression

        2
  •  2
  •   Maarten Breddels    7 年前

    而不是使用 interactive interact 你也可以使用 interact_manual (见 the docs 更多信息)。 你得到的是一个按钮,让你手动运行功能,一旦你高兴。

    你需要这两条线

    from ipywidgets import interactive, interact_manual
    interactive_plot = interact_manual(scatterplt,
    ...
    

    enter image description here

    单击按钮后,将显示完整输出: enter image description here

        3
  •  2
  •   DougR    7 年前

    部分问题是很难修改Matplotlib图中的单个元素,即从头开始重绘整个图要容易得多。重新绘制整个图形不会太快或太平滑。因此,我将向您展示一个如何在BQplot中执行此操作的示例(如Pascal bugnon所建议的)。它不是Matplotlib,我猜你可能想要,但它确实演示了一种方法,分离斜率和随机性指令和计算从每个单独的滑块,同时仍然使用标准的交互式小部件。

    enter image description here

    import bqplot as bq
    import numpy as np
    import ipywidgets as widgets
    
    
    def calcSlope(num_points, slope):
        a = np.linspace(0, 9, num_points)
        b = a * slope
    
        line1.x = a
        line1.y = b
    
    
    def calcXY(num_points, randNum):
        x = np.linspace(3, 9, num_points)
        y = x
    
        #add randomness to scatter
        x = np.random.uniform(low=1-randNum/100, high=1+ randNum/100, size=(len(x))) * x
        y = np.random.uniform(low=1-randNum/100, high=1+ randNum/100, size=(len(y))) * y
    
        #format & plot the figure
        x_sc.min = x.min()
        x_sc.max = x.max() + 1
    
        scat.x = x
        scat.y = y        
    
    
    
    def rand_int(rand):
        calcXY(num_i.children[0].value, rand)
    
    def num_points_int(num_points):
        calcXY(num_points, rand_i.children[0].value)
        calcSlope(num_points, slope_i.children[0].value)
    
    def slope_int(slope):
        calcSlope(num_i.children[0].value, slope)
    
    
    
    rand_i = widgets.interactive(rand_int, 
                     rand = widgets.FloatSlider(
                                    value=3,
                                    min=0,
                                    max=50,
                                    step=3,
                                    description='Randomness:', num_points=(10, 50, 5)
                                    )
                                  )
    
    
    num_i = widgets.interactive(num_points_int, 
                     num_points = widgets.IntSlider(
                                    value=20,
                                    min=10,
                                    max=50,
                                    step=5,
                                    description='Number of points:'
                                    )
                                  )
    
    
    slope_i = widgets.interactive(slope_int, 
                     slope=widgets.FloatSlider(
                                    value=1,
                                    min=-1,
                                    max=5,
                                    step=0.1,
                                    description='Slope'
                                    )
                                  )
    
    
    # Create the initial bqplot figure
    x_sc = bq.LinearScale()
    ax_x = bq.Axis(label='X', scale=x_sc, grid_lines='solid', tick_format='0f')
    ax_y = bq.Axis(label='Y', scale=x_sc, orientation='vertical', tick_format='0.2f')
    
    line1 = bq.Lines( scales={'x': x_sc, 'y': x_sc} , colors=['blue'],display_legend = False, labels=['y1'],stroke_width = 1.0)
    scat = bq.Scatter(scales={'x': x_sc, 'y': x_sc} , colors=['red'],display_legend = False, labels=['y1'],stroke_width = 1.0)
    
    
    calcSlope(num_i.children[0].value, slope_i.children[0].value)
    calcXY(num_i.children[0].value, rand_i.children[0].value)
    
    m_fig = dict(left=100, top=50, bottom=50, right=100)
    fig = bq.Figure(axes=[ax_x, ax_y], marks=[line1,scat], fig_margin=m_fig, animation_duration = 1000)
    
    widgets.VBox([rand_i,num_i,slope_i,fig])