{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "
\"Matplotlib
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Matplotlib Introduction" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---\n", "## Overview\n", "We will cover the basics of plotting within Python, using the Matplotlib library, focusing on line plots based on meteorological data.\n", "\n", "1. Create a basic line plot.\n", "1. Add labels and grid lines to the plot.\n", "1. Plot multiple time series of data." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Imports" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's import the matplotlib library's `pyplot` interface; this interface is the simplest way to create new Matplotlib figures. To shorten this long name, we import it as `plt` to keep things short but clear." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "

Info

\n", " Matplotlib is a python 2D plotting library which produces publication quality figures in a variety of hard-copy formats and interactive environments across platforms.\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Plot some temperature data:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's create two lists, corresponding to a day's worth of hourly temperature data for Albany:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "hours = range (0,24)\n", "temps = [-4.4, -3.3, -3.3, -3.3, -3.9, -3.3, -4.4, -2.8, -2.8, -2.2, -1.1, 0.0, 1.7, 1.7, 2.2, 2.2, 2.2, 2.2, -0.6, -1.1, -1.7, -2.2, -3.3, -3.9]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "

Note:

\n", " We could have explicitly created the hours list object as follows:
\n", " hours = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]
\n", " The way we did it, using the range function, is quicker, both in terms of typing and execution time.\n", "

