In [1]:
%matplotlib ipympl
import matplotlib.pyplot as plt
import numpy as np
from mpl_interactions import interactive_plot, interactive_plot_factory
import ipywidgets as widgets
Interactive line plots¶
Simplest example¶
Here is an example with no extra arguments or styling so that the example looks nice and compact + simple.
In [2]:
x = np.linspace(0,np.pi,100)
τ = np.linspace(1,10, 100)
β = np.linspace(1,10)
def f(x, τ, β):
return np.sin(x*τ)*x**β
fig, ax, sliders = interactive_plot(f, x=x, τ = τ, β = β)
Troubleshooting¶
If instead of a plot you got an output that looks like this:
VBox([IntSlider(min=0, max=10 .....
and you are using jupyterlab
then you probably need to install jupyterlab-manager:
conda install -c conda-forge nodejs=12
jupyter labextension install @jupyter-widgets/jupyterlab-manager
after the install and finishes refresh the browser page and it should work
set parameters with tuples¶
When you use tuples with length of 2 or 3 as a parameter then it will be treated as an argument to linspace. So the below example is equivalent to first example
In [3]:
fig, ax, sliders = interactive_plot(f, x=x, τ = (1, 10, 100), β = (1, 10 ))
Use sets for categorical values¶
sets with three or fewer items will be rendered as checkboxs, while with
more they will use the selection widget. Unfortunately sets are
instrinsically disordered so if you use a set you cannot garuntee the
order of the categoricals. To get around this if you have a set of
single tuple it will be ordered. i.e. {('sin', 'cos')}
will show up
in the order: sin, cos. While {'sin', 'cos'}
will show up in the
order: cos, sin
In [4]:
def f(x, τ, β, type_):
if type_ == 'sin':
return np.sin(x*τ)*x**β
elif type_ =='cos':
return np.cos(x*τ)*x**β
fig, ax, sliders = interactive_plot(f, x=x, τ = (1, 10, 100), β = (1, 10 ), type_ = {('sin', 'cos', 'beep','boop')})
Using widgets for as parameters¶
You can also pass an ipywidgets
widget that has a value
attribute
In [5]:
def f(x, tau, beta, type_):
if type_ == 'sin':
return np.sin(x*tau)*x**beta
elif type_ =='cos':
return np.cos(x*tau)*x**beta
tau = widgets.FloatText(value = 7, step = .1)
fig, ax, sliders = interactive_plot(f, x=x, tau = tau, beta = (1, 10 ), type_ = {('sin', 'cos')})
With multiple functions + showcase automatic legend¶
You can compare multiple functions by providing a list of functions. If you do not explicitly provide a label the name of the function will be used as the label so you can still differentiate them in a legend.
In [6]:
x = np.linspace(0,np.pi,100)
τ = np.linspace(.5, 10, 100)
β = np.linspace(1, 10, 100)
def f1(x, τ, β):
return np.sin(x*τ)*x*β
def f2(x, τ, β):
return np.sin(x*β)*x*τ
fig, ax, sliders = interactive_plot([f1, f2], x=x, τ = τ, β = β)
plt.legend()
Out[6]:
<matplotlib.legend.Legend at 0x7fb6a8492c10>
Styling of plot¶
You can either use the figure and axis objects returned by the function,
or if the figure is the current active figure the standard plt.__
commands should work as expected. You can also provide explict
plot_kwargs to the plt.plot
command that is used internally using
the plot_kwargs argument
You can control how xlim
/ylim
s behave using the
x_scale
/y_scale
arguments. The options are: 1. stretch
-
never shrink the x/y axis but will expand it to fit larger values 2.
auto
- autoscale the x/y axis for every plot update 3. fixed
-
always used the initial values of the limits 4. a tuple - You can pass a
value such as [-4,5]
to have the limits not be updated by moving the
sliders.
Title¶
You can make the title auto update with information about the values by
using the title
argument. Just use the name of one of the parameters
as in a format specifier in the string. e.g. to put the value of τ
in and round it to two decimals use the following title string:
{'τ:.2f}'
In [7]:
x = np.linspace(0,np.pi,100)
τ = np.linspace(1,10,100)
def f(τ):
return [np.sin(x*τ)*x**τ, x*τ]
interactive_plot(f,τ=τ,x_scale='stretch',y_scale='auto',plot_kwargs={'label':'interactive!'}, title='the value of τ is: {τ:.2f}')
#you can still use plt commands if this is the active figure
plt.ylabel('yikes a ylabel!')
# you can new lines - though they won't be updated interactively.
plt.plot(x,np.sin(x),label='Added after, not interactive')
_ = plt.legend() # _ to capture the annoying output that would otherwise appear
Slider precision¶
You can change the precision of individual slider displays by passing
slider_format_string
as a dictionary. In the below cell we give τ 99
decimal points of precision and use scientific notation to display it.
The other sliders will use the default 1 decimal point of precision.
In [8]:
x = np.linspace(0,np.pi,100)
τ = np.linspace(.5, 10, 100)
β = np.linspace(1, 10, 100)
def f1(x, τ, β):
return np.sin(x*τ)*x*β
def f2(x, τ, β):
return np.sin(x*β)*x*τ
fig, ax, sliders = interactive_plot([f1, f2], x=x, τ = τ, β = β, slider_format_string = {"τ": '{:.99e}'})
_ = plt.legend()
fixed y-scale¶
You can also set yscale
to anything the matplotlib will accept as a
ylim
In [9]:
x = np.linspace(0,np.pi,100)
τ = np.linspace(1,10,100)
def f(x,τ):
return np.sin(x*τ)*x**τ
interactive_plot(f,x, τ=τ, y_scale=[-3,4],plot_kwargs={'label':'interactive!'})
#you can still use plt commands if this is the active figure
plt.ylabel('yikes a ylabel!')
plt.title('Fixed ylim')
# you can new lines - though they won't be updated interactively.
plt.plot(x,np.sin(x),label='Added after, not interactive')
_ = plt.legend() # _ to capture the annoying output that would otherwise appear
Controlling layout¶
If you want more control over the layout then you can use the argument
display=False
and use the returned elements to place the figure and
sliders whereever you would like. If you want even more control then you
should use interactive_plot_factory
which will not make any choices
about display and that you can provide a matplotlib.axis to. That has it
own example below
However even just using the returned values gives you enough flexibility to make questionable layout choices like so:
In [10]:
from ipywidgets import HBox, VBox
fig, ax, sliders = interactive_plot([f1, f2], x=x, τ = τ, β = β, display=False)
HBox([sliders, VBox([sliders,fig.canvas,sliders]), sliders])
interactive_plot_factory¶
This function will not wrap the sliders into VBox for you, it will return them as a list. It also takes a matplotlib axis as a required argument so you are responsible for creating and displaying the figure. In the below example I split up the sliders across multiple cells just to demonstrate that you can (though you probably should not)
In [11]:
plt.ioff(); fig, (ax1, ax2) = plt.subplots(1,2); plt.ion()
ax2.set_title('a totally separate axis', fontsize=14)
ax2.plot(f2(x, -.4, 3))
sliders = interactive_plot_factory(ax1, [f1, f2], x=x, τ = τ, β = β)
ax1.legend()
display(sliders[0])
In [12]:
VBox([*sliders[1:], fig.canvas])