{ "metadata": { "name": "", "signature": "sha256:bc0eb67bf64009a1f11e50a42ca1bd26dc0052bafa7be529bf17d6b6c50cfe80" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "[![](https://bytebucket.org/davis68/resources/raw/f7c98d2b95e961fae257707e22a58fa1a2c36bec/logos/baseline_cse_wdmk.png?token=be4cc41d4b2afe594f5b1570a3c5aad96a65f0d6)](http://cse.illinois.edu/)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Plotting in Python\n", "\n", "\n", "## Introduction\n", "\n", "This notebook discusses the main features of [MatPlotLib](http://www.matplotlib.org/) (`matplotlib`) and other related plotting packages in a scientific computing context.\n", "\n", "Although any installation of [IPython](http://ipython.org/) will work with a version of this notebook, we recommend that you download and install [Anaconda](https://continuum.io/downloads) Python. To launch the notebook, open a command terminal, type `ipython notebook tutorial.ipynb`, and press Return.\n", "\n", "A few notes on this tutorial:\n", "\n", "- The code in this tutorial is written for Python 3, so if you have Python 2 then you may need to make some sensible modifications.\n", "\n", "- Code blocks starting with `$` are intended to be run on the command line, not executed as Python code.\n", "\n", "## Contents\n", "- [Introduction](#intro)\n", "- [Plotting in 2D: `matplotlib`](#mpl)\n", " - [Professional Plotting](#prof)\n", " - [Animating Plots](#anim)\n", "- [Prettier Plotting (`seaborn`)](#pretty)\n", " - [Palettes](#palettes)\n", "- [More Professional Plotting](#prof-more)\n", "- [References](#refs)\n", "- [Credits](#credits)" ] }, { "cell_type": "heading", "level": 4, "metadata": {}, "source": [ "Standard Header" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As we will be utilizing a number of packages with reasonably long names, we will adopt the _de facto_ standard module abbreviations in the following header. We also ensure that our [division behavior is sensible](http://www.python.org/dev/peps/pep-0238/) by importing from `__future__`: _i.e._, promotion to `double` will occur from `int` or `long` data types involving division: `1/2 == 0.5`. Although this is the default in Python 3, it is a trivial way to help this notebook work in Python 2 if that's what you are using." ] }, { "cell_type": "code", "collapsed": false, "input": [ "from __future__ import division, print_function\n", "\n", "import numpy as np\n", "import scipy as sp\n", "import matplotlib as mpl\n", "import matplotlib.pyplot as plt\n", "\n", "#IPython magic command for inline plotting\n", "%matplotlib inline\n", "#a better plot shape for IPython\n", "mpl.rcParams['figure.figsize']=[15,3]" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## Plotting in 2D: `matplotlib`\n", "\n", "Thus far you have seen the use of a cryptic (or not-so-cryptic) `plt.plot` command many times. Its syntax is quite similar to the MATLAB `plot` command; indeed, you can even load it with `from matplotlib.pyplot import plot` to save yourself much typing in the future.\n", "\n", "`plot` displays coordinate pairs in a number of formats. It is currently the workhorse of 2D plotting in Python, and the MATLAB-like syntax will make basic manipulation accessible to many engineers.\n", "\n", "There are a number of alternative display commands as well which tweak the output: `fill` and `errorbar`, for instance. The MatPlotLib developers maintain a [gallery](http://matplotlib.org/gallery.html) of examples with full source code.\n", "\n", "You have access to the [entire palette](http://matplotlib.org/api/colors_api.html) of modern systems as well." ] }, { "cell_type": "code", "collapsed": false, "input": [ "x = np.linspace(0, 1, 10001)\n", "y = np.cos(np.pi/x) * np.exp(-2*x)\n", "\n", "plt.plot(x, y)\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": true, "input": [ "plt.plot(x, y, 'r--')" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "plt.plot(x, y, 'g-', linewidth=3)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "plt.plot(x, y, linewidth=0.5, color='burlywood')" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "plt.plot(x, y, linewidth=5, color='#d1d3d4')\n", "plt.xlim(0, 1)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 4, "metadata": {}, "source": [ "Exercise" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* Plot the following equations over the domain $y \\in \\left[-1, 2\\right]$.\n", " * $y = f(x) = x^2 \\exp(-x)$\n", " * $y = f(x) = \\log x$\n", " * $y = f(x) = 1 + x^x + 3 x^4$" ] }, { "cell_type": "code", "collapsed": false, "input": [ "x = np.linspace(-1,2,1000)\n", "y = x**2*np.log(x)\n", "plt.plot(x,y)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "### Color" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Named MPL colors\n", "for c in mpl.colors.cnames:\n", " print(c, mpl.colors.cnames[c])" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "x = np.linspace(0, 10, 21)\n", "y = 10*np.exp(-x)\n", "yerr = np.random.rand(len(x)) * 2*np.exp(-x/2)\n", "\n", "plt.errorbar(x, y, yerr=yerr)\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "x = np.linspace(0, 2*np.pi, 1001)\n", "y = np.sin(2*x**2/np.pi)\n", "\n", "plt.fill(x, y)\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "x = np.arange(0.1, 4, 0.5)\n", "y = np.exp(-x)\n", "\n", "y_err = 0.1 + 0.2 * x\n", "x_err_lower = 0.4 * y_err\n", "x_err_upper = y_err\n", "x_err = [x_err_lower, x_err_upper]\n", "\n", "mpl.rcParams['figure.figsize']=[8,8]\n", "\n", "fig, (ax0, ax1) = plt.subplots(nrows=2, sharex=True)\n", "ax0.errorbar(x, y, yerr=y_err, fmt='-o')\n", "ax0.set_title('variable, symmetric error')\n", "\n", "ax1.errorbar(x, y, xerr=x_err, fmt='o')\n", "ax1.set_title('variable, asymmetric error')\n", "ax1.set_yscale('log')\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "from numpy.random import normal\n", "n = (1000,1)\n", "x = normal(size=n)\n", "\n", "avg = np.mean(x)\n", "std = np.std(x)\n", "\n", "x_avg = np.ones(n)* avg\n", "x_stdl = np.ones(n)*(avg-std)\n", "x_stdh = np.ones(n)*(avg+std)\n", "\n", "plt.plot(x,'bx',x_avg,'r-',x_stdl,'r--',x_stdh,'r--')\n", "plt.title('%d Random Gaussian Numbers' % n[0]);\n", "plt.xlabel(r'$n$');\n", "plt.ylabel(r'$U(n)$')\n", "plt.show()\n", "\n", "plt.hist(x,bins=20)\n", "plt.title('Distribution of %d Random Gaussian Numbers' % n[0]);\n", "plt.xlabel(r'$U(n)$');\n", "plt.ylabel(r'Frequency of $U$')\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "def func(x, y):\n", " return x*(1-x)*np.cos(4*np.pi*x) * np.sin(2*np.pi*np.sqrt(y))\n", "\n", "# Define the basic grid coordinates.\n", "grid_x, grid_y = np.mgrid[0:1:250j, 0:1:250j]\n", "\n", "# Define a random subset of the grid for which we will generate data.\n", "pts = np.random.rand(800,2)\n", "vals = func(pts[:,0], pts[:,1])\n", "\n", "# Load the methods and generate a grid for each approach.\n", "from scipy.interpolate import griddata\n", "grid_z0 = griddata(pts, vals, (grid_x, grid_y), method='nearest')\n", "grid_z1 = griddata(pts, vals, (grid_x, grid_y), method='linear')\n", "grid_z2 = griddata(pts, vals, (grid_x, grid_y), method='cubic')\n", "\n", "from matplotlib import cm\n", "plt.subplot(221)\n", "plt.imshow(func(grid_x, grid_y).T, extent=(0,1,0,1), origin='lower', cmap=cm.PiYG)\n", "plt.plot(pts[:,0], pts[:,1], 'k.', ms=1)\n", "plt.title('Original')\n", "\n", "plt.subplot(222)\n", "plt.imshow(grid_z0.T, extent=(0,1,0,1), origin='lower', cmap=cm.PiYG)\n", "plt.title('Nearest')\n", "\n", "plt.subplot(223)\n", "plt.imshow(grid_z1.T, extent=(0,1,0,1), origin='lower', cmap=cm.PiYG)\n", "plt.title('Linear')\n", "\n", "plt.subplot(224)\n", "plt.imshow(grid_z2.T, extent=(0,1,0,1), origin='lower', cmap=cm.PiYG)\n", "plt.title('Cubic')\n", "\n", "plt.gcf().set_size_inches(12, 12)\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- Play around with some other functions in the cell above's `func`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "### Professional Plotting\n", "\n", "Using standard commands, you can easily design publication-quality output. (This is one place where an interactive shell like IPython really shines.) You can save the figures as high-resolution PNGs (or other file types). You can even use $\\LaTeX$ markup in raw Python strings to yield mathematical formulae in labels, titles, and legends." ] }, { "cell_type": "code", "collapsed": false, "input": [ "mpl.rcParams['figure.figsize']=[15,3]" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "from numpy.polynomial import Chebyshev as T\n", "from numpy.polynomial import Polynomial as P\n", "x = np.linspace(-1, 1, 100)\n", "for i in range(6):\n", " ax = plt.plot(x, T.basis(i)(x), lw=2, label=r'$T_%d$'%i)\n", " print(r'T_%d(x) = %s'%(i, T.basis(i).convert(kind=P)))\n", "plt.legend(loc=\"upper left\")\n", "plt.savefig('chebyshev.png', dpi=120, transparent=True)\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "from numpy.polynomial import Laguerre as L\n", "x = np.linspace(0, 4, 100)\n", "for i in range(6):\n", " ax = plt.plot(x, L.basis(i)(x), lw=2, label=r'$L_%d$'%i)\n", " print(r'L_%d(x) = %s'%(i, L.basis(i).convert(kind=P)))\n", "plt.legend(loc=\"lower left\",ncol=3)\n", "plt.savefig('laguerre.png', dpi=120, transparent=True)\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- Experiment with the following $\\LaTeX$ labels in the `label` argument in the following code block. (They won't describe the figure, but whatever...)\n", " - `r'A_i^j (x)$'`\n", " - `r'$(1 - x_0) \\cdot x^{x^x}$'`\n", " - `r'$\\frac{x + y}{x^y}$'`\n", " - `r'$\\int_0^\\infty \\exp(-x) dx$'`\n", " - `r'$\\sum_{n=0}^{10} \\frac{x}{x-5}$'`\n", " - `r'$\\pi = %f$'%np.pi`" ] }, { "cell_type": "code", "collapsed": false, "input": [ "x = np.linspace(0, 2*np.pi, 101)\n", "plt.plot(x, np.sin(x), lw=2, label=r'$L_i$')\n", "plt.legend(loc=\"lower left\", prop={'size':24})\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To get plots really looking sharp, you need to adjust the labels, font sizes, typefaces, and other plot properties for readability." ] }, { "cell_type": "code", "collapsed": false, "input": [ "from scipy.special import jv, yv, iv, kv, jn_zeros\n", "x = np.linspace(0, 6, 201)\n", "y = np.zeros((8, 201))\n", "y[0,:] = jv(0, x)\n", "y[1,:] = jv(1, x)\n", "y[2,:] = yv(0, x)\n", "y[3,:] = yv(1, x)\n", "y[4,:] = iv(0, x)\n", "y[5,:] = iv(1, x)\n", "y[6,:] = kv(0, x)\n", "y[7,:] = kv(1, x)\n", "\n", "mpl.rcParams['figure.figsize']=[15,6]" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "fig = plt.figure()\n", "ax = fig.add_subplot(111)\n", "ax.plot(x, y[0], 'r-', lw=2, label=r'$J_0(x)$')\n", "ax.plot(x, y[1], 'r--', lw=2, label=r'$J_1(x)$')\n", "ax.plot(x, y[2], 'b-', lw=2, label=r'$Y_0(x)$')\n", "ax.plot(x, y[3], 'b--', lw=2, label=r'$Y_1(x)$')\n", "ax.plot(x, y[4], 'g-', lw=2, label=r'$I_0(x)$')\n", "ax.plot(x, y[5], 'g--', lw=2, label=r'$I_1(x)$')\n", "ax.plot(x, y[6], 'y-', lw=2, label=r'$K_0(x)$')\n", "ax.plot(x, y[7], 'y--', lw=2, label=r'$K_1(x)$')\n", "\n", "ax.set_title(r'Examples of Zeroth- and First-Order Bessel Functions', fontsize=24, family='serif')\n", "ax.set_ylabel(r'$f(x)$', fontsize=18)\n", "ax.set_xlabel(r'$x$', fontsize=18)\n", "ax.set_ylim((-1, 2))\n", "\n", "from matplotlib import font_manager as fm\n", "ticks_font = fm.FontProperties(family='serif', size=14)\n", "for label in ax.get_xticklabels(): label.set_fontproperties(ticks_font)\n", "for label in ax.get_yticklabels(): label.set_fontproperties(ticks_font)\n", "\n", "ax.legend(loc='best', prop={'size':18}, ncol=4) #you often have to oversize LaTeX code to make it look right in MPL\n", "\n", "plt.savefig('bessel.png', dpi=120, transparent=True)\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are a few situations when you may want your plots to be in black and white, such as publication in a journal or when your article may be photocopied." ] }, { "cell_type": "code", "collapsed": false, "input": [ "# the only difference here is that we set the lines to grayscale for publication purposes\n", "fig = plt.figure()\n", "ax = fig.add_subplot(111)\n", "ax.plot(x, y[0], 'k-', lw=2, label=r'$J_0(x)$')\n", "ax.plot(x, y[1], 'k--', lw=2, label=r'$J_1(x)$')\n", "ax.plot(x, y[2], 'k.', lw=2, label=r'$Y_0(x)$')\n", "ax.plot(x, y[3], 'k:', lw=2, label=r'$Y_1(x)$')\n", "ax.plot(x, y[4], '-', color=(0.75,0.75,0.75), lw=2, label=r'$I_0(x)$')\n", "ax.plot(x, y[5], '--', color=(0.75,0.75,0.75), lw=2, label=r'$I_1(x)$')\n", "ax.plot(x, y[6], '.', color=(0.75,0.75,0.75), lw=2, label=r'$K_0(x)$')\n", "ax.plot(x, y[7], ':', color=(0.75,0.75,0.75), lw=2, label=r'$K_1(x)$')\n", "\n", "ax.set_title(r'Examples of Zeroth- and First-Order Bessel Functions', fontsize=24, family='serif')\n", "ax.set_ylabel(r'$f(x)$', fontsize=18)\n", "ax.set_xlabel(r'$x$', fontsize=18)\n", "ax.set_ylim((-1, 2))\n", "\n", "from matplotlib import font_manager as fm\n", "ticks_font = fm.FontProperties(family='serif', size=14)\n", "for label in ax.get_xticklabels(): label.set_fontproperties(ticks_font)\n", "for label in ax.get_yticklabels(): label.set_fontproperties(ticks_font)\n", "\n", "ax.legend(loc='best', prop={'size':18}, ncol=4) #you often have to oversize LaTeX code to make it look right in MPL\n", "\n", "plt.savefig('bessel.png', dpi=120, transparent=True)\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Additionally, accomodating color blindness is a common motivation for choosing a nonstandard palette.\n", "\n", "[ColorBrewer](http://colorbrewer2.org/#) presents color palettes with a number of filters such as print-friendliness and visibility for the color blind." ] }, { "cell_type": "code", "collapsed": false, "input": [ "fig = plt.figure()\n", "ax = fig.add_subplot(111, axisbg='darkslategray')\n", "ax.plot(x, y[0], '-', color='#b2182b', lw=2, label=r'$J_0(x)$')\n", "ax.plot(x, y[1], '-', color='#d6604d', lw=2, label=r'$J_1(x)$')\n", "ax.plot(x, y[2], '-', color='#f4a582', lw=2, label=r'$Y_0(x)$')\n", "ax.plot(x, y[3], '-', color='#fddbc7', lw=2, label=r'$Y_1(x)$')\n", "ax.plot(x, y[4], '-', color='#2166ac', lw=2, label=r'$I_0(x)$')\n", "ax.plot(x, y[5], '-', color='#4393c3', lw=2, label=r'$I_1(x)$')\n", "ax.plot(x, y[6], '-', color='#92c5de', lw=2, label=r'$K_0(x)$')\n", "ax.plot(x, y[7], '-', color='#d1e5f0', lw=2, label=r'$K_1(x)$')\n", "\n", "ax.set_title(r'Examples of Zeroth- and First-Order Bessel Functions', fontsize=24, family='serif')\n", "ax.set_ylabel(r'$f(x)$', fontsize=18)\n", "ax.set_xlabel(r'$x$', fontsize=18)\n", "ax.set_ylim((-1, 2))\n", "\n", "from matplotlib import font_manager as fm\n", "ticks_font = fm.FontProperties(family='serif', size=14)\n", "for label in ax.get_xticklabels(): label.set_fontproperties(ticks_font)\n", "for label in ax.get_yticklabels(): label.set_fontproperties(ticks_font)\n", "\n", "ax.legend(loc='best', prop={'size':18}, ncol=4) #you often have to oversize LaTeX code to make it look right in MPL\n", "\n", "plt.savefig('bessel.png', dpi=120, transparent=True)\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "### Animating Plots\n", "\n", "The modes of vibration of a drum are the orthogonal basis functions which uniquely describe any vibration on a struck drumhead." ] }, { "cell_type": "code", "collapsed": false, "input": [ "from mpl_toolkits.mplot3d import Axes3D\n", "from scipy.special import jn, jn_zeros\n", "mpl.rcParams['figure.figsize']=[12,8]\n", "\n", "def drumhead_height(n, k, distance, angle, t):\n", " nth_zero = jn_zeros(n, k)\n", " return np.cos(t)*np.cos(n*angle)*jn(n, distance*nth_zero[-1])\n", "\n", "# Define polar and cartesian coordinates for the drum.\n", "theta = np.r_[0:2*np.pi:50j]\n", "radius = np.r_[0:1:50j]\n", "x = np.array([r*np.cos(theta) for r in radius])\n", "y = np.array([r*np.sin(theta) for r in radius])\n", "\n", "radial_nodes = 2\n", "zeros = 3\n", "\n", "# Define the base plot.\n", "fig = plt.figure()\n", "\n", "# Display the desired angular nodes.\n", "ax = fig.add_subplot(1,1,1,projection='3d')\n", "z = np.array([drumhead_height(radial_nodes, zeros, r, theta, 0.5) for r in radius])\n", "ax.plot_surface(x,y,z,rstride=1,cstride=1,cmap=mpl.cm.jet)\n", "\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's display several at once." ] }, { "cell_type": "code", "collapsed": false, "input": [ "from mpl_toolkits.mplot3d import Axes3D\n", "from scipy.special import jn, jn_zeros\n", "import subprocess\n", "\n", "def drumhead_height(n, k, distance, angle, t):\n", " nth_zero = jn_zeros(n, k)\n", " return np.cos(t)*np.cos(n*angle)*jn(n, distance*nth_zero[-1])\n", "\n", "# Define polar and cartesian coordinates for the drum.\n", "theta = np.r_[0:2*np.pi:50j]\n", "radius = np.r_[0:1:50j]\n", "x = np.array([r*np.cos(theta) for r in radius])\n", "y = np.array([r*np.sin(theta) for r in radius])\n", "\n", "radial_nodes = 3\n", "zeros = 2\n", "\n", "# Define the base plot.\n", "fig = plt.figure(num=None,figsize=(16,16),dpi=120,facecolor='w',edgecolor='k')\n", "ax = list()\n", "\n", "# Loop over the desired angular nodes.\n", "cnt = 0\n", "pixcnt = 0\n", "\n", "t = 0\n", "cnt = 0\n", "pixcnt += 1\n", "for i in np.r_[0:radial_nodes+1:1]:\n", " for j in np.r_[1:zeros+1:1]:\n", " cnt += 1;\n", " ax.append(fig.add_subplot(radial_nodes+1,zeros,cnt,projection='3d'))\n", " z = np.array([drumhead_height(i, j, r, theta, t) for r in radius])\n", " ax[-1].set_xlabel('R@%d,A@%d' % (i,j))\n", " ax[-1].plot_surface(x,y,z,rstride=1,cstride=1,cmap=mpl.cm.Accent,linewidth=0,vmin=-1,vmax=1)\n", " ax[-1].set_zlim(-1,1)\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We could then write them to disk sequentially. (**Warning**: this takes several minutes to complete.)" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from mpl_toolkits.mplot3d import Axes3D\n", "from scipy.special import jn, jn_zeros\n", "import subprocess\n", "\n", "def drumhead_height(n, k, distance, angle, t):\n", " nth_zero = jn_zeros(n, k)\n", " return np.cos(t)*np.cos(n*angle)*jn(n, distance*nth_zero[-1])\n", "\n", "# Define polar and cartesian coordinates for the drum.\n", "theta = np.r_[0:2*np.pi:50j]\n", "radius = np.r_[0:1:50j]\n", "x = np.array([r*np.cos(theta) for r in radius])\n", "y = np.array([r*np.sin(theta) for r in radius])\n", "\n", "radial_nodes = 3\n", "zeros = 2\n", "\n", "# Define the base plot.\n", "fig = plt.figure(num=None,figsize=(16,16),dpi=120,facecolor='w',edgecolor='k')\n", "ax = list()\n", "\n", "# Loop over the desired angular nodes.\n", "cnt = 0\n", "pixcnt = 0\n", "#plt.ion()\n", "for t in np.r_[0:2*np.pi:40j]:\n", " cnt = 0\n", " pixcnt += 1\n", " for i in np.r_[0:radial_nodes+1:1]:\n", " for j in np.r_[1:zeros+1:1]:\n", " cnt += 1;\n", " ax.append(fig.add_subplot(radial_nodes+1,zeros,cnt,projection='3d'))\n", " z = np.array([drumhead_height(i, j, r, theta, t) for r in radius])\n", " ax[-1].set_xlabel('R@%d,A@%d' % (i,j))\n", " ax[-1].plot_surface(x,y,z,rstride=1,cstride=1,cmap=mpl.cm.Accent,linewidth=0,vmin=-1,vmax=1)\n", " ax[-1].set_zlim(-1,1)\n", " #plt.show()\n", " plt.savefig('./drum-modes-%d.png' % pixcnt, format='png')" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, we could collect them as an animated GIF using the following code." ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Collate pictures to an animated GIF.\n", "import os,string\n", "cwd = os.getcwd()\n", "cmd = 'cd %s; ls drum-modes*.png | sort -k1.12n'%cwd\n", "png_files = os.popen(cmd)\n", "png_files_list = string.join(png_files.readlines()).replace('\\n',' ')\n", "os.popen('convert -delay 10 -loop 1 %s ./drum-animate.gif'%png_files_list)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "![](https://raw.githubusercontent.com/uiuc-cse/python-sp15/gh-pages/lessons/img/drum-animate.gif)\n", "\n", "There are also ways to [generate animated plots](http://matplotlib.org/api/animation_api.html) in MatPlotLib, but that's beyond the scope of this tutorial.\n", "\n", "If you are more interested in 3D plotting, please take a look at [Mayavi](code.enthought.com/projects/mayavi/)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## Prettier Plotting (`seaborn`)\n", "\n", "MatPlotLib is a stable basis for plotting in Python, but for domain applications or specialized needs it can fall short. In addition, the default settings (colormaps, etc.) are not as attractive as they could be. One solutions to these problems builds directly on MPL:\n", "- [Seaborn](https://github.com/mwaskom/seaborn) is designed to create high-level statistical graphics with a high degree of customizability.\n", "\n", "To install `seaborn` in Canopy, please open a new terminal window and enter the following (on the EWS machines):\n", "\n", " module load canopy\n", " canopy\n", "\n", "This will open the interface where you can log in and install new packages, including `seaborn`." ] }, { "cell_type": "code", "collapsed": false, "input": [ "import numpy as np\n", "import seaborn as sns\n", "import matplotlib.pyplot as plt\n", "%matplotlib inline\n", "np.random.seed(sum(map(ord, \"palettes\")))" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "### Palettes\n", "\n", "As mentioned, one criticism which has been levelled against MatPlotLib is that the default color palettes leave much to be desired. Seaborn has a number of built-in basic palettes available which provide a pleasant aesthetic." ] }, { "cell_type": "code", "collapsed": false, "input": [ "current_palette = sns.color_palette()\n", "print(current_palette)\n", "sns.palplot(current_palette)\n", "\n", "gammas = sns.load_dataset(\"gammas\")\n", "plt.figure(figsize=(12, 6))\n", "sns.tsplot(gammas, \"timepoint\", \"subject\", \"ROI\", \"BOLD signal\", color=current_palette);" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Removing chart junk (grid lines, etc.) is as simple as `despine()`:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "sns.set(style='ticks', palette='Set2')\n", "sns.tsplot(gammas, \"timepoint\", \"subject\", \"ROI\", \"BOLD signal\", color=current_palette);\n", "sns.despine()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can use built-in Seaborn palettes or pull from MatPlotLib [colormaps](http://matplotlib.org/examples/color/colormaps_reference.html?highlight=cmap)." ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Built-in Seaborn variations\n", "sns.palplot(sns.color_palette(\"pastel\", 8))\n", "sns.palplot(sns.color_palette(\"deep\", 8))\n", "sns.palplot(sns.color_palette(\"dark\", 8))\n", "sns.palplot(sns.color_palette(\"colorblind\", 8))\n", "\n", "# Selections from MPL colormaps.\n", "sns.palplot(sns.color_palette(\"coolwarm\", 8))\n", "sns.palplot(sns.color_palette(\"gist_earth\", 8))\n", "sns.palplot(sns.color_palette(\"Paired\", 10))\n", "sns.palplot(sns.color_palette(\"RdPu_r\", 8))" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`blend_palette()` allows you to interpolate between an arbitrary number of specified MatPlotLib colors." ] }, { "cell_type": "code", "collapsed": false, "input": [ "sns.palplot(sns.blend_palette([\"indigo\", \"#fff3f4\",\"royalblue\"], 12))" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are [a few more ways](http://stanford.edu/~mwaskom/software/seaborn/tutorial/color_palettes.html) to generate and use palettes in both MatPlotLib and Seaborn, but this should give you a good feel for the flexibility and ease-of-use of this way of working with color." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, palettes are invoked using the `color` `kwarg` in Seaborn:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "plt.figure(figsize=(12, 6))\n", "sns.tsplot(gammas, \"timepoint\", \"subject\", \"ROI\", \"BOLD signal\", color=sns.color_palette(\"RdPu_r\", 3));" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## More Professional Plotting\n", "\n", "- Output plots as vector images (`eps` (preferred) or `svg`) as well as raster (`png` (preferred), `gif`, or `jpg`). This allows you to generate new versions with more detail if necessary. Also have a clearly documented section of code and data for each image in order to reproduce images as well if necessary." ] }, { "cell_type": "code", "collapsed": false, "input": [ "from numpy.polynomial import Chebyshev as T\n", "from numpy.polynomial import Polynomial as P\n", "x = np.linspace(-1, 1, 100)\n", "for i in range(6):\n", " ax = plt.plot(x, T.basis(i)(x), lw=2, label=r'$T_%d$'%i)\n", "plt.legend(loc=\"upper left\")\n", "plt.savefig('chebyshev.jpg', dpi=120, transparent=True)\n", "plt.savefig('chebyshev.png', dpi=120, transparent=True)\n", "plt.savefig('chebyshev.eps', dpi=120, transparent=True)\n", "plt.savefig('chebyshev.svg', dpi=120, transparent=True)\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- Prefer figures to tables. (Tabular data can be included in an appendix if necessary.) Data comprehension is much better when viewing graphical representations." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- Consider utilizing a CUBEHELIX palette in documents which may be printed or accessed in black and white[[citation](http://adsabs.harvard.edu/abs/2011BASI...39..289G)]; [[tutorial](http://www.mrao.cam.ac.uk/~dag/CUBEHELIX/)]. The CUBEHELIX algorithm generates color palettes which continuously increase (or decrease) in intensity regardless of the hue, thus making them acceptable to print or view in monochrome and for individuals with any form of colorblindness. They also stand up well in desaturated environments like lousy projectors in overbright rooms. The basic CUBEHELIX palette is available in MatPlotLib and thus any of its derivatives; the module [`cubehelix`](https://github.com/jradavenport/cubehelix) is also available to generate more variations.\n", "\n", "Basically, the CUBEHELIX algorithm desaturates consistently through a given color space ([more info here](http://stackoverflow.com/a/15623251/1272411)).\n", "\n", "![](http://i.stack.imgur.com/3Izz0.jpg)" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from scipy.special import j0\n", "def func(x, y):\n", " return j0(8*np.pi*x) * np.sin(2*np.pi*np.sqrt(y))\n", "\n", "# Define the basic grid coordinates.\n", "grid_x, grid_y = np.mgrid[0:0.2:250j, 0:0.4:250j]\n", "\n", "from matplotlib import cm\n", "plt.subplot('121')\n", "plt.imshow(func(grid_x, grid_y).T, extent=(0,0.2,0,0.4), origin='lower', cmap=cm.cubehelix_r)\n", "plt.title(r'$z(x, y) = J_{0}(8\\pi x) \\sin(2 \\pi \\sqrt{y})$')\n", "plt.xlabel(r'$x$')\n", "plt.ylabel(r'$y$')\n", "\n", "import cubehelix\n", "plt.subplot('122')\n", "plt.imshow(func(grid_x, grid_y).T, extent=(0,0.2,0,0.4), origin='lower', cmap=cubehelix.cmap(reverse=True, start=0.3, rot=0.5))\n", "plt.title(r'$z(x, y) = J_{0}(8\\pi x) \\sin(2 \\pi \\sqrt{y})$')\n", "plt.xlabel(r'$x$')\n", "plt.ylabel(r'$y$')\n", "\n", "\n", "plt.gcf().set_size_inches(8, 4)\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## References\n", "\n", "- Langtangen, Hans Petter. _Python Scripting for Computational Science_, 3ed. Berlin\u2013Heidelberg: Springer\u2013Verlag, 2009.\n", "- Lugo, Michael. [On propagation of errors](http://gottwurfelt.com/2012/03/26/on-propagation-of-errors/). 26 March 2012.\n", "- Warren, Russell. [A Brief Intro to Profiling in Python](https://speakerdeck.com/rwarren/a-brief-intro-to-profiling-in-python). Ottawa Python Authors Group, 28 February 2013.\n", "\n", "### Seaborn\n", "- Waskon, Michael. [Choosing color palettes](http://stanford.edu/~mwaskom/software/seaborn/tutorial/color_palettes.html).\n", "- Brewer, Cynthia. [ColorBrewer](http://colorbrewer2.org/)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "\n", "## Credits\n", "\n", "Neal Davis and Lakshmi Rao developed these materials for [Computational Science and Engineering](http://cse.illinois.edu/) at the University of Illinois at Urbana\u2013Champaign.\n", "\n", "\n", "This content is available under a [Creative Commons Attribution 4.0 Unported License](https://creativecommons.org/licenses/by/4.0/).\n", "\n", "[![](https://bytebucket.org/davis68/resources/raw/f7c98d2b95e961fae257707e22a58fa1a2c36bec/logos/baseline_cse_wdmk.png?token=be4cc41d4b2afe594f5b1570a3c5aad96a65f0d6)](http://cse.illinois.edu/)" ] } ], "metadata": {} } ] }