Note that Python's \"range\" function returns a sequence of numbers, starting with the first indicated integer, and ending with the second indicated integer NOT inclusive. Thus, in this example, range(0,24) yields a range of integers from 0 to 23.\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Figure and Axes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let's make our first plot with Matplotlib. Matplotlib has two core objects: the `Figure` and the `Axes`. The `Axes` is an individual plot with an x-axis, a y-axis, labels, etc; it has all of the various plotting methods we use. A `Figure` holds one or more `Axes` on which we draw; think of the `Figure` as the level at which things are saved to files (e.g. PNG, SVG)\n", "\n", "![anatomy of a figure](https://matplotlib.org/stable/_images/sphx_glr_anatomy_001.png \"anatomy of a figure\")\n", "\n", "(source: https://matplotlib.org/gallery/showcase/anatomy.html)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "

Note:

\n", " The terminology takes a bit of getting used to as axes are not the same thing as an x- and y-axis. Instead, think of axes as one distinct plot on a canvas, where the canvas is represented by figure.\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Line plots\n", "\n", "Let's create a `Figure` whose dimensions, if printed out on hardcopy, would be 10 inches wide and 6 inches long (assuming a landscape orientation). We then create an `Axes`, consisting of a single subplot, on the `Figure`. After that, we call `plot`, with `hours` as the data along the x-axis (independent values) and `temps` as the data along the y-axis (the dependent values)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "

Info

\n", " By default, ax.plot will create a line plot, as seen below \n", "
" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Create a figure (the container, aka the canvas, for the resulting graphic)\n", "fig = plt.figure(figsize=(10, 6))\n", "\n", "# On this figure, create an axes object. The figure will contain just this one axes.\n", "ax = fig.add_subplot(1, 1, 1)\n", "\n", "# Plot hours as x-variable and temperatures as y-variable\n", "ax.plot(hours, temps)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "

Question

\n", " Let's say you added one more time value to the hours array above, while not adding a corresponding value to the temps array. Do you think the plot you made above would still work? Why or why not? Copy the code cell above into a new code cell below to test your hypothesis.\n", "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Adding axes labels" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, add x- and y-axis labels to our `Axes` object." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Add some labels to the plot\n", "ax.set_xlabel('Hour (EST)')\n", "ax.set_ylabel('Temperature (deg C)')\n", "\n", "# Prompt the notebook to re-display the figure after we modify it\n", "fig" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also add a title to the plot and specify the title's fontsize:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ax.set_title('Hourly temperatures at ALB, Feb. 27, 2022',fontsize=16)\n", "\n", "fig" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Let's set up another array; this one contains 24 hours' worth of temperatures for Watertown, NY (KART) \n", "\n", "We start by setting up another temperature array" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "temps_ART = [-2.2, -2.2, -2.2, -1.7, -1.7, -2.2, -2.2, -2.2, -2.2, -1.7, -1.7, -1.1, 0.0, 0.0, -0.6, -1.7, -2.2, -1.7, -2.2, -2.8, -4.4, -6.1, -6.7, -8.9]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Adding labels and a grid" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here we call `plot` more than once to plot multiple series of temperature on the same plot; when plotting we pass `label` to `plot` to facilitate automatic creation of legend labels. This is added with the `legend` call. We also add gridlines to the plot using the `grid()` call." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(10, 6))\n", "ax = fig.add_subplot(1, 1, 1)\n", "\n", "# Plot two series of data\n", "# The label argument is used when generating a legend.\n", "ax.plot(hours, temps, label='Albany')\n", "ax.plot(hours, temps_ART, label='Watertown')\n", "\n", "# Add labels and title\n", "ax.set_xlabel('Hour (EST)')\n", "ax.set_ylabel('Temperature (°C)')\n", "ax.set_title('Hourly Temperatures, Feb. 27 2022')\n", "\n", "# Add gridlines\n", "ax.grid(True)\n", "\n", "# Add a legend to the upper left corner of the plot\n", "ax.legend(loc='upper left')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "

Note

\n", " In theax.set_ylabel call, we represented the degree symbol using TeX in Matplotlib\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Customizing colors" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We're not restricted to the default look of the plots, but rather we can override style attributes, such as `linestyle` and `color`. `color` can accept a wide array of options for color, such as `red` or `blue` or HTML color codes. Here we use some different shades of red taken from the Tableau color set in matplotlib, by using `tab:red` for color. For more details, see https://matplotlib.org/stable/gallery/color/named_colors.html" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(10, 6))\n", "ax = fig.add_subplot(1, 1, 1)\n", "\n", "# Specify how our lines should look\n", "ax.plot(hours, temps, color='tab:red', label='ALB')\n", "ax.plot(\n", " hours,\n", " temps_ART,\n", " color='tab:blue',\n", " linestyle='--',\n", " label='ART',\n", ")\n", "\n", "# Set the labels and title\n", "ax.set_xlabel('Hour (EST)')\n", "ax.set_ylabel('Temperature (°C)')\n", "ax.set_title('Hourly Temperatures, Feb. 27 2022')\n", "\n", "# Add the grid\n", "ax.grid(True)\n", "\n", "# Add a legend to the upper left corner of the plot\n", "ax.legend(loc='upper left')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Working with multiple panels in a figure" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let's add some dewpoint data at the same times for the two locations." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "dewp_ALB = [-11.1, -11.7, -11.1, -10.6, -10.6, -11.1, -10.6, -10.6, -10.6, -10.0, -10.0, -9.4, -8.9, -8.9, -8.9, -8.3, -7.8, -7.8, -3.3, -9.4, -11.7, -12.8, -12.8, -9.4]\n", "dewp_ART = [-6.1, -5.0, -5.6, -5.6, -6.1, -3.9, -5.6, -6.1, -5.6, -5.6, -3.9, -3.9, -4.4, -3.9, -4.4, -6.1, -5.0, -10.6, -10.0, -11.7, -12.2, -13.3, -13.3, -15.6]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's also make the temperature array for Albany have an object name that fits the other three list objects." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "temps_ALB = temps" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, let's plot it up, with the other temperature data!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Using add_subplot to create two different subplots within the figure\n", "We can use the `.add_subplot()` method to add multiple `Axes`, i.e. *subplots*, to our figure! The `add_subplot` arguments are formatted as follows:\n", "`(rows, columns, subplot_number)`\n", "\n", "For example, if we want a single row, with two columns, we use the following code block" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(10, 6))\n", "\n", "# Create a plot for temperature\n", "ax = fig.add_subplot(1, 2, 1)\n", "ax.plot(hours, temps_ALB, color='tab:red')\n", "\n", "# Create a plot for dewpoint\n", "ax2 = fig.add_subplot(1, 2, 2)\n", "ax2.plot(hours, dewp_ALB, color='tab:green')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "

Warning:

