Files
AstronomicalData/07_plot.ipynb
2021-03-26 10:44:09 -04:00

992 lines
28 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 7. Visualization\n",
"\n",
"This is the seventh in a series of notebooks related to astronomy data.\n",
"\n",
"As a continuing example, we will replicate part of the analysis in a recent paper, \"[Off the beaten path: Gaia reveals GD-1 stars outside of the main stream](https://arxiv.org/abs/1805.00425)\" by Adrian M. Price-Whelan and Ana Bonaca.\n",
"\n",
"In the previous notebook we selected photometry data from Pan-STARRS and used it to identify stars we think are likely to be in GD-1\n",
"\n",
"In this notebook, we'll take the results from previous lessons and use them to make a figure that tells a compelling scientific story."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Outline\n",
"\n",
"Here are the steps in this notebook:\n",
"\n",
"1. Starting with the figure from the previous notebook, we'll add annotations to present the results more clearly.\n",
"\n",
"2. The we'll see several ways to customize figures to make them more appealing and effective.\n",
"\n",
"3. Finally, we'll see how to make a figure with multiple panels or subplots.\n",
"\n",
"After completing this lesson, you should be able to\n",
"\n",
"* Design a figure that tells a compelling story.\n",
"\n",
"* Use Matplotlib features to customize the appearance of figures.\n",
"\n",
"* Generate a figure with multiple subplots."
]
},
{
"cell_type": "markdown",
"metadata": {
"tags": []
},
"source": [
"## Installing libraries\n",
"\n",
"If you are running this notebook on Colab, you can run the following cell to install the libraries we'll use.\n",
"\n",
"If you are running this notebook on your own computer, you might have to install these libraries yourself. See the instructions in the preface."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"# If we're running on Colab, install libraries\n",
"\n",
"import sys\n",
"IN_COLAB = 'google.colab' in sys.modules\n",
"\n",
"if IN_COLAB:\n",
" !pip install wget"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Making Figures That Tell a Story\n",
"\n",
"So far the figure we've made have been \"quick and dirty\". Mostly we have used Matplotlib's default style, although we have adjusted a few parameters, like `markersize` and `alpha`, to improve legibility.\n",
"\n",
"Now that the analysis is done, it's time to think more about:\n",
"\n",
"1. Making professional-looking figures that are ready for publication, and\n",
"\n",
"2. Making figures that communicate a scientific result clearly and compellingly.\n",
"\n",
"Not necessarily in that order."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's start by reviewing Figure 1 from the original paper. We've seen the individual panels, but now let's look at the whole thing, along with the caption:\n",
"\n",
"<img width=\"500\" src=\"https://github.com/datacarpentry/astronomy-python/raw/gh-pages/fig/gd1-5.png\">"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Exercise\n",
"\n",
"Think about the following questions:\n",
"\n",
"1. What is the primary scientific result of this work?\n",
"\n",
"2. What story is this figure telling?\n",
"\n",
"3. In the design of this figure, can you identify 1-2 choices the authors made that you think are effective? Think about big-picture elements, like the number of panels and how they are arranged, as well as details like the choice of typeface.\n",
"\n",
"4. Can you identify 1-2 elements that could be improved, or that you might have done differently?"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"# Solution goes here"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Plotting GD-1\n",
"\n",
"Let's start with the panel in the lower left. The following cell reloads the data."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"from wget import download\n",
"\n",
"filename = 'gd1_data.hdf'\n",
"path = 'https://github.com/AllenDowney/AstronomicalData/raw/main/data/'\n",
"\n",
"if not os.path.exists(filename):\n",
" print(download(path+filename))"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"\n",
"winner_df = pd.read_hdf(filename, 'winner_df')"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"\n",
"def plot_second_selection(df):\n",
" x = df['phi1']\n",
" y = df['phi2']\n",
"\n",
" plt.plot(x, y, 'ko', markersize=0.7, alpha=0.9)\n",
"\n",
" plt.xlabel('$\\phi_1$ [deg]')\n",
" plt.ylabel('$\\phi_2$ [deg]')\n",
" plt.title('Proper motion + photometry selection', fontsize='medium')\n",
"\n",
" plt.axis('equal')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"And here's what it looks like."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"plt.figure(figsize=(10,2.5))\n",
"plot_second_selection(winner_df)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Annotations\n",
"\n",
"The figure in the paper uses three other features to present the results more clearly and compellingly:\n",
"\n",
"* A vertical dashed line to distinguish the previously undetected region of GD-1,\n",
"\n",
"* A label that identifies the new region, and\n",
"\n",
"* Several annotations that combine text and arrows to identify features of GD-1."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Exercise\n",
"\n",
"Choose any or all of these features and add them to the figure:\n",
"\n",
"* To draw vertical lines, see [`plt.vlines`](https://matplotlib.org/3.3.1/api/_as_gen/matplotlib.pyplot.vlines.html) and [`plt.axvline`](https://matplotlib.org/3.3.1/api/_as_gen/matplotlib.pyplot.axvline.html#matplotlib.pyplot.axvline).\n",
"\n",
"* To add text, see [`plt.text`](https://matplotlib.org/3.3.1/api/_as_gen/matplotlib.pyplot.text.html).\n",
"\n",
"* To add an annotation with text and an arrow, see [plt.annotate]().\n",
"\n",
"And here is some [additional information about text and arrows](https://matplotlib.org/3.3.1/tutorials/text/annotations.html#plotting-guide-annotation)."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"# Solution goes here"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Customization\n",
"\n",
"Matplotlib provides a default style that determines things like the colors of lines, the placement of labels and ticks on the axes, and many other properties.\n",
"\n",
"There are several ways to override these defaults and customize your figures:\n",
"\n",
"* To customize only the current figure, you can call functions like `tick_params`, which we'll demonstrate below.\n",
"\n",
"* To customize all figures in a notebook, you use `rcParams`.\n",
"\n",
"* To override more than a few defaults at the same time, you can use a style sheet."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As a simple example, notice that Matplotlib puts ticks on the outside of the figures by default, and only on the left and bottom sides of the axes.\n",
"\n",
"To change this behavior, you can use `gca()` to get the current axes and `tick_params` to change the settings.\n",
"\n",
"Here's how you can put the ticks on the inside of the figure:\n",
"\n",
"```\n",
"plt.gca().tick_params(direction='in')\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Exercise\n",
"\n",
"Read the documentation of [`tick_params`](https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.axes.Axes.tick_params.html) and use it to put ticks on the top and right sides of the axes."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"# Solution goes here"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## rcParams\n",
"\n",
"If you want to make a customization that applies to all figures in a notebook, you can use `rcParams`.\n",
"\n",
"Here's an example that reads the current font size from `rcParams`:"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
"plt.rcParams['font.size']"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"And sets it to a new value:"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"plt.rcParams['font.size'] = 14"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As an exercise, plot the previous figure again, and see what font sizes have changed. Look up any other element of `rcParams`, change its value, and check the effect on the figure."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you find yourself making the same customizations in several notebooks, you can put changes to `rcParams` in a `matplotlibrc` file, [which you can read about here](https://matplotlib.org/3.3.1/tutorials/introductory/customizing.html#customizing-with-matplotlibrc-files)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Style sheets\n",
"\n",
"The `matplotlibrc` file is read when you import Matplotlib, so it is not easy to switch from one set of options to another.\n",
"\n",
"The solution to this problem is style sheets, [which you can read about here](https://matplotlib.org/3.1.1/tutorials/introductory/customizing.html).\n",
"\n",
"Matplotlib provides a set of predefined style sheets, or you can make your own.\n",
"\n",
"The following cell displays a list of style sheets installed on your system."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"plt.style.available"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Note that `seaborn-paper`, `seaborn-talk` and `seaborn-poster` are particularly intended to prepare versions of a figure with text sizes and other features that work well in papers, talks, and posters.\n",
"\n",
"To use any of these style sheets, run `plt.style.use` like this:\n",
"\n",
"```\n",
"plt.style.use('fivethirtyeight')\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The style sheet you choose will affect the appearance of all figures you plot after calling `use`, unless you override any of the options or call `use` again."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As an exercise, choose one of the styles on the list and select it by calling `use`. Then go back and plot one of the figures above and see what effect it has."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you can't find a style sheet that's exactly what you want, you can make your own. This repository includes a style sheet called `az-paper-twocol.mplstyle`, with customizations chosen by Azalee Bostroem for publication in astronomy journals.\n",
"\n",
"The following cell downloads the style sheet."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"from wget import download\n",
"\n",
"filename = 'az-paper-twocol.mplstyle'\n",
"path = 'https://github.com/AllenDowney/AstronomicalData/raw/main/'\n",
"\n",
"if not os.path.exists(filename):\n",
" print(download(path+filename))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You can use it like this:\n",
"\n",
"```\n",
"plt.style.use('./az-paper-twocol.mplstyle')\n",
"```\n",
"\n",
"The prefix `./` tells Matplotlib to look for the file in the current directory."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As an alternative, you can install a style sheet for your own use by putting it in your configuration directory. To find out where that is, you can run the following command:\n",
"\n",
"```\n",
"import matplotlib as mpl\n",
"\n",
"mpl.get_configdir()\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## LaTeX fonts\n",
"\n",
"When you include mathematical expressions in titles, labels, and annotations, Matplotlib uses [`mathtext`](https://matplotlib.org/3.1.0/tutorials/text/mathtext.html) to typeset them. `mathtext` uses the same syntax as LaTeX, but it provides only a subset of its features.\n",
"\n",
"If you need features that are not provided by `mathtext`, or you prefer the way LaTeX typesets mathematical expressions, you can customize Matplotlib to use LaTeX.\n",
"\n",
"In `matplotlibrc` or in a style sheet, you can add the following line:\n",
"\n",
"```\n",
"text.usetex : true\n",
"```\n",
"\n",
"Or in a notebook you can run the following code.\n",
"\n",
"```\n",
"plt.rcParams['text.usetex'] = True\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"plt.rcParams['text.usetex'] = True"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you go back and draw the figure again, you should see the difference.\n",
"\n",
"If you get an error message like\n",
"\n",
"```\n",
"LaTeX Error: File `type1cm.sty' not found.\n",
"```\n",
"\n",
"You might have to install a package that contains the fonts LaTeX needs. On some systems, the packages `texlive-latex-extra` or `cm-super` might be what you need. [See here for more help with this](https://stackoverflow.com/questions/11354149/python-unable-to-render-tex-in-matplotlib).\n",
"\n",
"In case you are curious, `cm` stands for [Computer Modern](https://en.wikipedia.org/wiki/Computer_Modern), the font LaTeX uses to typeset math.\n",
"\n",
"Before we go on, let's put things back where we found them."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [],
"source": [
"plt.rcParams['text.usetex'] = False\n",
"plt.style.use('default')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Multiple panels\n",
"\n",
"So far we've been working with one figure at a time, but the figure we are replicating contains multiple panels, also known as \"subplots\".\n",
"\n",
"Confusingly, Matplotlib provides *three* functions for making figures like this: `subplot`, `subplots`, and `subplot2grid`.\n",
"\n",
"* [`subplot`](https://matplotlib.org/3.3.1/api/_as_gen/matplotlib.pyplot.subplot.html) is simple and similar to MATLAB, so if you are familiar with that interface, you might like `subplot`\n",
"\n",
"* [`subplots`](https://matplotlib.org/3.3.1/api/_as_gen/matplotlib.pyplot.subplots.html) is more object-oriented, which some people prefer.\n",
"\n",
"* [`subplot2grid`](https://matplotlib.org/3.3.1/api/_as_gen/matplotlib.pyplot.subplot2grid.html) is most convenient if you want to control the relative sizes of the subplots. \n",
"\n",
"So we'll use `subplot2grid`.\n",
"\n",
"All of these functions are easier to use if we put the code that generates each panel in a function."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Upper right\n",
"\n",
"To make the panel in the upper right, we have to reload `centerline_df`."
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [],
"source": [
"filename = 'gd1_data.hdf'\n",
"centerline_df = pd.read_hdf(filename, 'centerline_df')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"And define the coordinates of the rectangle we selected."
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
"pm1_min = -8.9\n",
"pm1_max = -6.9\n",
"pm2_min = -2.2\n",
"pm2_max = 1.0\n",
"\n",
"pm1_rect = [pm1_min, pm1_min, pm1_max, pm1_max]\n",
"pm2_rect = [pm2_min, pm2_max, pm2_max, pm2_min]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To plot this rectangle, we'll use a feature we have not seen before: `Polygon`, which is provided by Matplotlib.\n",
"\n",
"To create a `Polygon`, we have to put the coordinates in an array with `x` values in the first column and `y` values in the second column. "
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [],
"source": [
"import numpy as np\n",
"\n",
"vertices = np.transpose([pm1_rect, pm2_rect])\n",
"vertices"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The following function takes a `DataFrame` as a parameter, plots the proper motion for each star, and adds a shaded `Polygon` to show the region we selected."
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [],
"source": [
"from matplotlib.patches import Polygon\n",
"\n",
"def plot_proper_motion(df):\n",
" pm1 = df['pm_phi1']\n",
" pm2 = df['pm_phi2']\n",
"\n",
" plt.plot(pm1, pm2, 'ko', markersize=0.3, alpha=0.3)\n",
" \n",
" poly = Polygon(vertices, closed=True, \n",
" facecolor='C1', alpha=0.4)\n",
" plt.gca().add_patch(poly)\n",
" \n",
" plt.xlabel('$\\mu_{\\phi_1} [\\mathrm{mas~yr}^{-1}]$')\n",
" plt.ylabel('$\\mu_{\\phi_2} [\\mathrm{mas~yr}^{-1}]$')\n",
"\n",
" plt.xlim(-12, 8)\n",
" plt.ylim(-10, 10)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Notice that `add_patch` is like `invert_yaxis`; in order to call it, we have to use `gca` to get the current axes.\n",
"\n",
"Here's what the new version of the figure looks like. We've changed the labels on the axes to be consistent with the paper."
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [],
"source": [
"plot_proper_motion(centerline_df)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Upper left\n",
"\n",
"Now let's work on the panel in the upper left. We have to reload `candidates`."
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [],
"source": [
"filename = 'gd1_data.hdf'\n",
"candidate_df = pd.read_hdf(filename, 'candidate_df')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here's a function that takes a `DataFrame` of candidate stars and plots their positions in GD-1 coordindates. "
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [],
"source": [
"def plot_first_selection(df):\n",
" x = df['phi1']\n",
" y = df['phi2']\n",
"\n",
" plt.plot(x, y, 'ko', markersize=0.3, alpha=0.3)\n",
"\n",
" plt.xlabel('$\\phi_1$ [deg]')\n",
" plt.ylabel('$\\phi_2$ [deg]')\n",
" plt.title('Proper motion selection', fontsize='medium')\n",
"\n",
" plt.axis('equal')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"And here's what it looks like."
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"plot_first_selection(candidate_df)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Lower right\n",
"\n",
"For the figure in the lower right, we'll use this function to plots the color-magnitude diagram."
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"\n",
"def plot_cmd(table):\n",
" \"\"\"Plot a color magnitude diagram.\n",
" \n",
" table: Table or DataFrame with photometry data\n",
" \"\"\"\n",
" y = table['g_mean_psf_mag']\n",
" x = table['g_mean_psf_mag'] - table['i_mean_psf_mag']\n",
"\n",
" plt.plot(x, y, 'ko', markersize=0.3, alpha=0.3)\n",
"\n",
" plt.xlim([0, 1.5])\n",
" plt.ylim([14, 22])\n",
" plt.gca().invert_yaxis()\n",
"\n",
" plt.ylabel('$Magnitude (g)$')\n",
" plt.xlabel('$Color (g-i)$')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here's what it looks like."
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [],
"source": [
"plot_cmd(candidate_df)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"And here's how we read it back."
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [],
"source": [
"filename = 'gd1_data.hdf'\n",
"loop_df = pd.read_hdf(filename, 'loop_df')\n",
"loop_df.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Exercise\n",
"\n",
"Add a few lines to `plot_cmd` to show the polygon we selected as a shaded area. \n",
"\n",
"Hint: pass `coords` as an argument to `Polygon` and plot it using `add_patch`."
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"# Solution goes here"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Subplots\n",
"\n",
"Now we're ready to put it all together. To make a figure with four subplots, we'll use `subplot2grid`, [which requires two arguments](https://matplotlib.org/3.3.1/api/_as_gen/matplotlib.pyplot.subplot2grid.html):\n",
"\n",
"* `shape`, which is a tuple with the number of rows and columns in the grid, and\n",
"\n",
"* `loc`, which is a tuple identifying the location in the grid we're about to fill.\n",
"\n",
"In this example, `shape` is `(2, 2)` to create two rows and two columns.\n",
"\n",
"For the first panel, `loc` is `(0, 0)`, which indicates row 0 and column 0, which is the upper-left panel.\n",
"\n",
"Here's how we use it to draw the four panels."
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {},
"outputs": [],
"source": [
"shape = (2, 2)\n",
"plt.subplot2grid(shape, (0, 0))\n",
"plot_first_selection(candidate_df)\n",
"\n",
"plt.subplot2grid(shape, (0, 1))\n",
"plot_proper_motion(centerline_df)\n",
"\n",
"plt.subplot2grid(shape, (1, 0))\n",
"plot_second_selection(winner_df)\n",
"\n",
"plt.subplot2grid(shape, (1, 1))\n",
"plot_cmd(candidate_df)\n",
"poly = Polygon(loop_df, closed=True, \n",
" facecolor='C1', alpha=0.4)\n",
"plt.gca().add_patch(poly)\n",
"\n",
"plt.tight_layout()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We use [`plt.tight_layout`](https://matplotlib.org/3.3.1/tutorials/intermediate/tight_layout_guide.html) at the end, which adjusts the sizes of the panels to make sure the titles and axis labels don't overlap.\n",
"\n",
"As an exercise, see what happens if you leave out `tight_layout`."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Adjusting proportions\n",
"\n",
"In the previous figure, the panels are all the same size. To get a better view of GD-1, we'd like to stretch the panels on the left and compress the ones on the right.\n",
"\n",
"To do that, we'll use the `colspan` argument to make a panel that spans multiple columns in the grid.\n",
"\n",
"In the following example, `shape` is `(2, 4)`, which means 2 rows and 4 columns.\n",
"\n",
"The panels on the left span three columns, so they are three times wider than the panels on the right.\n",
"\n",
"At the same time, we use `figsize` to adjust the aspect ratio of the whole figure."
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {},
"outputs": [],
"source": [
"plt.figure(figsize=(9, 4.5))\n",
"\n",
"shape = (2, 4)\n",
"plt.subplot2grid(shape, (0, 0), colspan=3)\n",
"plot_first_selection(candidate_df)\n",
"\n",
"plt.subplot2grid(shape, (0, 3))\n",
"plot_proper_motion(centerline_df)\n",
"\n",
"plt.subplot2grid(shape, (1, 0), colspan=3)\n",
"plot_second_selection(winner_df)\n",
"\n",
"plt.subplot2grid(shape, (1, 3))\n",
"plot_cmd(candidate_df)\n",
"poly = Polygon(loop_df, closed=True, \n",
" facecolor='C1', alpha=0.4)\n",
"plt.gca().add_patch(poly)\n",
"\n",
"plt.tight_layout()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This is looking more and more like the figure in the paper."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Exercise\n",
"\n",
"In this example, the ratio of the widths of the panels is 3:1. How would you adjust it if you wanted the ratio to be 3:2?"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {},
"outputs": [],
"source": [
"# Solution goes here"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Summary\n",
"\n",
"In this notebook, we reverse-engineered the figure we've been replicating, identifying elements that seem effective and others that could be improved.\n",
"\n",
"We explored features Matplotlib provides for adding annotations to figures -- including text, lines, arrows, and polygons -- and several ways to customize the appearance of figures. And we learned how to create figures that contain multiple panels."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Best practices\n",
"\n",
"* The most effective figures focus on telling a single story clearly and compellingly.\n",
"\n",
"* Consider using annotations to guide the reader's attention to the most important elements of a figure.\n",
"\n",
"* The default Matplotlib style generates good quality figures, but there are several ways you can override the defaults.\n",
"\n",
"* If you find yourself making the same customizations on several projects, you might want to create your own style sheet."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"celltoolbar": "Tags",
"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.8.8"
}
},
"nbformat": 4,
"nbformat_minor": 2
}