Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added paper/imgs/logistic_growth-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added paper/imgs/logistic_growth.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added paper/imgs/sin-composed.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added paper/imgs/sinusoid-full.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added paper/imgs/sinusoid.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added paper/imgs/sinusoid_1-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added paper/imgs/sinusoid_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added paper/imgs/sinusoid_2-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added paper/imgs/sinusoid_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added paper/imgs/sinusoid_3-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added paper/imgs/sinusoid_3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added paper/imgs/sinusoid_4-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added paper/imgs/sinusoid_4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added paper/imgs/threshold-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added paper/imgs/threshold.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions paper/paper.bib
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@Article{Hunter:2007,
Author = {Hunter, J. D.},
Title = {Matplotlib: A 2D graphics environment},
Journal = {Computing in Science \& Engineering},
Volume = {9},
Number = {3},
Pages = {90--95},
abstract = {Matplotlib is a 2D graphics package used for Python for
application development, interactive scripting, and publication-quality
image generation across user interfaces and operating systems.},
publisher = {IEEE COMPUTER SOC},
doi = {10.1109/MCSE.2007.55},
year = 2007
}

@unpublished{interactive_Jupyter_widgets,
author = {Jupyter widgets community},
title = {ipywidgets, a GitHub repository},
year = {2015},
note = {Retrieved from https://github.com/jupyter-widgets/ipywidgets}
}
150 changes: 150 additions & 0 deletions paper/paper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
---
title: "mpl-interactions: A Python package for Interactive Matplotlib Figures"
tags:
- Python
- Matplotlib
- Ipywidgets
- Interactive
- Visualization
authors:
- name: Ian Hunt-Isaak
orcid: 0000-0002-7591-083X
affiliation: 1 # (Multiple affiliations must be quoted)
- name: Doeke Hekstra
orcid: 0000-0003-2332-9223
affiliation: "1, 2" # (Multiple affiliations must be quoted)
affiliations:
- name: John A. Paulson School of Engineering and Applied Sciences, Harvard University, Cambridge, MA, USA
index: 1
- name: Department of Molecular and Cellular Biology, Harvard University, Cambridge, MA, USA
index: 2
date: 31 May 2023
bibliography: paper.bib
---

# Summary

<!-- A summary describing the high-level functionality and purpose of the software for a diverse, non-specialist audience. -->

Data exploration, model building and pedagogy all benefit from the ability to interactively update elements in Matplotlib [@Hunter:2007] figures. `mpl-interactions` enables this by making it easy for users to create matplotlib figures in which the displayed data can be dynamically controlled through widgets. These widgets can be automatically generated by passing arguments such as arrays or shorthands (such as a tuple of numbers to generate a slider) to modified pyplot functions. After creation of these widgets, `mpl-interactions` updates plot elements without further user intervention. For ease of use, it adds these features while otherwise staying close to the `matplotlib.pyplot` interface. `mpl-interactions` is built such that parameters controlled by the generated widgets are easy to re-use for multiple plot elements, while not interfering with static elements. This design allows for building any figure that `matplotlib` can produce, while adding interactivity to specific parts as desired.

Complete Tutorials, Examples, and API documentation are available on https://mpl-interactions.readthedocs.io/en/stable/.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Complete Tutorials, Examples, and API documentation are available on https://mpl-interactions.readthedocs.io/en/stable/.
Complete Tutorials, Examples, and API documentation are available on https://mpl-interactions.readthedocs.io.

(here no pinned URL, see other comment)


# Statement of Need

<!-- A Statement of need section that clearly illustrates the research purpose of the software and places it in the context of related work. -->

The ability to interact dynamically with plots through widgets such as sliders can be a powerful tool in the scientific process and in pedagogy. For instance, varying a parameter of a mathematical model plotted on top of data helps to understand the relationship between the model and the data. Similarly, exploratory data analysis can be enhanced by interactively modifying aspects of the plot such as which points are displayed, or the threshold level of a displayed image.

Matplotlib provides mechanisms for updating elements (artists) in figures. However, the APIs for these artists are not consistent and some are under- or undocumented. Furthermore, the creation and positioning of the native Matplotlib widgets is nontrivial. While the `ipywidgets` [@interactive_Jupyter_widgets] library makes widget creation and positioning easier, it is difficult to integrate with matplotlib in a performant manner. The easiest way to do so is to use the `ipywidgets`' `interact()` function, which automatically generates sliders and other widgets to control arguments to arbitrary python functions. However, this can result in completely regenerating the figure which can be slow. Alternatively, the user needs to remember the specifics of how to update each individual artist. The final issue is that `ipywidgets` is a general framework, and thus constrained in its choices of how to interpret shorthands for widget generation -- as such, the choices it makes are not always optimal for scientific plotting.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Matplotlib provides mechanisms for updating elements (artists) in figures. However, the APIs for these artists are not consistent and some are under- or undocumented. Furthermore, the creation and positioning of the native Matplotlib widgets is nontrivial. While the `ipywidgets` [@interactive_Jupyter_widgets] library makes widget creation and positioning easier, it is difficult to integrate with matplotlib in a performant manner. The easiest way to do so is to use the `ipywidgets`' `interact()` function, which automatically generates sliders and other widgets to control arguments to arbitrary python functions. However, this can result in completely regenerating the figure which can be slow. Alternatively, the user needs to remember the specifics of how to update each individual artist. The final issue is that `ipywidgets` is a general framework, and thus constrained in its choices of how to interpret shorthands for widget generation -- as such, the choices it makes are not always optimal for scientific plotting.
Matplotlib provides mechanisms for updating elements (artists) in figures. However, the APIs for these artists can be inconsistent and some are not well documented. Furthermore, the creation and positioning of the native Matplotlib widgets is nontrivial. While the `ipywidgets` [@interactive_Jupyter_widgets] library makes widget creation and positioning easier, it is difficult to integrate with matplotlib in a performant manner. The easiest way to do so is to use the `ipywidgets`' `interact()` function, which automatically generates sliders and other widgets to control arguments to arbitrary python functions. However, this can result in completely regenerating the figure, which can be slow. Alternatively, the user needs to remember the specifics of how to update each individual artist. The final issue is that `ipywidgets` is a general framework, and thus constrained in its choices of how to interpret shorthands for widget generation. As such, the choices it makes are not always optimal for scientific plotting.

Just some minor suggestions here.

Btw, matplotlib or Matplotlib? 🤔 Same for some other package names.


While `matplotlib` and `ipywidgets` provide the tools for controlling plots with widgets, the overhead of implementing such control can overwhelm its utility. `mpl-interactions` fills this gap by making it easy for users to generate widgets that dynamically control plots.

# Overview

`mpl-interactions` provides several key features to make generating interactive figures simple. The first is what arguments are accepted. While `matplotlib` requires users to pass arrays as arguments, `mpl-interactions` allows passing a function that returns numeric values. Parameters to these functions are specified by adding extra keyword arguments (`kwargs`) to the plotting function call. Then `mpl-interactions` will generate the appropriate widgets for the parameters and run the functions to generate the numerical data to plot. For example, to plot a sinusoid and control its amplitude and frequency using sliders, a function returning the `y` values is defined and passed as the `y` parameter to the `plot` function. The ranges of the `A` and `f` parameters are defined as extra keyword arguments using tuples as a shorthand for what widget to generate.

```python
import mpl_interactions.ipyplot as iplt
import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots()

def sinusoid(x, A, f):
return A*np.sin(x * f)

x = np.linspace(0, np.pi, 100)

ctrls = iplt.plot(x, sinusoid, A=(1, 10), f = (.5, 2))
plt.show()
```

![Multiple states of the figure resulting from moving the sliders after running above example in jupyter lab.\label{fig:sinusoid}](imgs/sin-composed.png)

A second important feature of `mpl-interactions` is that interactive plot components are not isolated from each other. That is, the control widgets generated from one plotting call can be re-used to control other components. In addition to showing the re-use of control widgets this example demonstrates how matplotlib styling arguments (such as `vmin`) can be controlled through widgets.

```python
N = 128
rng = np.random.default_rng(seed=1995)
im = rng.normal(size=(N,N))

fig, axs = plt.subplots(1, 2, figsize=(12, 5))


# create interactive controls
ctrls = iplt.imshow(im, vmin_vmax=("r", im.min(), im.max()), ax=axs[0])

# plot histogram of pixel intensities

# by indexing the ctrls object it is possible to
# re-use the the vmin and vmax created by imshow
# to control the position of the axvlines
iplt.axvline(ctrls["vmin"], ax=axs[1], c="k")
iplt.axvline(ctrls["vmax"], ax=axs[1], c="k");
axs[1].hist(im.flatten(), bins="auto")
axs[1].set_title("Histogram of Pixel Intensities")
axs[1].set_xlabel('Pixel Intensity')
```

![An imshow where the thresholds of the colorbar are controlled by the `vmin_vmax` range slider. While the slider is created by the `imshow` call, its interactive parameters can be reused to control the position of the veritcal lines on the histogram.](imgs/threshold-dark.png)

Finally, `mpl-interactions` allows the reuse of Python functions performing mathematical operations in multiple parts of user code, rather than requiring users to write a plotting specific version. For example, below the `logistic_growth` function is used for both curve fitting and an interactive display in order to better understand the role of the model parameters.

```python
%matplotlib ipympl
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import curve_fit

import mpl_interactions.ipyplot as iplt


def logistic_growth(t, L, k, t0):
return L / (1 + np.exp(-k * (t - t0)))


# create a synthetic dataset of logistic growth
rng = np.random.default_rng(seed=1995)
t_data = np.sort(rng.uniform(0, 10, size=50))
y_data = logistic_growth(t_data, L=5, k=1, t0=1) + rng.normal(size=t_data.size, scale=0.1)


# You can use the `logistic_growth` function to curve_fit
popt, pcov = curve_fit(logistic_growth, t_data, y_data)


# Now you can directly the same function to make
# an interactive plot to better understand its parameters
fig, axs = plt.subplots(1, 2, sharey=True)
axs[0].plot(t_data, y_data, "o")
axs[0].plot(t_data, logistic_growth(t_data, *popt))
axs[0].set_title("Data + Fit")
axs[1].set_title("Interactive Exploration")

ctrls = iplt.plot(
np.linspace(0, 10),
logistic_growth,
L=(0.5, 10),
k=(0.1, 1),
t0=(0, 2.5),
ax=axs[1],
label="interactive",
)
axs[1].plot(t_data, logistic_growth(t_data, *popt), "--", label="fit")
plt.legend()
```

![Generated figure and sliders after running above example in jupyter lab.\label{fig:logistic}](imgs/logistic_growth-dark.png){ width=75% }

This framework makes it easy generate complex interactive visualizations. It also enables `mpl-interactions` to manage generating GIFs. Any parameter controlled through `mpl-interactions` can be used to automatically generate a gif of the plot changing as a function of that parameter ([Animation Documentation](https://mpl-interactions.readthedocs.io/en/stable/examples/animations.html)). Thus `mpl-interactions` can assist across the data visualization process, from initial exploration to the creation of a final animated plot as a GIF.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the paper, I would use pinned URLs, e.g. of the latest version:

[Animation Documentation](https://mpl-interactions.readthedocs.io/en/0.23.0/examples/animations.html)


# Acknowledgements

WThis work was supported by a National Defense Science and Engineering Graduate Fellowship (FA9550-19-F-0008, to IHI), the George W. Merck Fund of the New York Community Trust (award 338034, to DRH), and funds from Harvard University.

We thank Dr. K. Dalton from stimulating discussion, and Easun Arunachalam for feedback on drafts of this paper. In addition, many users have contributed features and bug fixes. Of particular note are Remco de Boer, John Russell, and Samantha Hamilton who made contributions to documentation and code, code, and documentation respectively. A full list of coding contributors can be found here: https://github.com/mpl-extensions/mpl-interactions/graphs/contributors
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

attention: @redeboer @samanthahamilton I hope you don't mind getting a shout out here :)

The other people I know in real life so will check with them offline


Finally, many users have contributed in ways other than coding. For example by raising issues either with the package or documentation. These users are recognized here: https://github.com/mpl-extensions/mpl-interactions#contributors-

# References