\n", " It's very easy to make a figure that either intentionally or unintentionally deceives the viewer! In this case, at first glance it appears that the temperature and dewpoint span the same range of values. But note the different y-axes!\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Adding titles to each subplot\n", "We can add titles to these plots too - notice how these subplots are titled separately, each using `ax.set_title` after each subplot" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(10, 6))\n", "\n", "# Create a plot for temperature\n", "ax = fig.add_subplot(1, 2, 1)\n", "ax.plot(hours, temps_ALB, color='tab:red')\n", "ax.set_title('Temperature ALB Feb 27 2022')\n", "\n", "# Create a plot for dewpoint\n", "ax2 = fig.add_subplot(1, 2, 2)\n", "ax2.plot(hours, dewp_ALB, color='tab:green')\n", "ax2.set_title('Dewpoint ALB Feb 27 2022')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Using `ax.set_xlim` and `ax.set_ylim` to control the plot boundaries\n", "\n", "You may want to specify the ranges of the x- and/or y-axes of each plot, which you can do by using `.set_xlim` and `set_ylim` on the axes you are editing." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(10, 6))\n", "\n", "# Create a plot for temperature\n", "ax = fig.add_subplot(1, 2, 1)\n", "ax.plot(hours, temps_ALB, color='tab:red')\n", "ax.set_title('Temperature ALB Feb 27 2022')\n", "ax.set_xlim(16, 22)\n", "ax.set_ylim(-15,5)\n", "\n", "# Create a plot for dewpoint\n", "ax2 = fig.add_subplot(1, 2, 2)\n", "ax2.plot(hours, dewp_ALB, color='tab:green')\n", "ax2.set_title('Dewpoint ALB Feb 27 2022')\n", "ax2.set_xlim(16,22)\n", "ax2.set_ylim(-15,5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Using `sharex` and `sharey` to share plot limits\n", "\n", "You may want to have both subplots share the same x/y axis limits - you can do this by adding `sharex=ax` and `sharey=ax` as arguments when adding a new axis (ex. `ax2 = fig.add_subplot()`) where `ax` is the axis you want your new axis to share limits with\n", "\n", "Let's take a look at an example" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(15, 9))\n", "\n", "# Create a plot for temperature\n", "ax = fig.add_subplot(1, 2, 1)\n", "ax.plot(hours, temps_ALB, color='tab:red')\n", "ax.set_title('Temperature ALB Feb 27 2022')\n", "ax.set_ylim(-15,5)\n", "\n", "# Create a plot for dewpoint\n", "ax2 = fig.add_subplot(1, 2, 2, sharex=ax, sharey=ax)\n", "ax2.plot(hours, dewp_ALB, color='tab:green')\n", "ax2.set_title('Dewpoint ALB Feb 27 2022')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Putting this all together" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "

Info

\n", " You may wish to move around the location of your legend - you can do this by changing the loc argument in ax.legend()\n", "
" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fig = plt.figure(figsize=(15, 9))\n", "ax = fig.add_subplot(1, 2, 1)\n", "\n", "# Specify how our lines should look\n", "ax.plot(hours, temps_ALB, color='tab:red', label='Albany')\n", "ax.plot(\n", " hours,\n", " temps_ART,\n", " color='tab:red',\n", " linestyle=':',\n", " marker='o',\n", " label='Watertown',\n", ")\n", "\n", "# Add labels, grid, and legend\n", "ax.set_xlabel('Hour (EST)')\n", "ax.set_ylabel('Temperature (°C)')\n", "ax.set_title('Hourly Temperatures, Feb 27 2022')\n", "ax.grid(True)\n", "ax.legend(loc='upper left')\n", "ax.set_ylim(-20,5)\n", "\n", "\n", "# Add our second plot - for dewpoint, changing the colors and labels\n", "ax2 = fig.add_subplot(1, 2, 2, sharey=ax)\n", "ax2.plot(hours, dewp_ALB, color='tab:green', label='Albany')\n", "ax2.plot(\n", " hours,\n", " dewp_ART,\n", " color='tab:green',\n", " linestyle=':',\n", " marker='o',\n", " label='Watertown',\n", ")\n", "\n", "ax2.set_xlabel('Time')\n", "ax2.set_ylabel('Dewpoint ($^\\circ$C)')\n", "ax2.set_title('Hourly Dewpoints, Feb 27 2022')\n", "ax2.grid(True)\n", "ax2.legend(loc='upper left')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Summary\n", "* `Matplotlib` can be used to visualize datasets you are working with\n", "* You can customize various features such as labels and styles\n", "* Besides line plots (`plot`), there are a wide variety of plotting options available, including (but not limited to)\n", " * Scatter plots (`scatter`)\n", " * Imshow (`imshow`)\n", " * Contour line and contour fill plots (`contour`, `contourf`)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## What's Next?\n", "Next, instead of \"hard-coding\" a series of meteorological observations, we will do a lab exercise where we read in observations from a file, using the [pandas](https://pandas.pydata.org) library. We will then make more plots with **Matplotlib**." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Resources and References\n", "\n", "The goal of this notebook was to provide an overview of the use of the Matplotlib library. It focuses on simple line plots and touches on additional types of Matplotlib plotting methods, but it is by no means comprehensive. For more information, check out the following links:\n", "- [Matplotlib Documentation](http://matplotlib.org)\n", "- [Matplotlib `plot` documentation](https://matplotlib.org/stable/index.html)\n", "- [GeoCAT examples gallery](https://geocat-examples.readthedocs.io/en/latest/gallery/index.html)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 August 2022 Environment", "language": "python", "name": "aug22" }, "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.10.5" } }, "nbformat": 4, "nbformat_minor": 4 }