diff --git a/examples/advanced.ipynb b/examples/advanced.ipynb new file mode 100644 index 00000000..4726db12 --- /dev/null +++ b/examples/advanced.ipynb @@ -0,0 +1,420 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Multiple independent axes, same plot\n", + "This constructive tutorial aims to enhance the following example, automating the creation of new independent, but in the same plot, axes for each series. Providing a final reusable function DisjointPlot(data,names)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Based on matplotlib example demo_parasite_axes2\n", + "https://matplotlib.org/examples/axes_grid/demo_parasite_axes2.html" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "from mpl_toolkits.axes_grid1 import host_subplot\n", + "import mpl_toolkits.axisartist as AA\n", + "import matplotlib.pyplot as plt\n", + "\n", + "host = host_subplot(111, axes_class=AA.Axes)\n", + "plt.subplots_adjust(right=0.75)\n", + "\n", + "par1 = host.twinx()\n", + "par2 = host.twinx()\n", + "\n", + "new_fixed_axis = par1.get_grid_helper().new_fixed_axis\n", + "par1.axis[\"right\"] = new_fixed_axis(loc=\"right\", axes=par1,\n", + " offset=(0, 0))\n", + "par1.axis[\"right\"].toggle(all=True)\n", + "offset = 60\n", + "new_fixed_axis2 = par2.get_grid_helper().new_fixed_axis\n", + "par2.axis[\"right\"] = new_fixed_axis2(loc=\"right\", axes=par2,\n", + " offset=(offset, 0))\n", + "par2.axis[\"right\"].toggle(all=True)\n", + "\n", + "host.set_xlim(0, 2)\n", + "host.set_ylim(0, 2)\n", + "\n", + "host.set_xlabel(\"Distance\")\n", + "host.set_ylabel(\"Density\")\n", + "par1.set_ylabel(\"Temperature\")\n", + "par2.set_ylabel(\"Velocity\")\n", + "\n", + "p1, = host.plot([0, 1, 2], [0, 1, 2], label=\"Density\")\n", + "p2, = par1.plot([0, 1, 2], [0, 3, 2], label=\"Temperature\")\n", + "p3, = par2.plot([0, 1, 2], [50, 30, 15], label=\"Velocity\")\n", + "\n", + "par1.set_ylim(0, 4)\n", + "par2.set_ylim(1, 65)\n", + "\n", + "host.legend()\n", + "\n", + "host.axis[\"left\"].label.set_color(p1.get_color())\n", + "par1.axis[\"right\"].label.set_color(p2.get_color())\n", + "par2.axis[\"right\"].label.set_color(p3.get_color())\n", + "\n", + "plt.draw()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## + 1 axis to get then hang of it" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "from mpl_toolkits.axes_grid1 import host_subplot\n", + "import mpl_toolkits.axisartist as AA\n", + "import matplotlib.pyplot as plt\n", + "\n", + "host = host_subplot(111, axes_class=AA.Axes)\n", + "plt.subplots_adjust(right=0.7)\n", + "\n", + "par1 = host.twinx()\n", + "par2 = host.twinx()\n", + "par3 = host.twinx()\n", + "\n", + "new_fixed_axis1 = par1.get_grid_helper().new_fixed_axis\n", + "par1.axis[\"right\"] = new_fixed_axis1(loc=\"right\", axes=par1, offset=(0, 0))\n", + "par1.axis[\"right\"].toggle(all=True)\n", + "\n", + "offset = 45\n", + "new_fixed_axis2 = par2.get_grid_helper().new_fixed_axis\n", + "par2.axis[\"right\"] = new_fixed_axis2(loc=\"right\", axes=par2, offset=(offset, 0))\n", + "par2.axis[\"right\"].toggle(all=True)\n", + "#\n", + "new_fixed_axis3 = par3.get_grid_helper().new_fixed_axis\n", + "par3.axis[\"right\"] = new_fixed_axis3(loc=\"right\", axes=par3, offset=(2*offset, 0))\n", + "par3.axis[\"right\"].toggle(all=True)\n", + "\n", + "host.set_xlim(0, 2)\n", + "host.set_ylim(0, 2)\n", + "\n", + "host.set_xlabel(\"Distance\")\n", + "host.set_ylabel(\"Density\")\n", + "par1.set_ylabel(\"Temperature\")\n", + "par2.set_ylabel(\"Velocity\")\n", + "par3.set_ylabel(\"Luminocity\")\n", + "\n", + "p1, = host.plot([0, 1, 2], [0, 1, 2], label=\"Density\")\n", + "p2, = par1.plot([0, 1, 2], [0, 3, 2], label=\"Temperature\")\n", + "p3, = par2.plot([0, 1, 2], [50, 30, 15], label=\"Velocity\")\n", + "p4, = par3.plot([0, 1, 2], [69, 250, 600], label=\"Luminocity\")\n", + "\n", + "par1.set_ylim(0, 4)\n", + "par2.set_ylim(1, 65)\n", + "par3.set_ylim(0, 700)\n", + "\n", + "host.legend()\n", + "\n", + "host.axis[\"left\"].label.set_color(p1.get_color())\n", + "par1.axis[\"right\"].label.set_color(p2.get_color())\n", + "par2.axis[\"right\"].label.set_color(p3.get_color())\n", + "par3.axis[\"right\"].label.set_color(p4.get_color())\n", + "\n", + "#plt.draw()\n", + "plt.tight_layout()\n", + "#plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## + interact with placement offset & subplots_adjust" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "from mpl_toolkits.axes_grid1 import host_subplot\n", + "import mpl_toolkits.axisartist as AA\n", + "import matplotlib.pyplot as plt\n", + "from ipywidgets import interact, FloatSlider\n", + "\n", + "print('(Un)Commenting tight_layout (line 60) makes a big difference,\\ndoes all the adjustement automatically given you chose small enough starting parameters')\n", + "\n", + "@interact( x= FloatSlider(min=0.5,max=1,step=0.02,value=0.6),\n", + " y= FloatSlider(min=30,max=60,step=2,value=45))\n", + "def on_value_change(x,y):\n", + " plt.clf()\n", + " \n", + " host = host_subplot(111, axes_class=AA.Axes)\n", + " plt.subplots_adjust(right=x)\n", + " \n", + " par1 = host.twinx()\n", + " par2 = host.twinx()\n", + " par3 = host.twinx()\n", + " \n", + " new_fixed_axis1 = par1.get_grid_helper().new_fixed_axis\n", + " par1.axis[\"right\"] = new_fixed_axis1(loc=\"right\", axes=par1, offset=(0, 0))\n", + " par1.axis[\"right\"].toggle(all=True)\n", + " \n", + " offset = y\n", + " new_fixed_axis2 = par2.get_grid_helper().new_fixed_axis\n", + " par2.axis[\"right\"] = new_fixed_axis2(loc=\"right\", axes=par2, offset=(offset, 0))\n", + " par2.axis[\"right\"].toggle(all=True)\n", + " #\n", + " new_fixed_axis3 = par3.get_grid_helper().new_fixed_axis\n", + " par3.axis[\"right\"] = new_fixed_axis3(loc=\"right\", axes=par3, offset=(2*offset, 0))\n", + " par3.axis[\"right\"].toggle(all=True)\n", + " \n", + " host.set_xlim(0, 2)\n", + " host.set_ylim(0, 2)\n", + " \n", + " host.set_xlabel(\"Distance\")\n", + " host.set_ylabel(\"Density\")\n", + " par1.set_ylabel(\"Temperature\")\n", + " par2.set_ylabel(\"Velocity\")\n", + " par3.set_ylabel(\"Luminocity\")\n", + " \n", + " p1, = host.plot([0, 1, 2], [0, 1, 2], label=\"Density\")\n", + " p2, = par1.plot([0, 1, 2], [0, 3, 2], label=\"Temperature\")\n", + " p3, = par2.plot([0, 1, 2], [50, 30, 15], label=\"Velocity\")\n", + " p4, = par3.plot([0, 1, 2], [10, 333, 600], label=\"Luminocity\")\n", + " \n", + " par1.set_ylim(0, 4)\n", + " par2.set_ylim(1, 65)\n", + " par3.set_ylim(0, 700)\n", + " \n", + " host.legend()\n", + " \n", + " host.axis[\"left\"].label.set_color(p1.get_color())\n", + " par1.axis[\"right\"].label.set_color(p2.get_color())\n", + " par2.axis[\"right\"].label.set_color(p3.get_color())\n", + " par3.axis[\"right\"].label.set_color(p4.get_color())\n", + " \n", + " #plt.draw()\n", + " #plt.tight_layout()\n", + " #plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## + n axes test\n", + "Slight bug: By uncommenting the print statement at line 71, it stops printing 2 graphs on play. An alternative is just interacting with the sliders" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "import matplotlib.pyplot as plt\n", + "from mpl_toolkits.axes_grid1 import host_subplot\n", + "import mpl_toolkits.axisartist as AA\n", + "from ipywidgets import interact, FloatSlider, IntSlider, Output, HTML, VBox, Layout\n", + "import numpy as np\n", + "randint = np.random.randint\n", + "randn = np.random.randn\n", + "# Two-by-four array of samples from N(3, 6.25):\n", + "# 2.5 * np.random.randn(2, 4) + 3\n", + "\n", + "out = Output()\n", + "@out.capture(clear_output=True)\n", + "@interact( w= IntSlider(description='x range',min=2,max=100,step=1,value=4,continuous_update=False),\n", + " x= FloatSlider(description='subplots_adjust(right',min=0.1,max=1,step=0.02,value=0.60,continuous_update=True,style = {'description_width': 'initial'}),\n", + " y= FloatSlider(description='offset',min=15,max=60,step=2,value=35,continuous_update=False),\n", + " z= IntSlider(description='number of lines',min=2,max=10,step=1,value=6,continuous_update=False,style = {'description_width': 'initial'}))\n", + "def on_value_change(w,x,y,z):\n", + "\n", + " data = 2.5 * np.random.randn(z, w) + 3\n", + " \n", + " plt.clf()\n", + " host = host_subplot(111, axes_class=AA.Axes)\n", + " plt.subplots_adjust(right=x)\n", + " \n", + " # parallel additional axis\n", + " par=[ host.twinx() for i in range(z-1) ]\n", + " \n", + " new_fixed_axis=[ par[i].get_grid_helper().new_fixed_axis for i in range(z-1) ]\n", + " for i in range(z-1):\n", + " par[i].axis[\"right\"] = new_fixed_axis[i](loc=\"right\", axes=par[i], offset=(i*y, 0))\n", + " par[i].axis[\"right\"].toggle(all=True)\n", + " \n", + " # horizontal lim\n", + " host.set_xlim(0, w-1)\n", + " # 0 \n", + " host.set_ylim( np.floor(data[0].min()), np.ceil(data[0].max())) #(0, 2)\n", + " \n", + " # 0\n", + " host.set_xlabel(\"x range\")\n", + " host.set_ylabel(\"label 0\")\n", + " #>0\n", + " for i in range(z-1):\n", + " par[i].set_ylabel(\"label %s\"%(i+1))\n", + "\n", + " \n", + " p=[ None for i in range(z) ]\n", + " p[0], = host.plot(range(w), data[0], label=\"label 0\")\n", + " for i in range(z-1):\n", + " p[i+1], = par[i].plot(range(w), data[i+1], label=\"label %s\"%(i+1))\n", + " \n", + " #>0\n", + " for i in range(z-1):\n", + " par[i].set_ylim( np.floor(data[i+1].min()), np.ceil(data[i+1].max()))\n", + "\n", + " host.legend(loc='best',framealpha=0.5)\n", + " \n", + " host.axis[\"left\"].label.set_color(p[0].get_color())\n", + " for i in range(z-1):\n", + " par[i].axis[\"right\"].label.set_color(p[i+1].get_color())\n", + " \n", + " #plt.draw()\n", + " plt.tight_layout()\n", + " #plt.show()\n", + " \n", + " paranoia=''\n", + " for r in range(z):\n", + " mmin=np.floor(data[r].min())\n", + " mmax=np.ceil(data[r].max())\n", + " paranoia+=str((mmin, data[r], mmax))+'
'\n", + " #print((mmin, data[r], mmax))\n", + " for c in range(w):\n", + " assert mmin <= data[r][c] and data[r][c] <= mmax, ' problem %s%s'%(row,col)\n", + " \n", + " display(VBox([plt.figure(1).canvas,HTML(paranoia)]))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Final reusable form" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib widget\n", + "from mpl_toolkits.axes_grid1 import host_subplot\n", + "import mpl_toolkits.axisartist as AA\n", + "import matplotlib.pyplot as plt\n", + "from numpy import floor, ceil\n", + "\n", + "def DisjointPlot( data=[[1,2,3],[33,44,38],[6,5,8],[69,420,666]], names=['a','b','c','d'] ):\n", + " '''\n", + " A quick and raw plot of at least 2 series with a shared axis, plotting each y-axis independently\n", + " name[0] ,data[0 ] is the shared axis\n", + " name[1:],data[1:] are the series to plot\n", + " Requirements:\n", + " At least 3 lists needed: one shared axis, two series\n", + " Every list should have the same dimension\n", + " Example in the default arguments:\n", + " DisjointPlot() ~ DisjointPlot( data=[[1,2,3],[33,44,38],[6,5,8],[69,420,666]], names=['a','b','c','d'] )\n", + " '''\n", + " # check data\n", + " rows=len(data)\n", + " assert rows>2, 'At least 3 lists needed: one shared axis, two series'\n", + " cols=len(data[0])\n", + " for r in range(1,rows):\n", + " assert cols==len(data[r]), 'Serie of index %s not of the same dimension of shared axis data[0]'%r\n", + " \n", + " z=len(data)-1 #number of series\n", + " y=50 #offset\n", + " \n", + " plt.clf()\n", + " host = host_subplot(111, axes_class=AA.Axes)\n", + " plt.subplots_adjust(right=0.7)\n", + " \n", + " # parallel additional axis\n", + " par=[ host.twinx() for i in range(z-1) ]\n", + " \n", + " new_fixed_axis=[ par[i].get_grid_helper().new_fixed_axis for i in range(z-1) ]\n", + " for i in range(z-1):\n", + " par[i].axis[\"right\"] = new_fixed_axis[i](loc=\"right\", axes=par[i], offset=(i*y, 0))\n", + " par[i].axis[\"right\"].toggle(all=True)\n", + " \n", + " # horizontal lim\n", + " host.set_xlim( data[0][0], data[0][-1])\n", + " # 0 \n", + " host.set_ylim( floor(min(data[1])), ceil(max(data[1]))) #(0, 2)\n", + " \n", + " # 0\n", + " host.set_xlabel(names[0])\n", + " host.set_ylabel(names[1])\n", + " #>0\n", + " for i in range(z-1):\n", + " par[i].set_ylabel(names[i+2])\n", + "\n", + " \n", + " p=[ None for i in range(z) ]\n", + " p[0], = host.plot(data[0], data[1], label=names[1])\n", + " for i in range(z-1):\n", + " p[i+1], = par[i].plot(data[0], data[i+2], label=names[i+2])\n", + " \n", + " #>0\n", + " for i in range(z-1):\n", + " par[i].set_ylim( floor(min(data[i+2])), ceil(max(data[i+2])))\n", + "\n", + " host.legend(loc='best',framealpha=0.5)\n", + " \n", + " host.axis[\"left\"].label.set_color(p[0].get_color())\n", + " for i in range(z-1):\n", + " par[i].axis[\"right\"].label.set_color(p[i+1].get_color())\n", + " \n", + " #plt.draw()\n", + " plt.tight_layout()\n", + " #plt.show()\n", + " \n", + " return plt.figure(1).canvas\n", + "\n", + "with plt.xkcd():\n", + " DisjointPlot()\n", + "# If you haven't installed the awesome xkcd styling, display it like\n", + "#display(DisjointPlot())" + ] + } + ], + "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 +} diff --git a/examples/ipympl.ipynb b/examples/ipympl.ipynb index 4f8bb161..bcefd3ff 100644 --- a/examples/ipympl.ipynb +++ b/examples/ipympl.ipynb @@ -169,7 +169,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.7.2" } }, "nbformat": 4,