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.
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.
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:
- we need to maintain some form of internal state: the application needs to remember the x and y locations of the random variates
- we need different behaviour to run based on what slider was triggered.
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
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.