{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Plot\n", "\n", "\n", "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%matplotlib ipympl\n", "import ipywidgets as widgets\n", "import matplotlib.pyplot as plt\n", "import numpy as np\n", "\n", "import mpl_interactions.ipyplot as iplt" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "gif": "plot-simple.gif" }, "outputs": [], "source": [ "x = np.linspace(0, np.pi, 100)\n", "tau = np.linspace(1, 10, 100)\n", "beta = np.linspace(0.001, 1)\n", "\n", "\n", "def f(x, tau, beta):\n", " return np.sin(x * tau) * x ** beta\n", "\n", "\n", "fig, ax = plt.subplots()\n", "controls = iplt.plot(x, f, tau=tau, beta=beta)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Troubleshooting\n", "If instead of a plot you got an output that looks like this:\n", "`VBox([IntSlider(min=0, max=10 .....`\n", "and you are using jupyterlab then you probably need to install jupyterlab-manager:\n", "```bash\n", "conda install -c conda-forge nodejs=12\n", "jupyter labextension install @jupyter-widgets/jupyterlab-manager\n", "```\n", "after the install and finishes refresh the browser page and it should work" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### set parameters with tuples\n", "\n", "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" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "gif": "plot-simple.gif" }, "outputs": [], "source": [ "fig2, ax2 = plt.subplots()\n", "controls2 = iplt.plot(x, f, tau=(1, 10, 100), beta=(1, 10))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Use sets for categorical values\n", "\n", "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" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "gif": "plot-categorical.apng" }, "outputs": [], "source": [ "def f(x, tau, beta, type_):\n", " if type_ == \"sin\":\n", " return np.sin(x * tau) * x ** beta\n", " elif type_ == \"cos\":\n", " return np.cos(x * tau) * x ** beta\n", " elif type_ == \"beep\":\n", " return x * beta / tau\n", " else:\n", " return x * beta * tau\n", "\n", "\n", "fig3, ax3 = plt.subplots()\n", "controls3 = iplt.plot(\n", " x, f, tau=(0.5, 10, 100), beta=(2, 10), type_={(\"sin\", \"cos\", \"beep\", \"boop\")}\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Using widgets for as parameters\n", "\n", "You can also pass an `ipywidgets` widget that has a `value` attribute" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def f(x, tau, beta, type_):\n", " if type_ == \"sin\":\n", " return np.sin(x * tau) * x ** beta\n", " elif type_ == \"cos\":\n", " return np.cos(x * tau) * x ** beta\n", "\n", "\n", "tau = widgets.FloatText(value=7, step=0.1)\n", "fig4, ax4 = plt.subplots()\n", "controls4 = iplt.plot(x, f, tau=tau, beta=(1, 10), type_={(\"sin\", \"cos\")})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## With multiple functions\n", "\n", "You have multiple interactive functions by passing the controls object to subsequent calls." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = np.linspace(0, np.pi, 100)\n", "tau = np.linspace(0.5, 10, 100)\n", "beta = np.linspace(1, 10, 100)\n", "\n", "\n", "def f1(x, tau, beta):\n", " return np.sin(x * tau) * x * beta\n", "\n", "\n", "def f2(x, tau, beta):\n", " return np.sin(x * beta) * x * tau\n", "\n", "\n", "fig, ax = plt.subplots()\n", "controls = iplt.plot(x, f1, tau=tau, beta=beta, label=\"f1\")\n", "iplt.plot(x, f2, label=\"f2\", controls=controls)\n", "_ = plt.legend()" ] }, { "cell_type": "markdown", "metadata": { "gif": "plot-multiple-functions.gif" }, "source": [ "## Styling of plot\n", "\n", "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\n", "\n", "You can control how `xlim`/`ylim`s behave using the `x_scale`/`y_scale` arguments. The options are:\n", "1. `stretch`\n", " - never shrink the x/y axis but will expand it to fit larger values\n", "2. `auto`\n", " - autoscale the x/y axis for every plot update\n", "3. `fixed`\n", " - always used the initial values of the limits\n", "4. a tuple\n", " - You can pass a value such as `[-4,5]` to have the limits not be updated by moving the sliders.\n", " \n", " \n", "### Title\n", "\n", "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}'`" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x_ = np.linspace(0, np.pi, 100)\n", "tau = np.linspace(1, 10, 100)\n", "\n", "\n", "def f_x(tau):\n", " return x_\n", "\n", "\n", "def f_y(x, tau):\n", " return np.sin(x * tau) * x\n", "\n", "\n", "fig, ax = plt.subplots()\n", "controls = iplt.plot(\n", " f_x,\n", " f_y,\n", " tau=tau,\n", " xlim=\"stretch\",\n", " ylim=\"auto\",\n", " title=\"the value of tau is: {tau:.2f}\",\n", " label=\"interactive!\",\n", ")\n", "\n", "# you can still use plt commands if this is the active figure\n", "plt.ylabel(\"yikes a ylabel!\")\n", "\n", "# you can new lines - though they won't be updated interactively.\n", "plt.plot(x, np.sin(x), label=\"Added after, not interactive\")\n", "\n", "_ = plt.legend() # _ to capture the annoying output that would otherwise appear" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Slider precision\n", "\n", "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. \n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = np.linspace(0, np.pi, 100)\n", "tau = np.linspace(0.5, 10, 100)\n", "beta = np.linspace(1, 10, 100)\n", "\n", "\n", "def f1(x, tau, beta):\n", " return np.sin(x * tau) * x * beta\n", "\n", "\n", "def f2(x, tau, beta):\n", " return np.sin(x * beta) * x * tau\n", "\n", "\n", "fig, ax = plt.subplots()\n", "controls = iplt.plot(x, f1, tau=tau, beta=beta, slider_formats={\"tau\": \"{:.50e}\"})" ] }, { "cell_type": "markdown", "metadata": { "image": "plot-slider-precision.png" }, "source": [ "### fixed y-scale\n", "\n", "\n", "You can also set `yscale` to anything the matplotlib will accept as a `ylim` " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "x = np.linspace(0, np.pi, 100)\n", "tau = np.linspace(1, 10, 100)\n", "\n", "\n", "def f(x, tau):\n", " return np.sin(x * tau) * x ** tau\n", "\n", "\n", "fig, ax = plt.subplots()\n", "iplt.plot(x, f, tau=tau, ylim=[-3, 4], label=\"interactive!\")\n", "\n", "# you can still use plt commands if this is the active figure\n", "plt.ylabel(\"yikes a ylabel!\")\n", "plt.title(\"Fixed ylim\")\n", "\n", "# you can new lines - though they won't be updated interactively.\n", "plt.plot(x, np.sin(x), label=\"Added after, not interactive\")\n", "\n", "_ = plt.legend() # _ to capture the annoying output that would otherwise appear" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.8" } }, "nbformat": 4, "nbformat_minor": 4 }