The following code simulates a machine learning, linear regression process.
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.
The first section (x,y) of the function generates a plot to perform the regression on.
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
The
interactive
function does not really give you access to this level of granularity. It always runs the entirescatterplt
callback. Basically, the point ofinteractive
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 createswidget
and binds a callback to whenever thatwidget
changes. The callback runsfunc
in anOutput
widget (docs). TheOutput
widget captures the entire output offunc
.interactive
then packswidget
and the output widget into aVBox
(a container for stacking widgets).Back to what you want to do now. Your application has the following criteria:
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:
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.
Instead of using
interactive
/interact
you can also useinteract_manual
(see the docs for more info). What you get is a button that lets you manually run the function once you are happy.You need these two lines
The first time you run it, you should see this:
After you click the button, it will show you the full output:
Part of the problem is that it is difficult to modify individual elements in a Matplotlib figure i.e. it is much easier to redraw the whole plot from scratch. Redrawing the whole figure will not be super quick or smooth. So instead, I am showing you an example of how to do it in BQplot (as suggested by Pascal Bugnion). Its not Matplotlib as I guess you probably wanted but it does demonstrate a method of separating the slope and randomness instructions and calculations from each individual slider whilst still using the standard interactive widgets.