{ "metadata": { "name": "", "signature": "sha256:37ce6e4f889e485d7cb1cf4d711e4623794906520fc8f273cfca8a6761d8f4c1" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ " Graphics and Equation Typesetting" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Some of this tour is modeled on the Matplotlib [pyplot tutorial](http://matplotlib.org/users/pyplot_tutorial.html), the [SciPy lecture notes on plotting](https://scipy-lectures.github.io/intro/matplotlib/matplotlib.html), Jake Vanderplas' [Matplotlib Intro](http://nbviewer.ipython.org/github/jakevdp/2013_fall_ASTR599/tree/master/http://nbviewer.ipython.org/github/jakevdp/2013_fall_ASTR599/blob/master/notebooks/06_MatplotlibIntro.ipynb) and the [Software Carpentry Bootcamp lesson](http://nbviewer.ipython.org/github/swcarpentry/2012-11-scripps/blob/master/python/matplotlib-full.ipynb) on Matplotlib." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Instructions:** Create a new directory called `Graphics` with a new notebook called `GraphicsTour`. Give it a heading 1 cell title **Graphics and Equation Typesetting**. Read this page, typing in the code in the code cells and executing them as you go. \n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "Do not copy/paste. \n", "\n", "Type the commands yourself to get the practice doing it. This will also slow you down so you can think about the commands and what they are doing as you type them.\n", "\n", "Save your notebook when you are done, then try the accompanying exercises.\n", "\n", "---" ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Matplotlib" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Matplotlib is probably the single most used Python package for 2D-graphics. It provides both a very quick way to visualize data from Python and publication-quality figures in many formats. \n", "\n", "This notebook will provide a brief summary of the most important aspects of Matplotlib for our class, but you are encouraged to explore the extensive [documentation](http://matplotlib.org/contents.html) for the library and in particular, the [gallery](http://matplotlib.org/gallery.html) of plots. \n", "\n", "No one can keep all of the functions and fine layout control commands in their brain. Often when I need to make a plot, I go to the gallery page and browse the images until I find one that is similar to what I want to create and then I copy the code and modify it to suit my needs. You are encouraged to do the same." ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Importing the library" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To import the parts of Matplotlib that we will need, we use" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# In iPython or the iPython notebook, it's easiest to use the pylab magic, which\n", "# imports matplotlib, numpy, and scipy.\n", "\n", "# The inline flag means that images will be shown here in the notebooks, rather\n", "# than in pop-up windows.\n", "\n", "%pylab inline\n", "\n", "# If you are using 'regular' Python, however, you'll want the following. You'll\n", "# need to also separately import numpy and any other packages that you might need.\n", "\n", "#import matplotlib.pyplot as plt\n", "#import numpy as np" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Creating figures" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are two major challenges with creating figures. First is understanding the syntax to actually make the basic plot appear. Second is formatting the basic plot to look exactly how you would like it to look. **In general, the formatting will probably take you longer...**\n", "\n", "Within Matplotlib's `pyplot` module (currently imported as '`plt`'), there are two basic ways to go about making plots - using the Matlab-like clone, and using the object-oriented approach. The latter provides better control over plot features, while only requiring slightly more typing. We will use a little bit of both here. The Matlab-clone syntax is good for quick and dirty, fast and simple plotting, while the object-oriented syntax is better for refining your plots to make them \"publication-ready\".\n", "\n", "**Note:** When you look at the source code from the Matplotlib gallery, the examples will mostly be using the object-oriented syntax." ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Simple Plots" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here is an example of creating a simple plot of two functions. To plot a function, we need to create a NumPy array for the independent variable ($x$) and then set the dependent variable ($y = f(x)$) by using NumPy's built-in functions. " ] }, { "cell_type": "code", "collapsed": false, "input": [ "#create the data to be plotted\n", "x = np.linspace(0, 2*np.pi, 300)\n", "y = np.sin(x)\n", "y2 = np.sin(x**2)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this case, `x` is now a NumPy array with 300 values ranging from 0 to 2$\\pi$ (included). `y` is the sine (array of 300 values) and `y2` is the square of the sine (array of 300 values) at each of those 300 `x` values." ] }, { "cell_type": "code", "collapsed": false, "input": [ "#Now plot it\n", "plt.plot(x, y) \n", "plt.plot(x, y2)\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What this plot lacks is important information about what is in it - there are no axis labels, no legend telling us what is different about blue vs. green, and the size of the font on the axis labels is a bit small. We should probably try to improve the plot's readability so other people viewing it will understand what we are trying to convey." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Font size for labels is especially important when you are creating plots for inclusion in a slide presentation.** If your labels are too small for the audience to read, you will quickly lose their attention. \n", "\n", "**Our demos during the final exam are a perfect place to practice creating readable plots. Grading of the demos will include an evaluation of how well information is presented, including readability of plots.**\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We'll explore some of the capabilities in Matplotlib for refining how our data is presented graphically below, but first, we'll look at a couple more simple tricks." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can control the style, color and other properties of the markers, for example:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "plt.plot(x, y, linewidth=2);\n", "plt.plot(x, y2, linewidth=2);" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "#decrease the number of points to illustrate the use of markers\n", "x = np.linspace(0, 2*np.pi, 50)\n", "y = np.sin(x)\n", "y2 = np.cos(x)\n", "plt.plot(x, y, 'o', markersize=5, color='r');\n", "plt.plot(x, y2, '^', markersize=5, color='b');" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "See the Matplotlib [line style demo](http://matplotlib.org/examples/pylab_examples/line_styles.html) to view the different types of line styles you can choose from. The source code is a little \"slick\", though, so it may not be obvious to you how the markers are set in that example." ] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Saving a figure" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To save a figure in a standard format such as `png`, `jpg` or `pdf`, just call the `savefig` method with a filename that includes the extension for the type of file you wish to create:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "#back to our original data\n", "x = np.linspace(0, 2*np.pi, 300)\n", "y = np.sin(x)\n", "y2 = np.sin(x**2)\n", "\n", "plt.plot(x, y)\n", "plt.plot(x, y2)\n", "\n", "#add a grid\n", "plt.grid()\n", "\n", "plt.savefig(\"Example.pdf\")\n", "plt.savefig(\"Example.png\")" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "#check that they exist\n", "!ls -l Example.*" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Refining our plots" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that we've seen how to create some data and plot it, we'll iteratively improve on the look of the plots as we explore features of Matplotlib. Start with something simple: draw the cosine/sine functions." ] }, { "cell_type": "code", "collapsed": false, "input": [ "#create the data\n", "x = np.linspace(-np.pi, np.pi, 56, endpoint=True)\n", "c, s = np.cos(x), np.sin(x)" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "plt.plot(x, c)\n", "plt.plot(x, s)\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Matplotlib comes with a set of default settings that allow customizing all kinds of properties. You can control the defaults of almost every property with Matplotlib: figure size and dpi, line width, color and style, axes, axis and grid properties, text and font properties and so on." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can explicitly set all of the parameters that define the plot when we create it. Here are the default settings, explicitly shown. \n", "\n", "**Play around with them to see how they change the look of the plot.**" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Create a figure of size 8x6 points, 80 dots per inch\n", "plt.figure(figsize=(8, 6), dpi=80)\n", "\n", "# Create a new subplot from a grid of 1x1\n", "plt.subplot(1, 1, 1)\n", "\n", "# Plot cosine with a blue continuous line of width 1 (pixels)\n", "plt.plot(x, c, color=\"blue\", linewidth=1.0, linestyle=\"-\")\n", "\n", "# Plot sine with a green continuous line of width 1 (pixels)\n", "plt.plot(x, s, color=\"green\", linewidth=1.0, linestyle=\"-\")\n", "\n", "# Set x limits\n", "plt.xlim(-4.0, 4.0)\n", "\n", "# Set x ticks\n", "plt.xticks(np.linspace(-4, 4, 9, endpoint=True))\n", "\n", "# Set y limits\n", "plt.ylim(-1.0, 1.0)\n", "\n", "# Set y ticks\n", "plt.yticks(np.linspace(-1, 1, 5, endpoint=True))\n", "\n", "# Save figure using 72 dots per inch\n", "plt.savefig(\"plot_example.png\", dpi=72)\n", "\n", "# Show result on screen\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Changing colors and line widths" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First step, we want to have the cosine in blue and the sine in red and a slighty thicker line for both of them. We\u2019ll also slightly alter the figure size to make it more horizontal." ] }, { "cell_type": "code", "collapsed": false, "input": [ "plt.figure(figsize=(10, 6), dpi=80)\n", "plt.plot(x, c, color=\"blue\", linewidth=2.5, linestyle=\"-\")\n", "plt.plot(x, s, color=\"red\", linewidth=2.5, linestyle=\"-\")\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As you follow along with each successive iteration below, feel free to cut and paste the old code from previous cells you've already typed in, and add the new code labeled between the identifiers:\n", "\n", " #-----Start new code\n", " \n", " #-----End new code" ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Setting limits" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Current limits of the figure are a bit too tight and we want to make some space in order to clearly see all data points." ] }, { "cell_type": "code", "collapsed": false, "input": [ "plt.figure(figsize=(10, 6), dpi=80)\n", "plt.plot(x, c, color=\"blue\", linewidth=2.5, linestyle=\"-\")\n", "plt.plot(x, s, color=\"red\", linewidth=2.5, linestyle=\"-\")\n", "\n", "#------Start new code\n", "plt.xlim(x.min() * 1.1, x.max() * 1.1)\n", "plt.ylim(c.min() * 1.1, c.max() * 1.1)\n", "#------End new code\n", "\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Setting ticks" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Current ticks are not ideal because they do not show the interesting values ($\\pm\\pi$,$\\pm\\pi/2$) for sine and cosine. We\u2019ll change them such that they show only these values." ] }, { "cell_type": "code", "collapsed": false, "input": [ "plt.figure(figsize=(10, 6), dpi=80)\n", "plt.plot(x, c, color=\"blue\", linewidth=2.5, linestyle=\"-\")\n", "plt.plot(x, s, color=\"red\", linewidth=2.5, linestyle=\"-\")\n", "plt.xlim(x.min() * 1.1, x.max() * 1.1)\n", "plt.ylim(c.min() * 1.1, c.max() * 1.1)\n", "\n", "#-----Start new code\n", "plt.xticks([-np.pi, -np.pi/2, 0, np.pi/2, np.pi])\n", "plt.yticks([-1, 0, +1])\n", "#-----End new code\n", "\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Setting tick labels" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ticks are now properly placed but their label is not very explicit. We could guess that 3.142 is $\\pi$ but it would be better to make it explicit. When we set tick values, we can also provide a corresponding label in the second argument list. Note that we\u2019ll use $\\LaTeX$ (more on that at the end of this tour) to allow for nice rendering of the label." ] }, { "cell_type": "code", "collapsed": false, "input": [ "plt.figure(figsize=(10, 6), dpi=80)\n", "plt.plot(x, c, color=\"blue\", linewidth=2.5, linestyle=\"-\")\n", "plt.plot(x, s, color=\"red\", linewidth=2.5, linestyle=\"-\")\n", "plt.xlim(x.min() * 1.1, x.max() * 1.1)\n", "plt.ylim(c.min() * 1.1, c.max() * 1.1)\n", "\n", "#-----Start new code\n", "plt.xticks([-np.pi, -np.pi/2, 0, np.pi/2, np.pi],\n", " [r'$-\\pi$', r'$-\\pi/2$', r'$0$', r'$+\\pi/2$', r'$+\\pi$'])\n", "\n", "plt.yticks([-1, 0, +1],\n", " [r'$-1$', r'$0$', r'$+1$'])\n", "#-----End new code\n", "\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Note:** To get the $\\pi$ symbol, we used syntax from the $\\LaTeX$ typesetting library, which is widely used in physics and math for creating high quality publications, presentations and equation formatting. We will learn more about it later on in this notebook." ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Moving spines" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Spines are the lines connecting the axis tick marks and noting the boundaries of the data area. They can be placed at arbitrary positions and until now, they were on the border of the axis. We\u2019ll change that since we want to have them in the middle. Since there are four of them (top/bottom/left/right), we\u2019ll discard the top and right by setting their color to none and we\u2019ll move the bottom and left ones to coordinate 0 in data space coordinates." ] }, { "cell_type": "code", "collapsed": false, "input": [ "plt.figure(figsize=(10, 6), dpi=80)\n", "plt.plot(x, c, color=\"blue\", linewidth=2.5, linestyle=\"-\")\n", "plt.plot(x, s, color=\"red\", linewidth=2.5, linestyle=\"-\")\n", "plt.xlim(x.min() * 1.1, x.max() * 1.1)\n", "plt.ylim(c.min() * 1.1, c.max() * 1.1)\n", "\n", "#-----Start new code\n", "ax = plt.gca() # gca stands for 'get current axis'\n", "ax.spines['right'].set_color('none')\n", "ax.spines['top'].set_color('none')\n", "ax.xaxis.set_ticks_position('bottom')\n", "ax.spines['bottom'].set_position(('data',0))\n", "ax.yaxis.set_ticks_position('left')\n", "ax.spines['left'].set_position(('data',0))\n", "#-----End new code\n", "\n", "#The tick labels must come after the spine adjustment\n", "plt.xticks([-np.pi, -np.pi/2, 0, np.pi/2, np.pi],\n", " [r'$-\\pi$', r'$-\\pi/2$', r'$0$', r'$+\\pi/2$', r'$+\\pi$'])\n", "\n", "plt.yticks([-1, 0, +1],\n", " [r'$-1$', r'$0$', r'$+1$'])\n", "\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Adding a legend" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let\u2019s add a legend in the upper left corner. This only requires adding the keyword argument `label` (that will be used in the legend box) to the `plot` commands." ] }, { "cell_type": "code", "collapsed": false, "input": [ "plt.figure(figsize=(10, 6), dpi=80)\n", "plt.plot(x, c, color=\"blue\", linewidth=2.5, linestyle=\"-\")\n", "plt.plot(x, s, color=\"red\", linewidth=2.5, linestyle=\"-\")\n", "plt.xlim(x.min() * 1.1, x.max() * 1.1)\n", "plt.ylim(c.min() * 1.1, c.max() * 1.1)\n", "\n", "ax = plt.gca() # gca stands for 'get current axis'\n", "ax.spines['right'].set_color('none')\n", "ax.spines['top'].set_color('none')\n", "ax.xaxis.set_ticks_position('bottom')\n", "ax.spines['bottom'].set_position(('data',0))\n", "ax.yaxis.set_ticks_position('left')\n", "ax.spines['left'].set_position(('data',0))\n", "\n", "plt.xticks([-np.pi, -np.pi/2, 0, np.pi/2, np.pi],\n", " [r'$-\\pi$', r'$-\\pi/2$', r'$0$', r'$+\\pi/2$', r'$+\\pi$'])\n", "\n", "plt.yticks([-1, 0, +1],\n", " [r'$-1$', r'$0$', r'$+1$'])\n", "\n", "#-----Start new code\n", "plt.plot(x, c, color=\"blue\", linewidth=2.5, linestyle=\"-\", label=\"cosine\")\n", "plt.plot(x, s, color=\"red\", linewidth=2.5, linestyle=\"-\", label=\"sine\")\n", "plt.legend(loc='upper left')\n", "#-----End new code\n", "\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Note:** It was not actually necessary to repeat the plotting commands in lines 22 and 23, we could have just added labels to the `plot` commands the first time they were called in lines 2 and 3. But for clarity of presentation in showing the evolution of the plot as we iteratively add features, I repeated the commands." ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Annotate some points" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let\u2019s annotate some interesting points using the `annotate` command. We choose the 2$\\pi$/3 value and we want to annotate both the sine and the cosine. We\u2019ll first draw a marker on the curve as well as a straight dotted line. Then, we\u2019ll use the `annotate` command to display some text with an arrow. The text is also using $\\LaTeX$ formatting." ] }, { "cell_type": "code", "collapsed": false, "input": [ "plt.figure(figsize=(10, 6), dpi=80)\n", "plt.plot(x, c, color=\"blue\", linewidth=2.5, linestyle=\"-\")\n", "plt.plot(x, s, color=\"red\", linewidth=2.5, linestyle=\"-\")\n", "plt.xlim(x.min() * 1.1, x.max() * 1.1)\n", "plt.ylim(c.min() * 1.1, c.max() * 1.1)\n", "\n", "ax = plt.gca() # gca stands for 'get current axis'\n", "ax.spines['right'].set_color('none')\n", "ax.spines['top'].set_color('none')\n", "ax.xaxis.set_ticks_position('bottom')\n", "ax.spines['bottom'].set_position(('data',0))\n", "ax.yaxis.set_ticks_position('left')\n", "ax.spines['left'].set_position(('data',0))\n", "\n", "plt.xticks([-np.pi, -np.pi/2, 0, np.pi/2, np.pi],\n", " [r'$-\\pi$', r'$-\\pi/2$', r'$0$', r'$+\\pi/2$', r'$+\\pi$'])\n", "\n", "plt.yticks([-1, 0, +1],\n", " [r'$-1$', r'$0$', r'$+1$'])\n", "\n", "plt.plot(x, c, color=\"blue\", linewidth=2.5, linestyle=\"-\", label=\"cosine\")\n", "plt.plot(x, s, color=\"red\", linewidth=2.5, linestyle=\"-\", label=\"sine\")\n", "plt.legend(loc='upper left')\n", "\n", "#-----Start new code\n", "t = 2 * np.pi / 3\n", "plt.plot([t, t], [0, np.cos(t)], color='blue', linewidth=2.5, linestyle=\"--\")\n", "plt.scatter([t, ], [np.cos(t), ], 50, color='blue')\n", "\n", "plt.annotate(r'$sin(\\frac{2\\pi}{3})=\\frac{\\sqrt{3}}{2}$',\n", " xy=(t, np.sin(t)), xycoords='data',\n", " xytext=(+10, +30), textcoords='offset points', fontsize=16,\n", " arrowprops=dict(arrowstyle=\"->\", connectionstyle=\"arc3,rad=.2\"))\n", "\n", "plt.plot([t, t],[0, np.sin(t)], color='red', linewidth=2.5, linestyle=\"--\")\n", "plt.scatter([t, ],[np.sin(t), ], 50, color='red')\n", "\n", "plt.annotate(r'$cos(\\frac{2\\pi}{3})=-\\frac{1}{2}$',\n", " xy=(t, np.cos(t)), xycoords='data',\n", " xytext=(-90, -50), textcoords='offset points', fontsize=16,\n", " arrowprops=dict(arrowstyle=\"->\", connectionstyle=\"arc3,rad=.2\"))\n", "#-----End new code\n", "\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Cleaning up the details" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The tick labels are now hardly visible because of the blue and red lines. We can make them bigger and we can also adjust their properties such that they\u2019ll be rendered on a semi-transparent white background. This will allow us to see both the data and the labels. It might also be good to think about how our plot would look in black and white. Maybe we should change one of the linestyles." ] }, { "cell_type": "code", "collapsed": false, "input": [ "plt.figure(figsize=(10, 6), dpi=80)\n", "#-----Changed line style to \"--\"\n", "plt.plot(x, c, color=\"blue\", linewidth=2.5, linestyle=\"--\")\n", "#-----\n", "plt.plot(x, s, color=\"red\", linewidth=2.5, linestyle=\"-\")\n", "plt.xlim(x.min() * 1.1, x.max() * 1.1)\n", "plt.ylim(c.min() * 1.1, c.max() * 1.1)\n", "\n", "ax = plt.gca() # gca stands for 'get current axis'\n", "ax.spines['right'].set_color('none')\n", "ax.spines['top'].set_color('none')\n", "ax.xaxis.set_ticks_position('bottom')\n", "ax.spines['bottom'].set_position(('data',0))\n", "ax.yaxis.set_ticks_position('left')\n", "ax.spines['left'].set_position(('data',0))\n", "\n", "plt.xticks([-np.pi, -np.pi/2, 0, np.pi/2, np.pi],\n", " [r'$-\\pi$', r'$-\\pi/2$', r'$0$', r'$+\\pi/2$', r'$+\\pi$'])\n", "\n", "plt.yticks([-1, 0, +1],\n", " [r'$-1$', r'$0$', r'$+1$'])\n", "\n", "#-----Changed line style to \"--\"\n", "plt.plot(x, c, color=\"blue\", linewidth=2.5, linestyle=\"--\", label=\"cosine\")\n", "#-----\n", "plt.plot(x, s, color=\"red\", linewidth=2.5, linestyle=\"-\", label=\"sine\")\n", "plt.legend(loc='upper left', fontsize=20)\n", "\n", "t = 2 * np.pi / 3\n", "plt.plot([t, t], [0, np.cos(t)], color='blue', linewidth=2.5, linestyle=\"--\")\n", "plt.scatter([t, ], [np.cos(t), ], 50, color='blue')\n", "\n", "plt.annotate(r'$sin(\\frac{2\\pi}{3})=\\frac{\\sqrt{3}}{2}$',\n", " xy=(t, np.sin(t)), xycoords='data',\n", " xytext=(+10, +30), textcoords='offset points', fontsize=20,\n", " arrowprops=dict(arrowstyle=\"->\", connectionstyle=\"arc3,rad=.2\"))\n", "\n", "plt.plot([t, t],[0, np.sin(t)], color='red', linewidth=2.5, linestyle=\"--\")\n", "plt.scatter([t, ],[np.sin(t), ], 50, color='red')\n", "\n", "plt.annotate(r'$cos(\\frac{2\\pi}{3})=-\\frac{1}{2}$',\n", " xy=(t, np.cos(t)), xycoords='data',\n", " xytext=(-90, -50), textcoords='offset points', fontsize=20,\n", " arrowprops=dict(arrowstyle=\"->\", connectionstyle=\"arc3,rad=.2\"))\n", "\n", "#-----Start new code\n", "for label in ax.get_xticklabels() + ax.get_yticklabels():\n", " label.set_fontsize(20)\n", " label.set_bbox(dict(facecolor='white', edgecolor='None', alpha=0.65))\n", "#-----End new code\n", "\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That's a pretty nicely formatted figure, ready for publication or presentation. Lines are thick enough to see and distinguishable even in black and white, and the legend and labels are also large enough to read easily. Axis labels would also generally be wise to include, but in this case they are implicit from the legend and the values on the axes." ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Having fun" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And now for something really fun, \"[xkcd](http://xkcd.com/)-ify\" it:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "plt.figure(figsize=(10, 6), dpi=80)\n", "\n", "#-----Start new code\n", "plt.xkcd()\n", "#-----End new code\n", "\n", "plt.plot(x, c, color=\"blue\", linewidth=2.5, linestyle=\"--\")\n", "plt.plot(x, s, color=\"red\", linewidth=2.5, linestyle=\"-\")\n", "plt.xlim(x.min() * 1.1, x.max() * 1.1)\n", "plt.ylim(c.min() * 1.1, c.max() * 1.1)\n", "\n", "ax = plt.gca() # gca stands for 'get current axis'\n", "ax.spines['right'].set_color('none')\n", "ax.spines['top'].set_color('none')\n", "ax.xaxis.set_ticks_position('bottom')\n", "ax.spines['bottom'].set_position(('data',0))\n", "ax.yaxis.set_ticks_position('left')\n", "ax.spines['left'].set_position(('data',0))\n", "\n", "plt.xticks([-np.pi, -np.pi/2, 0, np.pi/2, np.pi],\n", " [r'$-\\pi$', r'$-\\pi/2$', r'$0$', r'$+\\pi/2$', r'$+\\pi$'])\n", "\n", "plt.yticks([-1, 0, +1],\n", " [r'$-1$', r'$0$', r'$+1$'])\n", "\n", "plt.plot(x, c, color=\"blue\", linewidth=2.5, linestyle=\"--\", label=\"cosine\")\n", "plt.plot(x, s, color=\"red\", linewidth=2.5, linestyle=\"-\", label=\"sine\")\n", "plt.legend(loc='upper left', fontsize=20)\n", "\n", "t = 2 * np.pi / 3\n", "plt.plot([t, t], [0, np.cos(t)], color='blue', linewidth=2.5, linestyle=\"--\")\n", "plt.scatter([t, ], [np.cos(t), ], 50, color='blue')\n", "\n", "plt.annotate(r'$sin(\\frac{2\\pi}{3})=\\frac{\\sqrt{3}}{2}$',\n", " xy=(t, np.sin(t)), xycoords='data',\n", " xytext=(+10, +30), textcoords='offset points', fontsize=20,\n", " arrowprops=dict(arrowstyle=\"->\", connectionstyle=\"arc3,rad=.2\"))\n", "\n", "plt.plot([t, t],[0, np.sin(t)], color='red', linewidth=2.5, linestyle=\"--\")\n", "plt.scatter([t, ],[np.sin(t), ], 50, color='red')\n", "\n", "plt.annotate(r'$cos(\\frac{2\\pi}{3})=-\\frac{1}{2}$',\n", " xy=(t, np.cos(t)), xycoords='data',\n", " xytext=(-90, -50), textcoords='offset points', fontsize=20,\n", " arrowprops=dict(arrowstyle=\"->\", connectionstyle=\"arc3,rad=.2\"))\n", "\n", "for label in ax.get_xticklabels() + ax.get_yticklabels():\n", " label.set_fontsize(20)\n", " label.set_bbox(dict(facecolor='white', edgecolor='None', alpha=0.65))\n", "\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "#But turn it off before you move on or all your plots in this session will look like that.\n", "plt.rcdefaults()\n", "%pylab inline" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Other types of plots" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the example above, we used the `plot` method to make line plots. There are also methods to make scatter plots, barplots, histograms, loglog plots, semilog plots, etc." ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Errorbars" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For data you might collect in the laboratory, you want to show the uncertainties on your data points." ] }, { "cell_type": "code", "collapsed": false, "input": [ "#Simple constant error bars on each point\n", "x = np.array([0.0, 2.0, 4.0, 6.0, 8.0])\n", "y = np.array([1.1, 1.9, 3.2, 4.0, 5.9])\n", "plt.figure()\n", "plt.errorbar(x, y, xerr=0.2, yerr=0.6, marker='o')\n", "plt.title(\"Simplest errorbars, 0.2 in x, 0.6 in y\");" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Perhaps your error bars vary from point to point:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# example data\n", "x = np.arange(0.1, 4, 0.5)\n", "y = np.exp(-x)\n", "\n", "# example variable error bar values\n", "yerr = 0.1 + 0.2*np.sqrt(x)\n", "xerr = 0.1 + yerr\n", "\n", "plt.figure()\n", "plt.errorbar(x, y, xerr, yerr, marker='^')\n", "plt.show()" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Subplots and Logarithmic axes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You may wish to have the axes plot on a logarithmic scale. There are two ways to do this: \"log-log\" or \"semilog\", where only one axis is in log scale. To simplify the presentation of the different options, we will also divide our figure up into four subplots. Have a look at each possibility:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "x = np.linspace(0., 5.)\n", "y = np.exp(-x)\n", "\n", "#Make a figure with 4 subplots and axes side-by-side\n", "fig, ax = plt.subplots(1,4, figsize=(10,6))\n", "\n", "#Plot on each axis\n", "ax[0].plot(x,y)\n", "ax[1].loglog(x,y)\n", "ax[2].semilogx(x,y)\n", "ax[3].semilogy(x,y);\n" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Recall that an exponential function plotted in `semilogy` is a straight line!" ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Scatter plots" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For sparse data, sometimes you want to see the values plotted as a scatter plot of `y` vs. `x`:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Make some data to plot\n", "x = np.arange(0, 100)\n", "y = np.random.rand(100) # 100 random numbers\n", "\n", "plt.scatter(x,y);" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Histograms" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Histograms are a class of plot that allow you to present information on the frequency of a particular value occuring in a distribution of events. They are used universally in many fields of science, physics included.\n", "\n", "Here is the [wikipedia definition](http://en.wikipedia.org/wiki/Histogram):\n", "\n", ">A histogram is a representation of tabulated frequencies, shown as adjacent rectangles, erected over discrete intervals (bins), with an area proportional to the frequency of the observations in the interval. The height of a rectangle is also equal to the frequency density of the interval, i.e., the frequency divided by the width of the interval. The total area of the histogram is equal to the number of data. A histogram may also be normalized displaying relative frequencies. It then shows the proportion of cases that fall into each of several categories, with the total area equaling 1. The categories are usually specified as consecutive, non-overlapping intervals of a variable. The categories (intervals) must be adjacent, and often are chosen to be of the same size. The rectangles of a histogram are drawn so that they touch each other to indicate that the original variable is continuous.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here is an example histogram annotated with text inside the plot, using the `text` function:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "mu, sigma = 100, 15\n", "x = mu + sigma * np.random.randn(10000)\n", "\n", "# the histogram of the data\n", "n, bins, patches = plt.hist(x, 50, normed=1, facecolor='g', alpha=0.75)\n", "\n", "plt.xlabel('Smarts',fontsize=20)\n", "plt.ylabel('Probability',fontsize=20)\n", "plt.title('Histogram of IQ',fontsize=20)\n", "\n", "# This will put a text fragment at the position given:\n", "plt.text(45, .027, r'$\\mu=100,\\ \\sigma=15$', fontsize=20)\n", "plt.axis([40, 160, 0, 0.03])\n", "plt.xticks(fontsize=20)\n", "plt.yticks(fontsize=20)\n", "plt.grid();" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The number of bins was set at 50 in the second argument of `plt.hist`. The area is normalized to 1, so the values on the `y` axis are fractions that will add up to 1 if all of the green bars are added together. The `alpha` parameter sets the transparency of the fill color. \n", "\n", "**Play with the settings a little bit to see how they change the look of the plot.**" ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "Plotting images or 2D NumPy arrays" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Recall that images can be read in as NumPy arrays. We can plot them with `imshow`:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Use an image file for first subplot, generate random 2D array for second subplot\n", "from scipy import misc\n", "img1 = misc.lena()\n", "img2 = np.random.rand(128, 128) #128x128 random numbers as a 2D array\n", "\n", "# Make figure\n", "fig, ax = plt.subplots(1, 2)\n", "ax[0].imshow(img1)\n", "ax[1].imshow(img2)\n", "\n", "ax[0].set_axis_off() # Hide \"spines\" on first axis, since it is a \"picture\"" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Loading examples from the Matplotlib gallery" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When you use the Matplotlib gallery to template a figure, you can very easily load the source code into your notebook and then modify it as needed to fit your specific needs. Try it now. After the code is loaded, just execute the cell to see the output." ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Try it here...\n", "%loadpy http://matplotlib.org/mpl_examples/pylab_examples/contour_demo.py" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "code", "collapsed": false, "input": [ "#!/usr/bin/env python\n", "\"\"\"\n", "Illustrate simple contour plotting, contours on an image with\n", "a colorbar for the contours, and labelled contours.\n", "\n", "See also contour_image.py.\n", "\"\"\"\n", "import matplotlib\n", "import numpy as np\n", "import matplotlib.cm as cm\n", "import matplotlib.mlab as mlab\n", "import matplotlib.pyplot as plt\n", "\n", "matplotlib.rcParams['xtick.direction'] = 'out'\n", "matplotlib.rcParams['ytick.direction'] = 'out'\n", "\n", "delta = 0.025\n", "x = np.arange(-3.0, 3.0, delta)\n", "y = np.arange(-2.0, 2.0, delta)\n", "X, Y = np.meshgrid(x, y)\n", "Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)\n", "Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)\n", "# difference of Gaussians\n", "Z = 10.0 * (Z2 - Z1)\n", "\n", "\n", "\n", "# Create a simple contour plot with labels using default colors. The\n", "# inline argument to clabel will control whether the labels are draw\n", "# over the line segments of the contour, removing the lines beneath\n", "# the label\n", "plt.figure()\n", "CS = plt.contour(X, Y, Z)\n", "plt.clabel(CS, inline=1, fontsize=10)\n", "plt.title('Simplest default with labels')\n", "\n", "\n", "# contour labels can be placed manually by providing list of positions\n", "# (in data coordinate). See ginput_manual_clabel.py for interactive\n", "# placement.\n", "plt.figure()\n", "CS = plt.contour(X, Y, Z)\n", "manual_locations = [(-1, -1.4), (-0.62, -0.7), (-2, 0.5), (1.7, 1.2), (2.0, 1.4), (2.4, 1.7)]\n", "plt.clabel(CS, inline=1, fontsize=10, manual=manual_locations)\n", "plt.title('labels at selected locations')\n", "\n", "\n", "# You can force all the contours to be the same color.\n", "plt.figure()\n", "CS = plt.contour(X, Y, Z, 6,\n", " colors='k', # negative contours will be dashed by default\n", " )\n", "plt.clabel(CS, fontsize=9, inline=1)\n", "plt.title('Single color - negative contours dashed')\n", "\n", "# You can set negative contours to be solid instead of dashed:\n", "matplotlib.rcParams['contour.negative_linestyle'] = 'solid'\n", "plt.figure()\n", "CS = plt.contour(X, Y, Z, 6,\n", " colors='k', # negative contours will be dashed by default\n", " )\n", "plt.clabel(CS, fontsize=9, inline=1)\n", "plt.title('Single color - negative contours solid')\n", "\n", "\n", "# And you can manually specify the colors of the contour\n", "plt.figure()\n", "CS = plt.contour(X, Y, Z, 6,\n", " linewidths=np.arange(.5, 4, .5),\n", " colors=('r', 'green', 'blue', (1,1,0), '#afeeee', '0.5')\n", " )\n", "plt.clabel(CS, fontsize=9, inline=1)\n", "plt.title('Crazy lines')\n", "\n", "\n", "# Or you can use a colormap to specify the colors; the default\n", "# colormap will be used for the contour lines\n", "plt.figure()\n", "im = plt.imshow(Z, interpolation='bilinear', origin='lower',\n", " cmap=cm.gray, extent=(-3,3,-2,2))\n", "levels = np.arange(-1.2, 1.6, 0.2)\n", "CS = plt.contour(Z, levels,\n", " origin='lower',\n", " linewidths=2,\n", " extent=(-3,3,-2,2))\n", "\n", "#Thicken the zero contour.\n", "zc = CS.collections[6]\n", "plt.setp(zc, linewidth=4)\n", "\n", "plt.clabel(CS, levels[1::2], # label every second level\n", " inline=1,\n", " fmt='%1.1f',\n", " fontsize=14)\n", "\n", "# make a colorbar for the contour lines\n", "CB = plt.colorbar(CS, shrink=0.8, extend='both')\n", "\n", "plt.title('Lines with colorbar')\n", "#plt.hot() # Now change the colormap for the contour lines and colorbar\n", "plt.flag()\n", "\n", "# We can still add a colorbar for the image, too.\n", "CBI = plt.colorbar(im, orientation='horizontal', shrink=0.8)\n", "\n", "# This makes the original colorbar look a bit out of place,\n", "# so let's improve its position.\n", "\n", "l,b,w,h = plt.gca().get_position().bounds\n", "ll,bb,ww,hh = CB.ax.get_position().bounds\n", "CB.ax.set_position([ll, b+0.1*h, ww, h*0.8])\n", "\n", "\n", "plt.show()\n" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Common formatting tricks" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are hundreds of formatting options available in Matplotlib, many of which you will end up using occasionally. There are a few options, however, that you will use very frequently. Here's a short list...\n", "\n", "* Changing axis limits\n", "* Changing line colors\n", "* Changing lines to dashed (for black and white figures)\n", "* Adding markers to lines\n", "* Make tick labels point outward instead of inward\n", "* Get rid of the box surrounding the plot\n", "* Adding subplot letters, like (a) and (b)\n", "\n", "...and some examples for how to accomplish all of these things." ] }, { "cell_type": "code", "collapsed": false, "input": [ "# Make some data to plot\n", "x = np.linspace(0, 2*np.pi)\n", "y1 = np.sin(x)\n", "y2 = np.cos(x)\n", "\n", "# First, create an empty figure with 1 subplot\n", "fig, ax1 = plt.subplots(1, 1)\n", "\n", "# Add title and labels\n", "ax1.set_title('My Plot',fontsize=20)\n", "ax1.set_xlabel('x',fontsize=20)\n", "ax1.set_ylabel('y',fontsize=20)\n", "\n", "# Change axis limits\n", "ax1.set_xlim([0,2])\n", "ax1.set_ylim([-1, 2])\n", "\n", "# Add the lines, changing their color, style, and marker\n", "ax1.plot(x, y1, 'k--o', label='sin') # Black line, dashed, with 'o' markers\n", "ax1.plot(x, y2, 'r-^', label='cos') # Red line, solid, with triangle-up markers\n", "\n", "# Adjust tick marks and get rid of 'box'\n", "ax1.tick_params(direction='out', top=False, right=False) # Turn ticks out\n", "ax1.spines['top'].set_visible(False) # Get rid of top axis line\n", "ax1.spines['right'].set_visible(False) # Get rid of bottom axis line\n", "\n", "# Add subplot letter\n", "ax1.annotate('(a)', (0.01, 0.96), size=12, xycoords='figure fraction')\n", "\n", "# Add legend\n", "ax1.legend()\n", "\n", "# Finally, save the figure as a png file\n", "fig.savefig('myfig-formatted.png')" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "heading", "level": 2, "metadata": {}, "source": [ "Simple 3D plotting with Matplotlib" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For 3D plots, you must execute at least once in your session:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from mpl_toolkits.mplot3d import Axes3D" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once this has been done, you can create 3D axes with the `projection='3d'` keyword to `add_subplot`:\n", "\n", "```python\n", "fig = plt.figure()\n", "fig.add_subplot(...,\n", " projection='3d')\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here is a simple 3D surface plot:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "from mpl_toolkits.mplot3d.axes3d import Axes3D\n", "from matplotlib import cm\n", "\n", "fig = plt.figure()\n", "ax = fig.add_subplot(1, 1, 1, projection='3d')\n", "X = np.arange(-5, 5, 0.25)\n", "Y = np.arange(-5, 5, 0.25)\n", "#Re-factor the x,y arrays to be a 2-D grid\n", "X, Y = np.meshgrid(X, Y)\n", "R = np.sqrt(X**2 + Y**2)\n", "Z = np.sin(R)\n", "surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.jet,\n", " linewidth=0, antialiased=False)\n", "ax.set_zlim3d(-1.01, 1.01);" ], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This graph uses a `meshgrid` to create a 2D array corresponding to $(x,y)$ points covering the whole $x-y$ plane. It is critical for being able to plot 3D functions." ] }, { "cell_type": "heading", "level": 3, "metadata": {}, "source": [ "An example from E&M:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A situation in physics where we might use 3D plots is for visualizing the electric potential of a group of charges. For example, the electric dipole potential is just the sum of the potentials from two point charges separated by some distance. Consider a pair of point charges $\\pm q$ located at $x$ = 0, $y$ = $\\pm d$.\n", "\n", "The potential is just the sum of the two terms - one for each point charge:\n", "\n", "$$\n", "V(x,y) = V_1 + V_2 = \\frac{k(q)}{\\sqrt{x^2+(y-d)^2}} + \\frac{k(-q)}{\\sqrt{x^2+(y+d)^2}}\n", "$$\n", "\n", "Recall that $k = 1/4\\pi\\epsilon_0$. To see what is happening we will just let $k$=1, $q$=1, and $d$=1. The answer won't depend in an important way on these choices.\n", "\n", "First, we have to make the 2D function $V(x,y)$. 2D functions are more complicated than 1D functions. We need a 2D array corresponding to $(x,y)$ points covering the whole $x-y$ plane. The `meshgrid` function creates the right $x$ and $y$ arrays. " ] }, { "cell_type": "code", "collapsed": false, "input": [ "from mpl_toolkits.mplot3d import Axes3D\n", "\n", "#make the x-y grid to cover the plane -2\n", "I calculated the expectation value $\\langle f \\rangle$ using the integral of the function over the interval as \n", "$\\int f(t) dt$ using $\\LaTeX$.\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Type that into a markdown cell to see it for yourself.** " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can get Greek symbols, math symbols, and a whole variety of text widgets from $\\LaTeX$. There are lots of quick-references and cheat sheets out on the web. The [AMS guide](ftp://ftp.ams.org/pub/tex/doc/amsmath/short-math-guide.pdf) I mentioned above is a great definitive authority, but here's [another example](http://web.ift.uib.no/Teori/KURS/WRK/TeX/symALL.html) for comparison. Often when I don't know the syntax for a specific symbol I google \"latex symbol reference\" and I can find what I need pretty quickly." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " All content is under a modified MIT License, and can be freely used and adapted. See the full license text [here](../../LICENSE)." ] } ], "metadata": {} } ] }