Usage Guide¶
Basic Usage¶
The mpl_interactions.pyplot
module generalizes functions from matplotlib to be able to plot the results of a function rather than an array. So if we wanted to explore how the function
behaves as a function of \(\tau\) and \(\beta\). Normally to do this we would need to calculate \(f\) for multiple values of tau and beta and then plot multiple lines onto a plot.
With mpl_interactions
we can plot the function and use sliders to control the values of \(\tau\) of and \(\beta\).
Note: For this first section we will use numpy arrays to specify what values tau
and beta
can have. But they can also be specified in a few other ways that may be more convenient – see the How to specify Parameters section below.
# imports
%matplotlib ipympl
import matplotlib.pyplot as plt
import numpy as np
import mpl_interactions.ipyplot as iplt
# define the function
def f(x, tau, beta):
return np.sin(x * tau) * x ** beta
x = np.linspace(0, 2 * np.pi, 1000)
tau = np.linspace(5, 10)
beta = np.linspace(0.25, 1)
# make the interactive figure
fig, ax = plt.subplots()
controls = iplt.plot(x, f, tau=tau, beta=beta)
The interactive functions behave as closely to their Matplotlib equivalents as possible. So all of these are valid calls:
controls1 = iplt.plot(f, tau=tau, beta=beta)
controls2 = iplt.plot(f, 'o--', tau=tau, beta=beta)
controls3 = iplt.plot(x, f, tau=tau, beta=beta)
controls4 = iplt.plot(x, f, 'o--', tau=tau, beta=beta)
However, if you run all three of those lines together then you will create three sets of controls. To have sliders affect multiple lines you can pass the controls to the function instead of using keyword arguments.
fig, ax = plt.subplots()
def f1(tau, beta):
return np.sin(x * tau) * x * beta
def f2(tau, beta):
return np.sin(x * beta) * x * tau
controls = iplt.plot(f1, "k--", tau=tau, beta=beta, label="f1")
iplt.plot(f2, controls=controls, label="f2")
_ = plt.legend()
Only Using some of the parameters + specifying axes¶
Sometimes you may want to use one controls object for multiple functions but the functions take different arguments. There are two ways you can use the same controls object to control functions with different signatures.
Progressively add arguments
If you provide both a kwarg and a
controls
object to an interactive plotting function then the
Index the controls object to select only the parameters you want.
This example also shows how to choose what axis the plot shows up on. If you do not specify the ax
argument then the function will plot on the current axis in the same way that that Matplotlib works.
def f1(tau):
return np.sin(x * tau) * x * tau
def f2(tau, beta):
return np.sin(x * beta) * x * tau
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(10, 4.8))
# approach 1: adding extra kwargs
controls = iplt.plot(f1, tau=tau, ax=ax1)
_ = iplt.plot(f2, controls=controls, beta=beta, ax=ax1)
# approach 2: Indexing the controls object
iplt.plot(f1, controls=controls["tau"], ax=ax2)
_ = iplt.plot(f2, controls=controls, ax=ax2)
Combining Multiple types of Plots¶
You are not limited to only using the plot function - or to only using one type of interactive plot in a single graph. You can mix and match them as you please.
x = np.linspace(0, np.pi, 200)
def f_line(x, tau, beta):
return np.sin(x * tau) * x * beta
def f_scatter(x, tau):
return np.sin(x * tau) * x * tau + np.random.randn(len(x)) * 0.5
fig, ax = plt.subplots()
controls = iplt.plot(x, f_line, tau=tau, beta=beta)
_ = iplt.scatter(x, f_scatter, s=0.5, controls=controls["tau"])
How to Specify Parameters¶
In the above example we specified the possible values for tau
and beta
by passing in a numpy array. However, you are not limited to giving numpy arrays to create sliders. You can also use:
Tuples¶
Will be passed as an argument to numpy.linspace()
and that resulting array will be used as the slider values.
n.b. If you use Matplotlib sliders instead of ipywidgets sliders then tuples of length will just be used to define the max and min value of the slider as Matplotlib sliders do not require
fig, ax = plt.subplots()
controls = iplt.plot(x, f, tau=(5, 10, 5), beta=(0.25, 1))
Tuples for RangeSliders¶
You can create a RangeSlider
by passing a tuple prefixed with an "r"
.
def f_x(min_max, **kwargs):
# break up the tuple
min_, max_ = min_max
return np.linspace(min_, max_, 100)
# break up the tuple
def f_range(x, tau, **kwargs):
return np.sin(x * tau)
fig, ax = plt.subplots()
controls = iplt.plot(f_x, f_range, tau=(5, 10, 5), min_max=("r", 0, 10))
Categoricals¶
If you pass a parameter as a python set
then the categorical selection widget will be created rather than a slider. One thing to be aware of is that sets are unordered in Python, so if you want to maintain the ordering you can pass in values defined with a set containing a single tuple:
{(<option 1>, <option 2>)}
Existing widgets¶
You can also use existing widgets from either Matplotlib or ipywidgets.
import ipywidgets as widgets
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=0.1)
fig, ax = plt.subplots()
controls = iplt.plot(x, f, tau=tau, beta=(0.25, 1), type_={("sin", "cos")})
Fixed Parameters¶
You may want to pin the value of some parameters but not others. There are two ways to accomplish this.
Pass a scalar
Use
ipywidgets.fixed
So to fix tau
to 5.61
:
fig, ax = plt.subplots()
def f(x, tau, beta):
return np.sin(x * tau) * x * beta
tau = 5.61
# tau = ipywidgets.fixed(5.61)
controls = iplt.plot(x, f, tau=tau, beta=(0.25, 1))
Styling¶
Axis scaling¶
You can control how the xlim/ylim
behaves using the xlim/ylim
arguments. The options are:
Reference parameters values in the Title, xlabel, or ylabel¶
You can make the Title automatically update with information about the values by using iplt.title
. You can either provide a function that returns a string, or you can provide a string with the names of one of the parameters as a format specifier in the string. Ultimately the string will be formatted using:
if isinstance(title, Callable):
title_str = title(**params)
else:
title_str = title
ax.set_title(title_str.format(**params))
the same applies for the x and y labels.
x_ = np.linspace(0, np.pi, 100)
tau = np.linspace(1, 10, 100)
def f_x(tau):
return x_
def f_y(x, tau):
return np.sin(x * tau) * x
fig, ax = plt.subplots()
controls = iplt.plot(
f_x,
f_y,
tau=tau,
xlim="stretch",
ylim="auto",
label="interactive!",
)
iplt.title("the value of tau is: {tau:.2f}", controls=controls["tau"])
# you can still use plt commands if this is the active figure
def ylabel(tau):
return f"tau/2 is {np.round(tau/2,3)}"
iplt.ylabel(ylabel, controls=controls["tau"])
iplt.xlabel("This xlabel also changes with tau! tau~={tau:.0f}", controls=controls["tau"])
# you can new lines - though they won't be updated interactively.
plt.plot(x, np.sin(x), label="Added after, not interactive")
_ = plt.legend()
Embedding the controls into a larger layout¶
If you are using ipywidgets
then you can incorporate the controls
object into an AppLayout
or VBox
or other ipywidgets layout tool by using controls.vbox
which is an ipywidget and thus can be included in the layout.
For matplotlib sliders, the Controls
object stores a list of all the figures it uses for controls in Controls.control_figures