#!/usr/bin/env python # coding: utf-8 # # Plot # # # The interactive plot function is availiable either as `mpl_interactions.pyplot.interactive_plot` or as `mpl_interactions.ipyplot.plot`. It will behave equivalently to the normal matplotlib `pyplot.plot` function if you don't pass it any non-plotting kwargs. If you do pass in non-plotting kwargs and one or both of `x` or `y` is a function then the kwargs will be converted into widgets to control the function. # In[ ]: get_ipython().run_line_magic('matplotlib', 'ipympl') import ipywidgets as widgets import matplotlib.pyplot as plt import numpy as np import mpl_interactions.ipyplot as iplt # In[ ]: x = np.linspace(0, np.pi, 100) tau = np.linspace(1, 10, 100) beta = np.linspace(0.001, 1) def f(x, tau, beta): return np.sin(x * tau) * x ** beta fig, ax = plt.subplots() controls = iplt.plot(x, f, tau=tau, beta=beta) # ### 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: # ```bash # 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[ ]: fig2, ax2 = plt.subplots() controls2 = iplt.plot(x, f, tau=(1, 10, 100), beta=(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[ ]: 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 elif type_ == "beep": return x * beta / tau else: return x * beta * tau fig3, ax3 = plt.subplots() controls3 = iplt.plot( x, f, tau=(0.5, 10, 100), beta=(2, 10), type_={("sin", "cos", "beep", "boop")} ) # ## Using widgets for as parameters # # You can also pass an `ipywidgets` widget that has a `value` attribute # In[ ]: 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) fig4, ax4 = plt.subplots() controls4 = iplt.plot(x, f, tau=tau, beta=(1, 10), type_={("sin", "cos")}) # ## With multiple functions # # You have multiple interactive functions by passing the controls object to subsequent calls. # In[ ]: x = np.linspace(0, np.pi, 100) tau = np.linspace(0.5, 10, 100) beta = np.linspace(1, 10, 100) def f1(x, tau, beta): return np.sin(x * tau) * x * beta def f2(x, tau, beta): return np.sin(x * beta) * x * tau fig, ax = plt.subplots() controls = iplt.plot(x, f1, tau=tau, beta=beta, label="f1") iplt.plot(x, f2, label="f2", controls=controls) _ = plt.legend() # ## 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 `tau` in and round it to two decimals use the following title string: `{'tau:.2f}'` # In[ ]: 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", title="the value of tau is: {tau:.2f}", label="interactive!", ) # 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 tau 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[ ]: x = np.linspace(0, np.pi, 100) tau = np.linspace(0.5, 10, 100) beta = np.linspace(1, 10, 100) def f1(x, tau, beta): return np.sin(x * tau) * x * beta def f2(x, tau, beta): return np.sin(x * beta) * x * tau fig, ax = plt.subplots() controls = iplt.plot(x, f1, tau=tau, beta=beta, slider_formats={"tau": "{:.50e}"}) # ### fixed y-scale # # # You can also set `yscale` to anything the matplotlib will accept as a `ylim` # In[ ]: x = np.linspace(0, np.pi, 100) tau = np.linspace(1, 10, 100) def f(x, tau): return np.sin(x * tau) * x ** tau fig, ax = plt.subplots() iplt.plot(x, f, tau=tau, ylim=[-3, 4], 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 # In[ ]: