在Jupyter中有2个Ipywidget作用于一个matplotlib图上-Python [英] Have 2 Ipywidgets acting on one matplotlib plot in Jupyter - 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:
- 我们需要保持某种形式的内部状态:应用程序需要记住随机变量的x和y位置
- 我们需要根据触发了什么滑块来运行不同的行为.
要满足(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屋!