mirror of
https://github.com/AllenDowney/AstronomicalData.git
synced 2025-12-05 20:40:17 -08:00
967 lines
28 KiB
Plaintext
967 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": {},
|
|
"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": 30,
|
|
"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. You can [download the data from the previous lesson](https://github.com/AllenDowney/AstronomicalData/raw/main/data/gd1_data.hdf) or run the following cell, which downloads it if necessary."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 31,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"from os.path import basename, exists\n",
|
|
"\n",
|
|
"def download(url):\n",
|
|
" filename = basename(url)\n",
|
|
" if not exists(filename):\n",
|
|
" from urllib.request import urlretrieve\n",
|
|
" local, _ = urlretrieve(url, filename)\n",
|
|
" print('Downloaded ' + local)\n",
|
|
"\n",
|
|
"download('https://github.com/AllenDowney/AstronomicalData/raw/main/' +\n",
|
|
" 'data/gd1_data.hdf')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"Now we can reload `winner_df`"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 32,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import pandas as pd\n",
|
|
"\n",
|
|
"filename = 'gd1_data.hdf'\n",
|
|
"winner_df = pd.read_hdf(filename, 'winner_df')"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 33,
|
|
"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": 34,
|
|
"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": 35,
|
|
"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": 36,
|
|
"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": 37,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"plt.rcParams['font.size']"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"And sets it to a new value:"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 38,
|
|
"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": 39,
|
|
"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",
|
|
"You can [download the style sheet](https://github.com/AllenDowney/AstronomicalData/raw/main/az-paper-twocol.mplstyle) or run the following cell, which downloads it if necessary."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 40,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"download('https://github.com/AllenDowney/AstronomicalData/raw/main/' +\n",
|
|
" 'az-paper-twocol.mplstyle')"
|
|
]
|
|
},
|
|
{
|
|
"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": 41,
|
|
"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": 42,
|
|
"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": 43,
|
|
"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": 44,
|
|
"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": 45,
|
|
"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": 46,
|
|
"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": 47,
|
|
"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": 48,
|
|
"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": 49,
|
|
"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": 50,
|
|
"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": 51,
|
|
"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": 52,
|
|
"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": 53,
|
|
"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": 54,
|
|
"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": 55,
|
|
"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": 56,
|
|
"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": 57,
|
|
"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
|
|
}
|