Skip to main content
 首页 » 编程设计

python之在 Jupyter之Python 中有 2 个 Ipywidgets 作用于一个 matplotlib 图

2025年05月04日22落叶

以下代码模拟机器学习、线性回归过程。

它旨在允许用户在 Jupyter notebook 中手动和可视化地进行回归,以更好地感受线性回归过程。

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

下一部分 (a,b) 生成用于模拟回归的直线。

我希望能够在不重新生成散点图的情况下更改斜率 slider 。

任何指导都将非常有帮助和受欢迎。 :-)

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 

请您参考如下方法:

interactive 功能并不能真正让您访问这种级别的粒度。它始终运行整个 scatterplt 回调。基本上,interactive 的目的是让一类问题变得非常简单——一旦你摆脱了那类问题,它就不再适用了。

然后您必须回退到其余的小部件机制。最初这可能有点难以理解,因此,为了尽量减少跳转,我将首先解释 interactive 在幕后做了什么。

当您调用 interactive(func, widget) 时,它会创建 widget 并在该 widget 更改时绑定(bind)回调。回调在 Output 小部件 ( docs ) 中运行 func。 Output 小部件捕获 func 的整个输出。 interactive 然后将 widget 和输出小部件打包到 VBox(用于堆叠小部件的容器)中。

回到你现在想做的事。您的申请符合以下条件:

  1. 我们需要维护某种形式的内部状态:应用程序需要记住随机变量的 x 和 y 位置
  2. 我们需要根据触发的 slider 运行不同的行为。

要满足 (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 

最后一点,当您移动 slider 时,动画仍然有点不和谐。为了更好的交互性,我建议查看 bqplot .特别是,Chakri Cherukuri 有一个很棒的 example of linear regression这与您正在尝试做的有点相似。