在Jupyter中有2个Ipywidget作用于一个matplotlib图上-Python [英] Have 2 Ipywidgets acting on one matplotlib plot in Jupyter - Python

查看:104
本文介绍了在Jupyter中有2个Ipywidget作用于一个matplotlib图上-Python的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下代码模拟了机器学习的线性回归过程.

The following code simulates a machine learning, linear regression process.

它旨在允许用户在Jupyter笔记本中手动和直观地进行回归,以更好地了解线性回归过程.

It is meant to allow the user to do the regression manually and visually in a Jupyter notebook to get a better feel for the linear regression process.

该函数的第一部分(x,y)生成一个图以对其执行回归.

The first section (x,y) of the function generates a plot to perform the regression on.

下一部分(a,b)为模拟回归生成要玩的行.

The next section (a,b) generates the line to play with, for the simulated regression.

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

I want to be able to change the slope slider without the scatter plot being regenerated.

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

Any guidance will be very helpful and welcome. :-)

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的要点是使一类问题真正变得很容易-一旦您摆脱了该类问题,那实际上就不适用了.

The interactive function does not really give you access to this level of granularity. It always runs the entire scatterplt callback. Basically, the point of interactive is to make a class of problems really easy -- once you move out of that class of problems, it's not really applicable.

然后,您必须使用其余的窗口小部件机制.最初可能很难理解这一点,因此,为了最大程度地减少跳跃,我将首先说明interactive的作用.

You then have to fall back to the rest of the widget machinery. This can be a bit hard to understand initially, so, to minimize the jump, I'll start by explaining what interactive does under the hood.

调用interactive(func, widget)时,它将创建widget并将绑定的回调绑定到widget发生变化的任何时间.回调在Output小部件(文档). Output小部件捕获func的整个输出. interactive然后将widget和输出窗口小部件打包到VBox(用于堆叠窗口小部件的容器)中.

When you call interactive(func, widget), it creates widget and binds a callback to whenever that widget changes. The callback runs func in an Output widget (docs). The Output widget captures the entire output of func. interactive then packs widget and the output widget into a VBox (a container for stacking widgets).

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

Back to what you want to do now. Your application has the following criteria:

  1. 我们需要保持某种形式的内部状态:应用程序需要记住随机变量的x和y位置
  2. 我们需要根据触发了什么滑块来运行不同的行为.

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

To satisfy (1), we should probably create a class to maintain the state. To satisfy (2), we need different callbacks to run based on what slider was called.

类似的事情似乎可以满足您的需求:

Something like this seems to do what you need:

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 .特别是Chakri Cherukuri有一个很棒的线性回归示例这与您尝试执行的操作有些相似.

As a final note, the animation remains a bit jarring when you move the sliders. For better interactivity, I suggest looking at bqplot. In particular, Chakri Cherukuri has a great example of linear regression that is somewhat similar to what you are trying to do.

这篇关于在Jupyter中有2个Ipywidget作用于一个matplotlib图上-Python的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