{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false, "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "# Let printing work the same in Python 2 and 3\n", "from __future__ import print_function" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "-" } }, "source": [ "# Matplotlib\n", "## Introduction\n", "\n", "Matplotlib is a library for producing publication-quality figures. mpl (for short) was designed from the beginning to serve two purposes: \n", "\n", " 1. allow for interactive, cross-platform control of figures and plots\n", " 2. make it easy to produce static raster or vector graphics files without the need for any GUIs. \n", " \n", "Furthermore, mpl -- much like Python itself -- gives the developer complete control over the appearance of their plots, while still being very usable through a powerful defaults system." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Online Documentation\n", "The [matplotlib.org](http://matplotlib.org) project website is the primary online resource for the library's documentation. It contains the [example galleries](https://matplotlib.org/gallery/index.html), [FAQs](http://matplotlib.org/faq/index.html), [API documentation](http://matplotlib.org/api/index.html), and [tutorials](https://matplotlib.org/tutorials/index.html).\n", "\n", "## Gallery\n", "Many users of Matplotlib are often faced with the question, \"I want to make a figure that has X with Y in the same figure, but it needs to look like Z\". Good luck getting an answer from a web search with that query! This is why the [gallery](https://matplotlib.org/gallery/index.html) is so useful, because it showcases the variety of ways one can make figures. Browse through the gallery, click on any figure that has pieces of what you want to see and the code that generated it. Soon enough, you will be like a chef, mixing and matching components to produce your masterpiece!\n", "\n", "As always, if you have a new and interesting plot that demonstrates a feature of Matplotlib, feel free to submit a concise, well-commented version of the code for inclusion in the gallery.\n", "\n", "## Mailing Lists, StackOverflow, and gitter\n", "When you are just simply stuck, and cannot figure out how to get something to work, or just need some hints on how to get started, you will find much of the community at the matplotlib-users [mailing list](https://mail.python.org/mailman/listinfo/matplotlib-users). This mailing list is an excellent resource of information with many friendly members who just love to help out newcomers. We love plots, so an image showing what is wrong often gets the quickest responses.\n", "\n", "Another community resource is [StackOverflow](http://stackoverflow.com/questions/tagged/matplotlib), so if you need to build up karma points, submit your questions here, and help others out too! \n", "\n", "We are also on [Gitter](https://gitter.im/matplotlib/matplotlib).\n", "\n", "## Github repository\n", "### Location\n", "[Matplotlib](https://github.com/matplotlib) is hosted by GitHub.\n", "\n", "### Bug Reports and feature requests\n", "So, you think you found a bug? Or maybe you think some feature is just too difficult to use? Or missing altogether? Submit your bug reports [here](https://github.com/matplotlib/matplotlib/issues) at Matplotlib's issue tracker. We even have a process for submitting and discussing Matplotlib Enhancement Proposals ([MEPs](https://matplotlib.org/devel/MEP/index.html))." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Quick note on \"backends\" and Jupyter notebooks\n", "\n", "Matplotlib has multiple \"backends\" that handle converting Matplotlib's in-memory representation of your plot into the colorful output you can look at. This is done either by writing files (e.g., png, svg, pdf) that you can use an external tool to look at or by embedding into your GUI toolkit of choice (Qt, Tk, Wx, etc).\n", "\n", "To check what backend Matplotlib is currently using:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import matplotlib\n", "print(matplotlib.__version__)\n", "print(matplotlib.get_backend())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you are working interactively at an (I)python prompt, the GUI framework is not critical (mostly aesthetic) however when working in Jupyter we need to pick a backend that integrates with Jupyter (javascript) framework.\n", "\n", "To select the backend use ``matplotlib.use(\"backend_name\")``, in this case we want ``'nbagg'``\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "matplotlib.use('nbagg')\n", "print(matplotlib.get_backend())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "which must be done *before* you `import matplotlib.pyplot as plt`.\n", "\n", "You can also set the backend via an 'ipython magic' ``%matplotlib backend_name``. In addition to setting the backend, the magic also calls `plt.ion()`, which puts Matplotlib in 'interacitve mode' (the inverse is `plt.ioff()`). In 'interactive mode' figures are shown (injected into the web page in the notebook) as soon as they are created. Otherwise, figures are not shown until you explicitly call `plt.show()`.\n", "\n", "\n", "In these tutorials we will mostly work in non-interactive mode for better control of when\n", "figures are shown in the notebooks.\n", "This also better mimics the behavior you can expect in regular python scripts.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# On with the show!\n", "Matplotlib is a large project and can seem daunting at first. However, by learning the components, it should begin to feel much smaller and more approachable.\n", "\n", "## Anatomy of a \"Plot\"\n", "\n", "People use \"plot\" to mean many different things. Here, we'll be using a consistent terminology (mirrored by the names of the underlying classes, etc):\n", "\n", "\n", "\n", "The ``Figure`` is the top-level container in this hierarchy. It is the overall window/page that everything is drawn on. You can have multiple independent figures and ``Figure``s can contain multiple ``Axes``. \n", "\n", "Most plotting ocurs on an ``Axes``. The axes is effectively the area that we plot data on and any ticks/labels/etc associated with it. Usually we'll set up an Axes with a call to ``subplot`` (which places Axes on a regular grid), so in most cases, ``Axes`` and ``Subplot`` are synonymous.\n", "\n", "Each ``Axes`` has an ``XAxis`` and a ``YAxis``. These contain the ticks, tick locations, labels, etc. In this tutorial, we'll mostly control ticks, tick labels, and data limits through other mechanisms, so we won't touch the individual ``Axis`` part of things all that much. However, it is worth mentioning here to explain where the term ``Axes`` comes from.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Getting Started\n", "\n", "In this tutorial, we'll use the following import statements. These abbreviations are semi-standardized, and most tutorials, other scientific python code that you'll find elsewhere will use them as well." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Figures\n", "\n", "Now let's create a figure..." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(facecolor=(1, 0, 0, .1)) # red background to see where the figure is" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Awww, nothing happened! This is because by default mpl will not show anything until told to do so, as we mentioned earlier in the \"backend\" discussion.\n", "\n", "Instead, we'll need to call ``plt.show()``" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Great, a blank figure! Not terribly useful yet.\n", "\n", "\n", "However, while we're on the topic, you can control the size of the figure through the ``figsize`` argument, which expects a tuple of ``(width, height)`` in inches. \n", "\n", "A really useful utility function is [`figaspect`](https://matplotlib.org/api/_as_gen/matplotlib.figure.figaspect.html?highlight=figaspect#matplotlib.figure.figaspect)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Twice as tall as it is wide:\n", "fig = plt.figure(figsize=plt.figaspect(2.0), facecolor=(1, 0, 0, .1))\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Axes\n", "\n", "All plotting is done with respect to an [`Axes`](http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes). An *Axes* is made up of [`Axis`](http://matplotlib.org/api/axis_api.html#matplotlib.axis.Axis) objects and many other things. An *Axes* object must belong to a *Figure* (and only one *Figure*). Most commands you will ever issue will be with respect to this *Axes* object.\n", "\n", "Typically, you'll set up a `Figure`, and then add an `Axes` to it. \n", "\n", "You can use `fig.add_axes`, but in most cases, you'll find that adding a subplot will fit your needs perfectly. (Again a \"subplot\" is just an axes on a grid system.) " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig = plt.figure()\n", "ax = fig.add_subplot(111) # We'll explain the \"111\" later. Basically, 1 row and 1 column.\n", "ax.set(xlim=[0.5, 4.5], ylim=[-2, 8], title='An Example Axes',\n", " ylabel='Y-Axis', xlabel='X-Axis')\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "Notice the call to ``set``. Matplotlib's objects typically have lots of \"explicit setters\" -- in other words, functions that start with ``set_`` and control a particular option. \n", "\n", "To demonstrate this (and as an example of IPython's tab-completion), try typing `ax.set_` in a code cell, then hit the `` key. You'll see a long list of `Axes` methods that start with `set`.\n", "\n", "For example, we could have written the third line above as:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ax.set_xlim([0.5, 4.5])\n", "ax.set_ylim([-2, 8])\n", "ax.set_title('A Different Example Axes Title')\n", "ax.set_ylabel('Y-Axis (changed)')\n", "ax.set_xlabel('X-Axis (changed)')\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Clearly this can get repitive quickly. Therefore, Matplotlib's `set` method can be very handy. It takes each kwarg you pass it and tries to call the corresponding \"setter\". For example, `foo.set(bar='blah')` would call `foo.set_bar('blah')`.\n", "\n", "Note that the `set` method doesn't just apply to `Axes`; it applies to more-or-less all matplotlib objects.\n", "\n", "However, there are cases where you'll want to use things like `ax.set_xlabel('Some Label', size=25)` to control other options for a particular function." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Basic Plotting\n", "\n", "Most plotting happens on an `Axes`. Therefore, if you're plotting something on an axes, then you'll use one of its methods.\n", "\n", "We'll talk about different plotting methods in more depth in the next section. For now, let's focus on two methods: `plot` and `scatter`.\n", "\n", "`plot` draws points with lines connecting them. `scatter` draws unconnected points, optionally scaled or colored by additional variables.\n", "\n", "As a basic example:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig = plt.figure()\n", "ax = fig.add_subplot(111)\n", "ax.plot([1, 2, 3, 4], [10, 20, 25, 30], color='lightblue', linewidth=3)\n", "ax.scatter([0.3, 3.8, 1.2, 2.5], [11, 25, 9, 26], c=[1, 2, 3, 5], marker='^')\n", "ax.set_xlim(0.5, 4.5)\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Axes methods vs. pyplot\n", "\n", "Interestingly, just about all methods of an *Axes* object exist as a function in the *pyplot* module (and vice-versa). For example, when calling `plt.xlim(1, 10)`, *pyplot* calls `ax.set_xlim(1, 10)` on whichever *Axes* is \"current\". Here is an equivalent version of the above example using just pyplot." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.plot([1, 2, 3, 4], [10, 20, 25, 30], color='lightblue', linewidth=3)\n", "plt.scatter([0.3, 3.8, 1.2, 2.5], [11, 25, 9, 26], c=[1, 2, 3, 5], marker='^')\n", "plt.xlim(0.5, 4.5)\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That is a bit terser and has fewer local varialbes, so, why will most of my examples not follow the pyplot approach? Because [PEP20](http://www.python.org/dev/peps/pep-0020/) \"The Zen of Python\" says:\n", "\n", "\"Explicit is better than implicit\"\n", "\n", "While very simple plots, with short scripts would benefit from the conciseness of the pyplot implicit approach, when doing more complicated plots, or working within larger scripts, you will want to explicitly pass around the *Axes* and/or *Figure* object to operate upon.\n", "\n", "The advantage of keeping which axes we're working with very clear in our code will become more obvious when we start to have multiple axes in one figure." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Multiple Axes\n", "\n", "We've mentioned before that a figure can have more than one `Axes` on it. If you want your axes to be on a regular grid system, then it's easiest to use `plt.subplots(...)` to create a figure and add the axes to it automatically.\n", "\n", "For example:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, axes = plt.subplots(nrows=2, ncols=2)\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`plt.subplots(...)` created a new figure and added 4 subplots to it. The `axes` object that was returned is a 2D numpy object array. Each item in the array is one of the subplots. They're laid out as you see them on the figure. \n", "\n", "Therefore, when we want to work with one of these axes, we can index the `axes` array and use that item's methods.\n", "\n", "For example:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig, axes = plt.subplots(nrows=2, ncols=2)\n", "axes[0,0].set(title='Upper Left')\n", "axes[0,1].set(title='Upper Right')\n", "axes[1,0].set(title='Lower Left')\n", "axes[1,1].set(title='Lower Right')\n", "\n", "# To iterate over all items in a multidimensional numpy array, use the `flat` attribute\n", "for ax in axes.flat:\n", " # Remove all xticks and yticks...\n", " ax.set(xticks=[], yticks=[])\n", " \n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "One really nice thing about `plt.subplots()` is that when it's called with no arguments, it creates a new figure with a single subplot. \n", "\n", "Any time you see something like\n", "\n", "```\n", "fig = plt.figure()\n", "ax = fig.add_subplot(111)\n", "```\n", "\n", "You can replace it with:\n", "\n", "```\n", "fig, ax = plt.subplots()\n", "```\n", "\n", "We'll be using that approach for the rest of the examples. It's much cleaner. \n", "\n", "However, keep in mind that we're still creating a figure and adding axes to it. When we start making plot layouts that can't be described by `subplots`, we'll go back to creating the figure first and then adding axes to it one-by-one." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Quick Exercise: Exercise 1.1\n", "--------------\n", "\n", "Let's use some of what we've been talking about. Can you reproduce this figure?\n", "\n", "\n", "\n", "Here's the data and some code to get you started." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%load exercises/1.1-subplots_and_basic_plotting.py" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import matplotlib.pyplot as plt\n", "\n", "# Try to reproduce the figure shown in images/exercise_1-1.png\n", "\n", "# Our data...\n", "x = np.linspace(0, 10, 100)\n", "y1, y2, y3 = np.cos(x), np.cos(x + 1), np.cos(x + 2)\n", "names = ['Signal 1', 'Signal 2', 'Signal 3']\n", "\n", "# Can you figure out what to do next to plot x vs y1, y2, and y3 on one figure?\n" ] } ], "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.6.5" } }, "nbformat": 4, "nbformat_minor": 1 }