<p><code>interactive</code>函数并不能真正让您访问这个粒度级别。它总是运行整个<code>scatterplt</code>回调。基本上,<code>interactive</code>的目的是让一类问题变得非常简单一旦你离开了这类问题,它就不再适用了。在</p>
<p>然后你必须回到其他的小部件机器。这在最初可能有点难以理解,因此,为了最小化跳跃,我将首先解释一下<code>interactive</code>在引擎盖下的作用。在</p>
<p>当您调用<code>interactive(func, widget)</code>时,它将创建<code>widget</code>,并在{<cd6>}更改时绑定回调。回调在<code>Output</code>小部件(<a href="https://ipywidgets.readthedocs.io/en/stable/examples/Output%20Widget.html" rel="nofollow noreferrer">docs</a>)中运行<code>func</code>。<code>Output</code>小部件捕获<code>func</code>的全部输出。<code>interactive</code>然后将<code>widget</code>和输出小部件打包到<code>VBox</code>(一个用于堆叠小部件的容器)。在</p>
<p>回到你现在想做的事情上。您的申请有以下条件:</p>
<ol>
<li>我们需要保持某种形式的内部状态:应用程序需要记住随机变量的x和y位置</li>
<li>我们需要不同的行为来运行基于什么滑块被触发。在</li>
</ol>
<p>为了满足(1),我们可能应该创建一个类来维护状态。为了满足(2),我们需要根据调用的滑块运行不同的回调。在</p>
<p>像这样的东西似乎能满足你的需要:</p>
<pre><code>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
</code></pre>
<p>最后一点,移动滑块时,动画仍有点刺耳。为了更好的交互性,我建议查看<a href="https://github.com/bloomberg/bqplot" rel="nofollow noreferrer">bqplot</a>。特别是,Chakri Cherukuri有一个很好的<a href="https://github.com/ChakriCherukuri/mlviz/blob/master/Linear%20Regression.ipynb" rel="nofollow noreferrer">example of linear regression</a>,这与您正在尝试做的有点相似。在</p>