Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save owencorrigan/932e539fd17c606fb7a050e4c2208554 to your computer and use it in GitHub Desktop.
Save owencorrigan/932e539fd17c606fb7a050e4c2208554 to your computer and use it in GitHub Desktop.
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"from matplotlib.animation import FuncAnimation\n",
"\n",
"import numpy as np\n",
"\n",
"from ipywidgets import interact, widgets\n",
"from IPython.display import HTML\n",
"\n",
"import time\n",
"from IPython import display"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%matplotlib notebook"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!python3.7 -m pip freeze | grep 'ipykernal\\|ipython\\|ipywidgets\\|numpy\\|matplotlib\\|pandas'"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Illustration that interact is slow"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"@interact(i=widgets.IntSlider(min=0, max=100, step=1, value=0))\n",
"def f(i):\n",
" x = np.linspace(0, 2, 1000)\n",
" y = np.sin(2 * np.pi * (x - 0.01 * i))\n",
" plt.plot(y)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In this notebook we willl try to speed this up"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Solution: JSPlayer"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"plt.rc('animation', html='jshtml')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"http://louistiao.me/posts/notebooks/embedding-matplotlib-animations-in-jupyter-as-interactive-javascript-widgets/"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig, ax = plt.subplots()\n",
"\n",
"ax.set_xlim(( 0, 2))\n",
"ax.set_ylim((-2, 2))\n",
"\n",
"line, = ax.plot([], [], lw=2)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def init():\n",
" line.set_data([], [])\n",
" return (line,)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def animate(i):\n",
" x = np.linspace(0, 2, 1000)\n",
" y = np.sin(2 * np.pi * (x - 0.01 * i))\n",
" line.set_data(x, y)\n",
" return (line,)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"anim = FuncAnimation(fig, animate, init_func=init,\n",
" frames=100, interval=20, \n",
" blit=True)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"anim"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This is nice, because speed is fixed and precompiled. However it is slow, potentially will take up lot of space, and not dynacmic. For example, we might want to do interactive queries"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# NBAgg Backend\n",
"\n",
"We can change the \"backend\" that matplotlib uses. Traditionally, it was used to switch between the two main widget frameworks on linux - qt and gtk. However, nbagg is a specific one for notebooks. Without it we can't animate frames inside a cell\n",
"\n",
"By enabiling we get scrolling and zooming in all matplotlib plots for free"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# enabel nbagg backend\n",
"\n",
"import matplotlib\n",
"matplotlib.use('nbagg')\n",
"\n",
"# or use this at top of notebook\n",
"# %matplotlib nbagg"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"https://pelson.github.io/2014/nbagg_backend/\n",
"\n",
"https://matplotlib.org/3.1.1/api/backend_nbagg_api.html\n",
"\n",
"https://stackoverflow.com/questions/34486642/what-is-the-currently-correct-way-to-dynamically-update-plots-in-jupyter-ipython\n",
"\n",
"https://stackoverflow.com/questions/43027980/purpose-of-matplotlib-inline"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Heres an example to demonstrate it working. We should see the lines update every second with random data"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import time\n",
"\n",
"def pltsin(ax, colors=['b']):\n",
" x = np.linspace(0,1,100)\n",
" if ax.lines:\n",
" for line in ax.lines:\n",
" line.set_xdata(x)\n",
" y = np.random.random(size=(100,1))\n",
" line.set_ydata(y)\n",
" else:\n",
" for color in colors:\n",
" y = np.random.random(size=(100,1))\n",
" ax.plot(x, y, color)\n",
" fig.canvas.draw()\n",
"\n",
"fig,ax = plt.subplots(1,1)\n",
"ax.set_xlabel('X')\n",
"ax.set_ylabel('Y')\n",
"ax.set_xlim(0,1)\n",
"ax.set_ylim(0,1)\n",
"for f in range(5):\n",
" pltsin(ax, ['b', 'r'])\n",
" time.sleep(1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"https://matplotlib.org/3.1.1/api/animation_api.html"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"https://matplotlib.org/3.1.1/tutorials/intermediate/artists.html"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"https://dev.to/skotaro/artist-in-matplotlib---something-i-wanted-to-know-before-spending-tremendous-hours-on-googling-how-tos--31oo"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"It's not D3, but it's entirely in python and allows us to work with existing workflow"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Sine wave example\n",
"\n",
"Lets speed up the sine wave example by redrawing it in the cell"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"https://stackoverflow.com/questions/8955869/why-is-plotting-with-matplotlib-so-slow"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Lets redo the example above to use nbagg + sine wave"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import time\n",
"\n",
"def pltsin(ax, i, colors=['b']):\n",
" x = np.linspace(0,2,2000)\n",
" line = ax.lines[0]\n",
" line.set_xdata(x)\n",
" y = np.sin(2 * np.pi * (x - 0.01 * i))\n",
" line.set_ydata(y)\n",
" fig.canvas.draw()\n",
"\n",
"fig,ax = plt.subplots(1,1)\n",
"ax.set_xlabel('X')\n",
"ax.set_ylabel('Y')\n",
"ax.set_xlim(0,2)\n",
"ax.set_ylim(-2,2)\n",
"x = np.linspace(0,2,2000)\n",
"y = np.sin(2 * np.pi * (x - 0.01 * 0))\n",
"ax.plot(x, y, 'r')\n",
"for i in range(10):\n",
" pltsin(ax, i, ['b'])\n",
" time.sleep(0.02)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now integrate with interact widgets"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import time\n",
"\n",
"x = np.linspace(0,2,2000)\n",
"def pltsin(ax, i):\n",
" line = ax.lines[0]\n",
" line.set_xdata(x)\n",
" y = np.sin(2 * np.pi * (x - 0.01 * i))\n",
" line.set_ydata(y)\n",
" fig.canvas.draw()\n",
"fig,ax = plt.subplots(1,1)\n",
"ax.set_xlabel('X')\n",
"ax.set_ylabel('Y')\n",
"ax.set_xlim(0,2)\n",
"ax.set_ylim(-2,2)\n",
"\n",
"y = np.sin(2 * np.pi * (x - 0.01 * 0))\n",
"ax.plot(x, y, 'r')\n",
"@interact(i=widgets.IntSlider(min=0, max=100, step=1, value=0))\n",
"def f(i):\n",
" pltsin(ax, i)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Seems to work, but layer doesn't clear if I move it too fast. Simple solution is to use smaller intervals so I can't move it that fast. Moving step by step and jumping seem to be fine\n",
"\n",
"Potentially could be smoother with blitting?\n",
"\n",
"https://matplotlib.org/3.1.1/api/animation_api.html"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# True Interactivity: Progressive Drawing\n",
"\n",
"The problem with the example above is that we are redrawing the background every single time, when only the line needs to be redrawn. In the example below we will cache the background and only redraw the line\n",
"\n",
"Note that this is roughly how the AnimateFunction class works"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import time\n",
"\n",
"x = np.linspace(0,2,2000)\n",
"def pltsin(ax, i, line):\n",
" line.set_xdata(x)\n",
" y = np.sin(2 * np.pi * (x - 0.01 * i))\n",
" line.set_ydata(y)\n",
" ax.figure.canvas.blit(ax.bbox)\n",
"\n",
"fig,ax = plt.subplots(1,1)\n",
"ax.set_xlabel('X')\n",
"ax.set_ylabel('Y')\n",
"ax.set_xlim(0,2)\n",
"ax.set_ylim(-2,2)\n",
"line = plt.Line2D([], [])\n",
"ax.add_artist(line)\n",
"fig.canvas.draw()\n",
"\n",
"@interact(i=widgets.IntSlider(min=0, max=1000, step=1, value=0))\n",
"def f(i):\n",
" pltsin(ax, i, line=line)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Seems to fix issue with background. Much more responsive"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Note that it will be important to fix axes ahead of time. Animated axis would be nice, but not really feasible. We can do animated axes in D3."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Could intentionally slow it down intentionally for better animation? But we're not making mario here"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Now lets do it with subplots"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig = plt.figure()\n",
"ax1 = fig.add_subplot(2, 1, 1)\n",
"ax2 = fig.add_subplot(2, 1, 2)\n",
"\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import time\n",
"\n",
"x = np.linspace(0,2,2000)\n",
"def pltsin(ax, i, line1, line2):\n",
" y = np.sin(2 * np.pi * (x - 0.01 * i))\n",
" line1.set_ydata(y)\n",
" y = np.cos(2 * np.pi * (x - 0.01 * i))\n",
" line2.set_ydata(y)\n",
" fig.canvas.blit(ax1.bbox)\n",
" fig.canvas.blit(ax2.bbox)\n",
"\n",
"ax1.set_xlabel('X')\n",
"ax1.set_ylabel('Y')\n",
"ax1.set_xlim(0,2)\n",
"ax1.set_ylim(-2,2)\n",
"ax2.set_xlabel('X')\n",
"ax2.set_ylabel('Y')\n",
"ax2.set_xlim(0,2)\n",
"ax2.set_ylim(-2,2)\n",
"line1, line2 = plt.Line2D([], []), plt.Line2D([], [])\n",
"line1.set_xdata(x)\n",
"line2.set_xdata(x)\n",
"ax1.add_artist(line1)\n",
"ax2.add_artist(line2)\n",
"\n",
"fig.subplots_adjust(hspace = 1)\n",
"@interact(i=widgets.IntSlider(min=0, max=1000, step=1, value=0))\n",
"def f(i):\n",
" pltsin(ax, i, line1=line1, line2=line2)"
]
}
],
"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.2"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment