mirror of
https://github.com/AllenDowney/AstronomicalData.git
synced 2025-12-25 04:15:38 -08:00
1364 lines
88 KiB
HTML
1364 lines
88 KiB
HTML
|
||
<!DOCTYPE html>
|
||
|
||
<html>
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<title>Chapter 3 — Astronomical Data in Python</title>
|
||
|
||
<link rel="stylesheet" href="_static/css/index.d431a4ee1c1efae0e38bdfebc22debff.css">
|
||
|
||
|
||
<link rel="stylesheet"
|
||
href="_static/vendor/fontawesome/5.13.0/css/all.min.css">
|
||
<link rel="preload" as="font" type="font/woff2" crossorigin
|
||
href="_static/vendor/fontawesome/5.13.0/webfonts/fa-solid-900.woff2">
|
||
<link rel="preload" as="font" type="font/woff2" crossorigin
|
||
href="_static/vendor/fontawesome/5.13.0/webfonts/fa-brands-400.woff2">
|
||
|
||
|
||
|
||
<link rel="stylesheet"
|
||
href="_static/vendor/open-sans_all/1.44.1/index.css">
|
||
<link rel="stylesheet"
|
||
href="_static/vendor/lato_latin-ext/1.44.1/index.css">
|
||
|
||
|
||
<link rel="stylesheet" href="_static/sphinx-book-theme.bfb7730f9caf2ec0b46a44615585038c.css" type="text/css" />
|
||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||
<link rel="stylesheet" type="text/css" href="_static/togglebutton.css" />
|
||
<link rel="stylesheet" type="text/css" href="_static/copybutton.css" />
|
||
<link rel="stylesheet" type="text/css" href="_static/mystnb.css" />
|
||
<link rel="stylesheet" type="text/css" href="_static/sphinx-thebe.css" />
|
||
<link rel="stylesheet" type="text/css" href="_static/panels-main.c949a650a448cc0ae9fd3441c0e17fb0.css" />
|
||
<link rel="stylesheet" type="text/css" href="_static/panels-variables.06eb56fa6e07937060861dad626602ad.css" />
|
||
|
||
<link rel="preload" as="script" href="_static/js/index.30270b6e4c972e43c488.js">
|
||
|
||
<script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
|
||
<script src="_static/jquery.js"></script>
|
||
<script src="_static/underscore.js"></script>
|
||
<script src="_static/doctools.js"></script>
|
||
<script src="_static/language_data.js"></script>
|
||
<script src="_static/togglebutton.js"></script>
|
||
<script src="_static/clipboard.min.js"></script>
|
||
<script src="_static/copybutton.js"></script>
|
||
<script >var togglebuttonSelector = '.toggle, .admonition.dropdown, .tag_hide_input div.cell_input, .tag_hide-input div.cell_input, .tag_hide_output div.cell_output, .tag_hide-output div.cell_output, .tag_hide_cell.cell, .tag_hide-cell.cell';</script>
|
||
<script src="_static/sphinx-book-theme.be0a4a0c39cd630af62a2fcf693f3f06.js"></script>
|
||
<script async="async" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/latest.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||
<script type="text/x-mathjax-config">MathJax.Hub.Config({"tex2jax": {"inlineMath": [["\\(", "\\)"]], "displayMath": [["\\[", "\\]"]], "processRefs": false, "processEnvironments": false}})</script>
|
||
<script async="async" src="https://unpkg.com/thebelab@latest/lib/index.js"></script>
|
||
<script >
|
||
const thebe_selector = ".thebe"
|
||
const thebe_selector_input = "pre"
|
||
const thebe_selector_output = ".output"
|
||
</script>
|
||
<script async="async" src="_static/sphinx-thebe.js"></script>
|
||
<link rel="index" title="Index" href="genindex.html" />
|
||
<link rel="search" title="Search" href="search.html" />
|
||
<link rel="next" title="Chapter 4" href="04_select.html" />
|
||
<link rel="prev" title="Chapter 2" href="02_coords.html" />
|
||
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<meta name="docsearch:language" content="en" />
|
||
|
||
|
||
|
||
</head>
|
||
<body data-spy="scroll" data-target="#bd-toc-nav" data-offset="80">
|
||
|
||
|
||
<div class="container-xl">
|
||
<div class="row">
|
||
|
||
<div class="col-12 col-md-3 bd-sidebar site-navigation show" id="site-navigation">
|
||
|
||
<div class="navbar-brand-box">
|
||
<a class="navbar-brand text-wrap" href="index.html">
|
||
|
||
|
||
<h1 class="site-logo" id="site-title">Astronomical Data in Python</h1>
|
||
|
||
</a>
|
||
</div>
|
||
|
||
<form class="bd-search d-flex align-items-center" action="search.html" method="get">
|
||
<i class="icon fas fa-search"></i>
|
||
<input type="search" class="form-control" name="q" id="search-input" placeholder="Search this book..." aria-label="Search this book..." autocomplete="off" >
|
||
</form>
|
||
|
||
<nav class="bd-links" id="bd-docs-nav" aria-label="Main navigation">
|
||
<ul class="nav sidenav_l1">
|
||
<li class="toctree-l1">
|
||
<a class="reference internal" href="README.html">
|
||
Astronomical Data in Python
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
<ul class="current nav sidenav_l1">
|
||
<li class="toctree-l1">
|
||
<a class="reference internal" href="01_query.html">
|
||
Chapter 1
|
||
</a>
|
||
</li>
|
||
<li class="toctree-l1">
|
||
<a class="reference internal" href="02_coords.html">
|
||
Chapter 2
|
||
</a>
|
||
</li>
|
||
<li class="toctree-l1 current active">
|
||
<a class="current reference internal" href="#">
|
||
Chapter 3
|
||
</a>
|
||
</li>
|
||
<li class="toctree-l1">
|
||
<a class="reference internal" href="04_select.html">
|
||
Chapter 4
|
||
</a>
|
||
</li>
|
||
<li class="toctree-l1">
|
||
<a class="reference internal" href="05_join.html">
|
||
Chapter 5
|
||
</a>
|
||
</li>
|
||
<li class="toctree-l1">
|
||
<a class="reference internal" href="06_photo.html">
|
||
Chapter 6
|
||
</a>
|
||
</li>
|
||
<li class="toctree-l1">
|
||
<a class="reference internal" href="07_plot.html">
|
||
Chapter 7
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
|
||
</nav>
|
||
|
||
<!-- To handle the deprecated key -->
|
||
|
||
<div class="navbar_extra_footer">
|
||
Powered by <a href="https://jupyterbook.org">Jupyter Book</a>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<main class="col py-md-3 pl-md-4 bd-content overflow-auto" role="main">
|
||
|
||
<div class="row topbar fixed-top container-xl">
|
||
<div class="col-12 col-md-3 bd-topbar-whitespace site-navigation show">
|
||
</div>
|
||
<div class="col pl-2 topbar-main">
|
||
|
||
<button id="navbar-toggler" class="navbar-toggler ml-0" type="button" data-toggle="collapse"
|
||
data-toggle="tooltip" data-placement="bottom" data-target=".site-navigation" aria-controls="navbar-menu"
|
||
aria-expanded="true" aria-label="Toggle navigation" aria-controls="site-navigation"
|
||
title="Toggle navigation" data-toggle="tooltip" data-placement="left">
|
||
<i class="fas fa-bars"></i>
|
||
<i class="fas fa-arrow-left"></i>
|
||
<i class="fas fa-arrow-up"></i>
|
||
</button>
|
||
|
||
<div class="dropdown-buttons-trigger">
|
||
<button id="dropdown-buttons-trigger" class="btn btn-secondary topbarbtn" aria-label="Download this page"><i
|
||
class="fas fa-download"></i></button>
|
||
|
||
|
||
<div class="dropdown-buttons">
|
||
<!-- ipynb file if we had a myst markdown file -->
|
||
|
||
<!-- Download raw file -->
|
||
<a class="dropdown-buttons" href="_sources/03_motion.ipynb"><button type="button"
|
||
class="btn btn-secondary topbarbtn" title="Download source file" data-toggle="tooltip"
|
||
data-placement="left">.ipynb</button></a>
|
||
<!-- Download PDF via print -->
|
||
<button type="button" id="download-print" class="btn btn-secondary topbarbtn" title="Print to PDF"
|
||
onClick="window.print()" data-toggle="tooltip" data-placement="left">.pdf</button>
|
||
</div>
|
||
|
||
</div>
|
||
<!-- Source interaction buttons -->
|
||
|
||
<div class="dropdown-buttons-trigger">
|
||
<button id="dropdown-buttons-trigger" class="btn btn-secondary topbarbtn"
|
||
aria-label="Connect with source repository"><i class="fab fa-github"></i></button>
|
||
<div class="dropdown-buttons sourcebuttons">
|
||
<a class="repository-button"
|
||
href="https://github.com/AllenDowney/AstronomyDataPython"><button type="button" class="btn btn-secondary topbarbtn"
|
||
data-toggle="tooltip" data-placement="left" title="Source repository"><i
|
||
class="fab fa-github"></i>repository</button></a>
|
||
|
||
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<!-- Full screen (wrap in <a> to have style consistency -->
|
||
<a class="full-screen-button"><button type="button" class="btn btn-secondary topbarbtn" data-toggle="tooltip"
|
||
data-placement="bottom" onclick="toggleFullScreen()" title="Fullscreen mode"><i
|
||
class="fas fa-expand"></i></button></a>
|
||
|
||
<!-- Launch buttons -->
|
||
|
||
<div class="dropdown-buttons-trigger">
|
||
<button id="dropdown-buttons-trigger" class="btn btn-secondary topbarbtn"
|
||
aria-label="Launch interactive content"><i class="fas fa-rocket"></i></button>
|
||
<div class="dropdown-buttons">
|
||
|
||
<a class="binder-button" href="https://mybinder.org/v2/gh/AllenDowney/AstronomyDataPython/master?urlpath=tree/03_motion.ipynb"><button type="button"
|
||
class="btn btn-secondary topbarbtn" title="Launch Binder" data-toggle="tooltip"
|
||
data-placement="left"><img class="binder-button-logo"
|
||
src="_static/images/logo_binder.svg"
|
||
alt="Interact on binder">Binder</button></a>
|
||
|
||
|
||
|
||
<a class="colab-button" href="https://colab.research.google.com/github/AllenDowney/AstronomyDataPython/blob/master/03_motion.ipynb"><button type="button" class="btn btn-secondary topbarbtn"
|
||
title="Launch Colab" data-toggle="tooltip" data-placement="left"><img class="colab-button-logo"
|
||
src="_static/images/logo_colab.png"
|
||
alt="Interact on Colab">Colab</button></a>
|
||
|
||
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<!-- Table of contents -->
|
||
<div class="d-none d-md-block col-md-2 bd-toc show">
|
||
|
||
<div class="tocsection onthispage pt-5 pb-3">
|
||
<i class="fas fa-list"></i> Contents
|
||
</div>
|
||
<nav id="bd-toc-nav">
|
||
<ul class="nav section-nav flex-column">
|
||
<li class="toc-h2 nav-item toc-entry">
|
||
<a class="reference internal nav-link" href="#outline">
|
||
Outline
|
||
</a>
|
||
</li>
|
||
<li class="toc-h2 nav-item toc-entry">
|
||
<a class="reference internal nav-link" href="#installing-libraries">
|
||
Installing libraries
|
||
</a>
|
||
</li>
|
||
<li class="toc-h2 nav-item toc-entry">
|
||
<a class="reference internal nav-link" href="#reload-the-data">
|
||
Reload the data
|
||
</a>
|
||
</li>
|
||
<li class="toc-h2 nav-item toc-entry">
|
||
<a class="reference internal nav-link" href="#selecting-rows-and-columns">
|
||
Selecting rows and columns
|
||
</a>
|
||
</li>
|
||
<li class="toc-h2 nav-item toc-entry">
|
||
<a class="reference internal nav-link" href="#scatter-plot">
|
||
Scatter plot
|
||
</a>
|
||
</li>
|
||
<li class="toc-h2 nav-item toc-entry">
|
||
<a class="reference internal nav-link" href="#transform-back">
|
||
Transform back
|
||
</a>
|
||
</li>
|
||
<li class="toc-h2 nav-item toc-entry">
|
||
<a class="reference internal nav-link" href="#pandas-dataframe">
|
||
Pandas DataFrame
|
||
</a>
|
||
</li>
|
||
<li class="toc-h2 nav-item toc-entry">
|
||
<a class="reference internal nav-link" href="#plot-proper-motion">
|
||
Plot proper motion
|
||
</a>
|
||
</li>
|
||
<li class="toc-h2 nav-item toc-entry">
|
||
<a class="reference internal nav-link" href="#selecting-the-centerline">
|
||
Selecting the centerline
|
||
</a>
|
||
</li>
|
||
<li class="toc-h2 nav-item toc-entry">
|
||
<a class="reference internal nav-link" href="#filtering-based-on-proper-motion">
|
||
Filtering based on proper motion
|
||
</a>
|
||
</li>
|
||
<li class="toc-h2 nav-item toc-entry">
|
||
<a class="reference internal nav-link" href="#saving-the-dataframe">
|
||
Saving the DataFrame
|
||
</a>
|
||
</li>
|
||
<li class="toc-h2 nav-item toc-entry">
|
||
<a class="reference internal nav-link" href="#summary">
|
||
Summary
|
||
</a>
|
||
</li>
|
||
<li class="toc-h2 nav-item toc-entry">
|
||
<a class="reference internal nav-link" href="#best-practices">
|
||
Best practices
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
|
||
</nav>
|
||
|
||
</div>
|
||
</div>
|
||
<div id="main-content" class="row">
|
||
<div class="col-12 col-md-9 pl-md-3 pr-md-0">
|
||
|
||
<div>
|
||
|
||
<div class="section" id="chapter-3">
|
||
<h1>Chapter 3<a class="headerlink" href="#chapter-3" title="Permalink to this headline">¶</a></h1>
|
||
<p>This is the third in a series of notebooks related to astronomy data.</p>
|
||
<p>As a running example, we are replicating parts of the analysis in a recent paper, “<a class="reference external" href="https://arxiv.org/abs/1805.00425">Off the beaten path: Gaia reveals GD-1 stars outside of the main stream</a>” by Adrian M. Price-Whelan and Ana Bonaca.</p>
|
||
<p>In the first lesson, we wrote ADQL queries and used them to select and download data from the Gaia server.</p>
|
||
<p>In the second lesson, we wrote a query to select stars from the region of the sky where we expect GD-1 to be, and saved the results in a FITS file.</p>
|
||
<p>Now we’ll read that data back and implement the next step in the analysis, identifying stars with the proper motion we expect for GD-1.</p>
|
||
<div class="section" id="outline">
|
||
<h2>Outline<a class="headerlink" href="#outline" title="Permalink to this headline">¶</a></h2>
|
||
<p>Here are the steps in this lesson:</p>
|
||
<ol class="simple">
|
||
<li><p>We’ll read back the results from the previous lesson, which we saved in a FITS file.</p></li>
|
||
<li><p>Then we’ll transform the coordinates and proper motion data from ICRS back to the coordinate frame of GD-1.</p></li>
|
||
<li><p>We’ll put those results into a Pandas <code class="docutils literal notranslate"><span class="pre">DataFrame</span></code>, which we’ll use to select stars near the centerline of GD-1.</p></li>
|
||
<li><p>Plotting the proper motion of those stars, we’ll identify a region of proper motion for stars that are likely to be in GD-1.</p></li>
|
||
<li><p>Finally, we’ll select and plot the stars whose proper motion is in that region.</p></li>
|
||
</ol>
|
||
<p>After completing this lesson, you should be able to</p>
|
||
<ul class="simple">
|
||
<li><p>Select rows and columns from an Astropy <code class="docutils literal notranslate"><span class="pre">Table</span></code>.</p></li>
|
||
<li><p>Use Matplotlib to make a scatter plot.</p></li>
|
||
<li><p>Use Gala to transform coordinates.</p></li>
|
||
<li><p>Make a Pandas <code class="docutils literal notranslate"><span class="pre">DataFrame</span></code> and use a Boolean <code class="docutils literal notranslate"><span class="pre">Series</span></code> to select rows.</p></li>
|
||
<li><p>Save a <code class="docutils literal notranslate"><span class="pre">DataFrame</span></code> in an HDF5 file.</p></li>
|
||
</ul>
|
||
</div>
|
||
<div class="section" id="installing-libraries">
|
||
<h2>Installing libraries<a class="headerlink" href="#installing-libraries" title="Permalink to this headline">¶</a></h2>
|
||
<p>If you are running this notebook on Colab, you can run the following cell to install Astroquery and a the other libraries we’ll use.</p>
|
||
<p>If you are running this notebook on your own computer, you might have to install these libraries yourself.</p>
|
||
<p>If you are using this notebook as part of a Carpentries workshop, you should have received setup instructions.</p>
|
||
<p>TODO: Add a link to the instructions.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="c1"># If we're running on Colab, install libraries</span>
|
||
|
||
<span class="kn">import</span> <span class="nn">sys</span>
|
||
<span class="n">IN_COLAB</span> <span class="o">=</span> <span class="s1">'google.colab'</span> <span class="ow">in</span> <span class="n">sys</span><span class="o">.</span><span class="n">modules</span>
|
||
|
||
<span class="k">if</span> <span class="n">IN_COLAB</span><span class="p">:</span>
|
||
<span class="o">!</span>pip install astroquery astro-gala pyia python-wget
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="reload-the-data">
|
||
<h2>Reload the data<a class="headerlink" href="#reload-the-data" title="Permalink to this headline">¶</a></h2>
|
||
<p>In the previous lesson, we ran a query on the Gaia server and downloaded data for roughly 100,000 stars. We saved the data in a FITS file so that now, picking up where we left off, we can read the data from a local file rather than running the query again.</p>
|
||
<p>If you ran the previous lesson successfully, you should already have a file called <code class="docutils literal notranslate"><span class="pre">gd1_results.fits</span></code> that contains the data we downloaded.</p>
|
||
<p>If not, you can run the following cell, which downloads the data from our repository.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">os</span>
|
||
<span class="kn">from</span> <span class="nn">wget</span> <span class="kn">import</span> <span class="n">download</span>
|
||
|
||
<span class="n">filename</span> <span class="o">=</span> <span class="s1">'gd1_results.fits'</span>
|
||
<span class="n">path</span> <span class="o">=</span> <span class="s1">'https://github.com/AllenDowney/AstronomicalData/raw/main/data/'</span>
|
||
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">exists</span><span class="p">(</span><span class="n">filename</span><span class="p">):</span>
|
||
<span class="nb">print</span><span class="p">(</span><span class="n">download</span><span class="p">(</span><span class="n">path</span><span class="o">+</span><span class="n">filename</span><span class="p">))</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>Now here’s how we can read the data from the file back into an Astropy <code class="docutils literal notranslate"><span class="pre">Table</span></code>:</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">astropy.table</span> <span class="kn">import</span> <span class="n">Table</span>
|
||
|
||
<span class="n">results</span> <span class="o">=</span> <span class="n">Table</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>The result is an Astropy <code class="docutils literal notranslate"><span class="pre">Table</span></code>.</p>
|
||
<p>We can use <code class="docutils literal notranslate"><span class="pre">info</span></code> to refresh our memory of the contents.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">results</span><span class="o">.</span><span class="n">info</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_plain highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span><Table length=140340>
|
||
name dtype unit description
|
||
--------------- ------- -------- ------------------------------------------------------------------
|
||
source_id int64 Unique source identifier (unique within a particular Data Release)
|
||
ra float64 deg Right ascension
|
||
dec float64 deg Declination
|
||
pmra float64 mas / yr Proper motion in right ascension direction
|
||
pmdec float64 mas / yr Proper motion in declination direction
|
||
parallax float64 mas Parallax
|
||
parallax_error float64 mas Standard error of parallax
|
||
radial_velocity float64 km / s Radial velocity
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="selecting-rows-and-columns">
|
||
<h2>Selecting rows and columns<a class="headerlink" href="#selecting-rows-and-columns" title="Permalink to this headline">¶</a></h2>
|
||
<p>In this section we’ll see operations for selecting columns and rows from an Astropy <code class="docutils literal notranslate"><span class="pre">Table</span></code>. You can find more information about these operations in the <a class="reference external" href="https://docs.astropy.org/en/stable/table/access_table.html">Astropy documentation</a>.</p>
|
||
<p>We can get the names of the columns like this:</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">results</span><span class="o">.</span><span class="n">colnames</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_plain highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span>['source_id',
|
||
'ra',
|
||
'dec',
|
||
'pmra',
|
||
'pmdec',
|
||
'parallax',
|
||
'parallax_error',
|
||
'radial_velocity']
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>And select an individual column like this:</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">results</span><span class="p">[</span><span class="s1">'ra'</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_html"><Column name='ra' dtype='float64' unit='deg' description='Right ascension' length=140340>
|
||
<table>
|
||
<tr><td>142.48301935991023</td></tr>
|
||
<tr><td>142.25452941346344</td></tr>
|
||
<tr><td>142.64528557468074</td></tr>
|
||
<tr><td>142.57739430926034</td></tr>
|
||
<tr><td>142.58913564478618</td></tr>
|
||
<tr><td>141.81762228999614</td></tr>
|
||
<tr><td>143.18339801317677</td></tr>
|
||
<tr><td>142.9347319464589</td></tr>
|
||
<tr><td>142.26769745823267</td></tr>
|
||
<tr><td>142.89551292869012</td></tr>
|
||
<tr><td>142.2780935768316</td></tr>
|
||
<tr><td>142.06138786534987</td></tr>
|
||
<tr><td>...</td></tr>
|
||
<tr><td>143.05456487172972</td></tr>
|
||
<tr><td>144.0436496516182</td></tr>
|
||
<tr><td>144.06566578919313</td></tr>
|
||
<tr><td>144.13177563215973</td></tr>
|
||
<tr><td>143.77696341662764</td></tr>
|
||
<tr><td>142.945956347594</td></tr>
|
||
<tr><td>142.97282480557786</td></tr>
|
||
<tr><td>143.4166017695258</td></tr>
|
||
<tr><td>143.64484588686904</td></tr>
|
||
<tr><td>143.41554585481808</td></tr>
|
||
<tr><td>143.6908739159247</td></tr>
|
||
<tr><td>143.7702681295401</td></tr>
|
||
</table></div></div>
|
||
</div>
|
||
<p>The result is a <code class="docutils literal notranslate"><span class="pre">Column</span></code> object that contains the data, and also the data type, units, and name of the column.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="nb">type</span><span class="p">(</span><span class="n">results</span><span class="p">[</span><span class="s1">'ra'</span><span class="p">])</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_plain highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span>astropy.table.column.Column
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>The rows in the <code class="docutils literal notranslate"><span class="pre">Table</span></code> are numbered from 0 to <code class="docutils literal notranslate"><span class="pre">n-1</span></code>, where <code class="docutils literal notranslate"><span class="pre">n</span></code> is the number of rows. We can select the first row like this:</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">results</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_html"><i>Row index=0</i>
|
||
<table id="table140510732444000">
|
||
<thead><tr><th>source_id</th><th>ra</th><th>dec</th><th>pmra</th><th>pmdec</th><th>parallax</th><th>parallax_error</th><th>radial_velocity</th></tr></thead>
|
||
<thead><tr><th></th><th>deg</th><th>deg</th><th>mas / yr</th><th>mas / yr</th><th>mas</th><th>mas</th><th>km / s</th></tr></thead>
|
||
<thead><tr><th>int64</th><th>float64</th><th>float64</th><th>float64</th><th>float64</th><th>float64</th><th>float64</th><th>float64</th></tr></thead>
|
||
<tr><td>637987125186749568</td><td>142.48301935991023</td><td>21.75771616932985</td><td>-2.5168384683875766</td><td>2.941813096629439</td><td>-0.2573448962333354</td><td>0.823720794509811</td><td>1e+20</td></tr>
|
||
</table></div></div>
|
||
</div>
|
||
<p>As you might have guessed, the result is a <code class="docutils literal notranslate"><span class="pre">Row</span></code> object.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="nb">type</span><span class="p">(</span><span class="n">results</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_plain highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span>astropy.table.row.Row
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>Notice that the bracket operator selects both columns and rows. You might wonder how it knows which to select.</p>
|
||
<p>If the expression in brackets is a string, it selects a column; if the expression is an integer, it selects a row.</p>
|
||
<p>If you apply the bracket operator twice, you can select a column and then an element from the column.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">results</span><span class="p">[</span><span class="s1">'ra'</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_plain highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span>142.48301935991023
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>Or you can select a row and then an element from the row.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">results</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="s1">'ra'</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_plain highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span>142.48301935991023
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>You get the same result either way.</p>
|
||
</div>
|
||
<div class="section" id="scatter-plot">
|
||
<h2>Scatter plot<a class="headerlink" href="#scatter-plot" title="Permalink to this headline">¶</a></h2>
|
||
<p>To see what the results look like, we’ll use a scatter plot. The library we’ll use is <a class="reference external" href="https://matplotlib.org/">Matplotlib</a>, which is the most widely-used plotting library for Python.</p>
|
||
<p>The Matplotlib interface is based on MATLAB (hence the name), so if you know MATLAB, some of it will be familiar.</p>
|
||
<p>We’ll import like this.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="nn">plt</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>Pyplot part of the Matplotlib library. It is conventional to import it using the shortened name <code class="docutils literal notranslate"><span class="pre">plt</span></code>.</p>
|
||
<p>Pyplot provides two functions that can make scatterplots, <a class="reference external" href="https://matplotlib.org/3.3.0/api/_as_gen/matplotlib.pyplot.scatter.html">plt.scatter</a> and <a class="reference external" href="https://matplotlib.org/api/_as_gen/matplotlib.pyplot.plot.html">plt.plot</a>.</p>
|
||
<ul class="simple">
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">scatter</span></code> is more versatile; for example, you can make every point in a scatter plot a different color.</p></li>
|
||
<li><p><code class="docutils literal notranslate"><span class="pre">plot</span></code> is more limited, but for simple cases, it can be substantially faster.</p></li>
|
||
</ul>
|
||
<p>Jake Vanderplas explains these differences in <a class="reference external" href="https://jakevdp.github.io/PythonDataScienceHandbook/04.02-simple-scatter-plots.html">The Python Data Science Handbook</a></p>
|
||
<p>Since we are plotting more than 100,000 points and they are all the same size and color, we’ll use <code class="docutils literal notranslate"><span class="pre">plot</span></code>.</p>
|
||
<p>Here’s a scatter plot with right ascension on the x-axis and declination on the y-axis, both ICRS coordinates in degrees.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">x</span> <span class="o">=</span> <span class="n">results</span><span class="p">[</span><span class="s1">'ra'</span><span class="p">]</span>
|
||
<span class="n">y</span> <span class="o">=</span> <span class="n">results</span><span class="p">[</span><span class="s1">'dec'</span><span class="p">]</span>
|
||
<span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="s1">'ko'</span><span class="p">)</span>
|
||
|
||
<span class="n">plt</span><span class="o">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s1">'ra (degree ICRS)'</span><span class="p">)</span>
|
||
<span class="n">plt</span><span class="o">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s1">'dec (degree ICRS)'</span><span class="p">);</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<img alt="_images/03_motion_28_0.png" src="_images/03_motion_28_0.png" />
|
||
</div>
|
||
</div>
|
||
<p>The arguments to <code class="docutils literal notranslate"><span class="pre">plt.plot</span></code> are <code class="docutils literal notranslate"><span class="pre">x</span></code>, <code class="docutils literal notranslate"><span class="pre">y</span></code>, and a string that specifies the style. In this case, the letters <code class="docutils literal notranslate"><span class="pre">ko</span></code> indicate that we want a black, round marker (<code class="docutils literal notranslate"><span class="pre">k</span></code> is for black because <code class="docutils literal notranslate"><span class="pre">b</span></code> is for blue).</p>
|
||
<p>The functions <code class="docutils literal notranslate"><span class="pre">xlabel</span></code> and <code class="docutils literal notranslate"><span class="pre">ylabel</span></code> put labels on the axes.</p>
|
||
<p>This scatter plot has a problem. It is “<a class="reference external" href="https://python-graph-gallery.com/134-how-to-avoid-overplotting-with-python/">overplotted</a>”, which means that there are so many overlapping points, we can’t distinguish between high and low density areas.</p>
|
||
<p>To fix this, we can provide optional arguments to control the size and transparency of the points.</p>
|
||
<p><strong>Exercise:</strong> In the call to <code class="docutils literal notranslate"><span class="pre">plt.plot</span></code>, add the keyword argument <code class="docutils literal notranslate"><span class="pre">markersize=0.1</span></code> to make the markers smaller.</p>
|
||
<p>Then add the argument <code class="docutils literal notranslate"><span class="pre">alpha=0.1</span></code> to make the markers nearly transparent.</p>
|
||
<p>Adjust these arguments until you think the figure shows the data most clearly.</p>
|
||
<p>Note: Once you have made these changes, you might notice that the figure shows stripes with lower density of stars. These stripes are caused by the way Gaia scans the sky, which <a class="reference external" href="https://www.cosmos.esa.int/web/gaia/scanning-law">you can read about here</a>. The dataset we are using, <a class="reference external" href="https://www.cosmos.esa.int/web/gaia/dr2">Gaia Data Release 2</a>, covers 22 months of observations; during this time, some parts of the sky were scanned more than others.</p>
|
||
</div>
|
||
<div class="section" id="transform-back">
|
||
<h2>Transform back<a class="headerlink" href="#transform-back" title="Permalink to this headline">¶</a></h2>
|
||
<p>Remember that we selected data from a rectangle of coordinates in the <code class="docutils literal notranslate"><span class="pre">GD1Koposov10</span></code> frame, then transformed them to ICRS when we constructed the query.
|
||
The coordinates in <code class="docutils literal notranslate"><span class="pre">results</span></code> are in ICRS.</p>
|
||
<p>To plot them, we will transform them back to the <code class="docutils literal notranslate"><span class="pre">GD1Koposov10</span></code> frame; that way, the axes of the figure are aligned with the GD-1, which will make it easy to select stars near the centerline of the stream.</p>
|
||
<p>To do that, we’ll put the results into a <code class="docutils literal notranslate"><span class="pre">GaiaData</span></code> object, provided by the <a class="reference external" href="https://pyia.readthedocs.io/en/latest/api/pyia.GaiaData.html">pyia library</a>.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">pyia</span> <span class="kn">import</span> <span class="n">GaiaData</span>
|
||
|
||
<span class="n">gaia_data</span> <span class="o">=</span> <span class="n">GaiaData</span><span class="p">(</span><span class="n">results</span><span class="p">)</span>
|
||
<span class="nb">type</span><span class="p">(</span><span class="n">gaia_data</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_plain highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span>pyia.data.GaiaData
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>Now we can extract sky coordinates from the <code class="docutils literal notranslate"><span class="pre">GaiaData</span></code> object, like this:</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">astropy.units</span> <span class="k">as</span> <span class="nn">u</span>
|
||
|
||
<span class="n">skycoord</span> <span class="o">=</span> <span class="n">gaia_data</span><span class="o">.</span><span class="n">get_skycoord</span><span class="p">(</span>
|
||
<span class="n">distance</span><span class="o">=</span><span class="mi">8</span><span class="o">*</span><span class="n">u</span><span class="o">.</span><span class="n">kpc</span><span class="p">,</span>
|
||
<span class="n">radial_velocity</span><span class="o">=</span><span class="mi">0</span><span class="o">*</span><span class="n">u</span><span class="o">.</span><span class="n">km</span><span class="o">/</span><span class="n">u</span><span class="o">.</span><span class="n">s</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>We provide <code class="docutils literal notranslate"><span class="pre">distance</span></code> and <code class="docutils literal notranslate"><span class="pre">radial_velocity</span></code> to prepare the data for reflex correction, which we explain below.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="nb">type</span><span class="p">(</span><span class="n">skycoord</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_plain highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span>astropy.coordinates.sky_coordinate.SkyCoord
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>The result is an Astropy <code class="docutils literal notranslate"><span class="pre">SkyCoord</span></code> object (<a class="reference external" href="https://docs.astropy.org/en/stable/api/astropy.coordinates.SkyCoord.html#astropy.coordinates.SkyCoord">documentation here</a>), which provides <code class="docutils literal notranslate"><span class="pre">transform_to</span></code>, so we can transform the coordinates to other frames.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">gala.coordinates</span> <span class="k">as</span> <span class="nn">gc</span>
|
||
|
||
<span class="n">transformed</span> <span class="o">=</span> <span class="n">skycoord</span><span class="o">.</span><span class="n">transform_to</span><span class="p">(</span><span class="n">gc</span><span class="o">.</span><span class="n">GD1Koposov10</span><span class="p">)</span>
|
||
<span class="nb">type</span><span class="p">(</span><span class="n">transformed</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_plain highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span>astropy.coordinates.sky_coordinate.SkyCoord
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>The result is another <code class="docutils literal notranslate"><span class="pre">SkyCoord</span></code> object, now in the <code class="docutils literal notranslate"><span class="pre">GD1Koposov10</span></code> frame.</p>
|
||
<p>The next step is to correct the proper motion measurements from Gaia for reflex due to the motion of our solar system around the Galactic center.</p>
|
||
<p>When we created <code class="docutils literal notranslate"><span class="pre">skycoord</span></code>, we provided <code class="docutils literal notranslate"><span class="pre">distance</span></code> and <code class="docutils literal notranslate"><span class="pre">radial_velocity</span></code> as arguments, which means we ignore the measurements provided by Gaia and replace them with these fixed values.</p>
|
||
<p>That might seem like a strange thing to do, but here’s the motivation:</p>
|
||
<ul class="simple">
|
||
<li><p>Because the stars in GD-1 are so far away, the distance estimates we get from Gaia, which are based on parallax, are not very precise. So we replace them with our current best estimate of the mean distance to GD-1, about 8 kpc. See <a class="reference external" href="https://ui.adsabs.harvard.edu/abs/2010ApJ...712..260K/abstract">Koposov, Rix, and Hogg, 2010</a>.</p></li>
|
||
<li><p>For the other stars in the table, this distance estimate will be inaccurate, so reflex correction will not be correct. But that should have only a small effect on our ability to identify stars with the proper motion we expect for GD-1.</p></li>
|
||
<li><p>The measurement of radial velocity has no effect on the correction for proper motion; the value we provide is arbitrary, but we have to provide a value to avoid errors in the reflex correction calculation.</p></li>
|
||
</ul>
|
||
<p>We are grateful to Adrian Price-Whelen for his help explaining this step in the analysis.</p>
|
||
<p>With this preparation, we can use <code class="docutils literal notranslate"><span class="pre">reflex_correct</span></code> from Gala (<a class="reference external" href="https://gala-astro.readthedocs.io/en/latest/api/gala.coordinates.reflex_correct.html">documentation here</a>) to correct for solar reflex motion.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">gd1_coord</span> <span class="o">=</span> <span class="n">gc</span><span class="o">.</span><span class="n">reflex_correct</span><span class="p">(</span><span class="n">transformed</span><span class="p">)</span>
|
||
|
||
<span class="nb">type</span><span class="p">(</span><span class="n">gd1_coord</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_plain highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span>astropy.coordinates.sky_coordinate.SkyCoord
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>The result is a <code class="docutils literal notranslate"><span class="pre">SkyCoord</span></code> object that contains</p>
|
||
<ul class="simple">
|
||
<li><p>The transformed coordinates as attributes named <code class="docutils literal notranslate"><span class="pre">phi1</span></code> and <code class="docutils literal notranslate"><span class="pre">phi2</span></code>, which represent right ascension and declination in the <code class="docutils literal notranslate"><span class="pre">GD1Koposov10</span></code> frame.</p></li>
|
||
<li><p>The transformed and corrected proper motions as <code class="docutils literal notranslate"><span class="pre">pm_phi1_cosphi2</span></code> and <code class="docutils literal notranslate"><span class="pre">pm_phi2</span></code>.</p></li>
|
||
</ul>
|
||
<p>We can select the coordinates like this:</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">phi1</span> <span class="o">=</span> <span class="n">gd1_coord</span><span class="o">.</span><span class="n">phi1</span>
|
||
<span class="n">phi2</span> <span class="o">=</span> <span class="n">gd1_coord</span><span class="o">.</span><span class="n">phi2</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>And plot them like this:</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">phi1</span><span class="p">,</span> <span class="n">phi2</span><span class="p">,</span> <span class="s1">'ko'</span><span class="p">,</span> <span class="n">markersize</span><span class="o">=</span><span class="mf">0.1</span><span class="p">,</span> <span class="n">alpha</span><span class="o">=</span><span class="mf">0.2</span><span class="p">)</span>
|
||
|
||
<span class="n">plt</span><span class="o">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s1">'ra (degree GD1)'</span><span class="p">)</span>
|
||
<span class="n">plt</span><span class="o">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s1">'dec (degree GD1)'</span><span class="p">);</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<img alt="_images/03_motion_45_0.png" src="_images/03_motion_45_0.png" />
|
||
</div>
|
||
</div>
|
||
<p>Remember that we started with a rectangle in GD-1 coordinates. When transformed to ICRS, it’s a non-rectangular polygon. Now that we have transformed back to GD-1 coordinates, it’s a rectangle again.</p>
|
||
</div>
|
||
<div class="section" id="pandas-dataframe">
|
||
<h2>Pandas DataFrame<a class="headerlink" href="#pandas-dataframe" title="Permalink to this headline">¶</a></h2>
|
||
<p>At this point we have three objects containing different subsets of the data.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="nb">type</span><span class="p">(</span><span class="n">results</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_plain highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span>astropy.table.table.Table
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="nb">type</span><span class="p">(</span><span class="n">gaia_data</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_plain highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span>pyia.data.GaiaData
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="nb">type</span><span class="p">(</span><span class="n">gd1_coord</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_plain highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span>astropy.coordinates.sky_coordinate.SkyCoord
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>On one hand, this makes sense, since each object provides different capabilities. But working with three different object types can be awkward.</p>
|
||
<p>It will be more convenient to choose one object and get all of the data into it. We’ll use a Pandas DataFrame, for two reasons:</p>
|
||
<ol class="simple">
|
||
<li><p>It provides capabilities that are pretty much a superset of the other data structures, so it’s the all-in-one solution.</p></li>
|
||
<li><p>Pandas is a general-purpose tool that is useful in many domains, especially data science. If you are going to develop expertise in one tool, Pandas is a good choice.</p></li>
|
||
</ol>
|
||
<p>However, compared to an Astropy <code class="docutils literal notranslate"><span class="pre">Table</span></code>, Pandas has one big drawback: it does not keep the metadata associated with the table, including the units for the columns.</p>
|
||
<p>It’s easy to convert a <code class="docutils literal notranslate"><span class="pre">Table</span></code> to a Pandas <code class="docutils literal notranslate"><span class="pre">DataFrame</span></code>.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="nn">pd</span>
|
||
|
||
<span class="n">df</span> <span class="o">=</span> <span class="n">results</span><span class="o">.</span><span class="n">to_pandas</span><span class="p">()</span>
|
||
<span class="n">df</span><span class="o">.</span><span class="n">shape</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_plain highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span>(140340, 8)
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p><code class="docutils literal notranslate"><span class="pre">DataFrame</span></code> provides <code class="docutils literal notranslate"><span class="pre">shape</span></code>, which shows the number of rows and columns.</p>
|
||
<p>It also provides <code class="docutils literal notranslate"><span class="pre">head</span></code>, which displays the first few rows. It is useful for spot-checking large results as you go along.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">df</span><span class="o">.</span><span class="n">head</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_html"><div>
|
||
<style scoped>
|
||
.dataframe tbody tr th:only-of-type {
|
||
vertical-align: middle;
|
||
}
|
||
|
||
.dataframe tbody tr th {
|
||
vertical-align: top;
|
||
}
|
||
|
||
.dataframe thead th {
|
||
text-align: right;
|
||
}
|
||
</style>
|
||
<table border="1" class="dataframe">
|
||
<thead>
|
||
<tr style="text-align: right;">
|
||
<th></th>
|
||
<th>source_id</th>
|
||
<th>ra</th>
|
||
<th>dec</th>
|
||
<th>pmra</th>
|
||
<th>pmdec</th>
|
||
<th>parallax</th>
|
||
<th>parallax_error</th>
|
||
<th>radial_velocity</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<th>0</th>
|
||
<td>637987125186749568</td>
|
||
<td>142.483019</td>
|
||
<td>21.757716</td>
|
||
<td>-2.516838</td>
|
||
<td>2.941813</td>
|
||
<td>-0.257345</td>
|
||
<td>0.823721</td>
|
||
<td>1.000000e+20</td>
|
||
</tr>
|
||
<tr>
|
||
<th>1</th>
|
||
<td>638285195917112960</td>
|
||
<td>142.254529</td>
|
||
<td>22.476168</td>
|
||
<td>2.662702</td>
|
||
<td>-12.165984</td>
|
||
<td>0.422728</td>
|
||
<td>0.297472</td>
|
||
<td>1.000000e+20</td>
|
||
</tr>
|
||
<tr>
|
||
<th>2</th>
|
||
<td>638073505568978688</td>
|
||
<td>142.645286</td>
|
||
<td>22.166932</td>
|
||
<td>18.306747</td>
|
||
<td>-7.950660</td>
|
||
<td>0.103640</td>
|
||
<td>0.544584</td>
|
||
<td>1.000000e+20</td>
|
||
</tr>
|
||
<tr>
|
||
<th>3</th>
|
||
<td>638086386175786752</td>
|
||
<td>142.577394</td>
|
||
<td>22.227920</td>
|
||
<td>0.987786</td>
|
||
<td>-2.584105</td>
|
||
<td>-0.857327</td>
|
||
<td>1.059607</td>
|
||
<td>1.000000e+20</td>
|
||
</tr>
|
||
<tr>
|
||
<th>4</th>
|
||
<td>638049655615392384</td>
|
||
<td>142.589136</td>
|
||
<td>22.110783</td>
|
||
<td>0.244439</td>
|
||
<td>-4.941079</td>
|
||
<td>0.099625</td>
|
||
<td>0.486224</td>
|
||
<td>1.000000e+20</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div></div></div>
|
||
</div>
|
||
<p>Python detail: <code class="docutils literal notranslate"><span class="pre">shape</span></code> is an attribute, so we can display it’s value without calling it as a function; <code class="docutils literal notranslate"><span class="pre">head</span></code> is a function, so we need the parentheses.</p>
|
||
<p>Now we can extract the columns we want from <code class="docutils literal notranslate"><span class="pre">gd1_coord</span></code> and add them as columns in the <code class="docutils literal notranslate"><span class="pre">DataFrame</span></code>. <code class="docutils literal notranslate"><span class="pre">phi1</span></code> and <code class="docutils literal notranslate"><span class="pre">phi2</span></code> contain the transformed coordinates.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">df</span><span class="p">[</span><span class="s1">'phi1'</span><span class="p">]</span> <span class="o">=</span> <span class="n">gd1_coord</span><span class="o">.</span><span class="n">phi1</span>
|
||
<span class="n">df</span><span class="p">[</span><span class="s1">'phi2'</span><span class="p">]</span> <span class="o">=</span> <span class="n">gd1_coord</span><span class="o">.</span><span class="n">phi2</span>
|
||
<span class="n">df</span><span class="o">.</span><span class="n">shape</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_plain highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span>(140340, 10)
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p><code class="docutils literal notranslate"><span class="pre">pm_phi1_cosphi2</span></code> and <code class="docutils literal notranslate"><span class="pre">pm_phi2</span></code> contain the components of proper motion in the transformed frame.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">df</span><span class="p">[</span><span class="s1">'pm_phi1'</span><span class="p">]</span> <span class="o">=</span> <span class="n">gd1_coord</span><span class="o">.</span><span class="n">pm_phi1_cosphi2</span>
|
||
<span class="n">df</span><span class="p">[</span><span class="s1">'pm_phi2'</span><span class="p">]</span> <span class="o">=</span> <span class="n">gd1_coord</span><span class="o">.</span><span class="n">pm_phi2</span>
|
||
<span class="n">df</span><span class="o">.</span><span class="n">shape</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_plain highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span>(140340, 12)
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p><strong>Detail:</strong> If you notice that <code class="docutils literal notranslate"><span class="pre">SkyCoord</span></code> has an attribute called <code class="docutils literal notranslate"><span class="pre">proper_motion</span></code>, you might wonder why we are not using it.</p>
|
||
<p>We could have: <code class="docutils literal notranslate"><span class="pre">proper_motion</span></code> contains the same data as <code class="docutils literal notranslate"><span class="pre">pm_phi1_cosphi2</span></code> and <code class="docutils literal notranslate"><span class="pre">pm_phi2</span></code>, but in a different format.</p>
|
||
</div>
|
||
<div class="section" id="plot-proper-motion">
|
||
<h2>Plot proper motion<a class="headerlink" href="#plot-proper-motion" title="Permalink to this headline">¶</a></h2>
|
||
<p>Now we are ready to replicate one of the panels in Figure 1 of the Price-Whelan and Bonaca paper, the one that shows the components of proper motion as a scatter plot:</p>
|
||
<a class="reference internal image-reference" href="https://github.com/datacarpentry/astronomy-python/raw/gh-pages/fig/gd1-1.png"><img alt="https://github.com/datacarpentry/astronomy-python/raw/gh-pages/fig/gd1-1.png" src="https://github.com/datacarpentry/astronomy-python/raw/gh-pages/fig/gd1-1.png" style="width: 300px;" /></a>
|
||
<p>In this figure, the shaded area is a high-density region of stars with the proper motion we expect for stars in GD-1.</p>
|
||
<ul class="simple">
|
||
<li><p>Due to the nature of tidal streams, we expect the proper motion for most stars to be along the axis of the stream; that is, we expect motion in the direction of <code class="docutils literal notranslate"><span class="pre">phi2</span></code> to be near 0.</p></li>
|
||
<li><p>In the direction of <code class="docutils literal notranslate"><span class="pre">phi1</span></code>, we don’t have a prior expectation for proper motion, except that it should form a cluster at a non-zero value.</p></li>
|
||
</ul>
|
||
<p>To locate this cluster, we’ll select stars near the centerline of GD-1 and plot their proper motion.</p>
|
||
</div>
|
||
<div class="section" id="selecting-the-centerline">
|
||
<h2>Selecting the centerline<a class="headerlink" href="#selecting-the-centerline" title="Permalink to this headline">¶</a></h2>
|
||
<p>As we can see in the following figure, many stars in GD-1 are less than 1 degree of declination from the line <code class="docutils literal notranslate"><span class="pre">phi2=0</span></code>.</p>
|
||
<img alt="https://github.com/datacarpentry/astronomy-python/raw/gh-pages/fig/gd1-4.png" src="https://github.com/datacarpentry/astronomy-python/raw/gh-pages/fig/gd1-4.png" />
|
||
<p>If we select stars near this line, they are more likely to be in GD-1.</p>
|
||
<p>We’ll start by selecting the <code class="docutils literal notranslate"><span class="pre">phi2</span></code> column from the <code class="docutils literal notranslate"><span class="pre">DataFrame</span></code>:</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">phi2</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="s1">'phi2'</span><span class="p">]</span>
|
||
<span class="nb">type</span><span class="p">(</span><span class="n">phi2</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_plain highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span>pandas.core.series.Series
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>The result is a <code class="docutils literal notranslate"><span class="pre">Series</span></code>, which is the structure Pandas uses to represent columns.</p>
|
||
<p>We can use a comparison operator, <code class="docutils literal notranslate"><span class="pre">></span></code>, to compare the values in a <code class="docutils literal notranslate"><span class="pre">Series</span></code> to a constant.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">phi2_min</span> <span class="o">=</span> <span class="o">-</span><span class="mf">1.0</span> <span class="o">*</span> <span class="n">u</span><span class="o">.</span><span class="n">deg</span>
|
||
<span class="n">phi2_max</span> <span class="o">=</span> <span class="mf">1.0</span> <span class="o">*</span> <span class="n">u</span><span class="o">.</span><span class="n">deg</span>
|
||
|
||
<span class="n">mask</span> <span class="o">=</span> <span class="p">(</span><span class="n">df</span><span class="p">[</span><span class="s1">'phi2'</span><span class="p">]</span> <span class="o">></span> <span class="n">phi2_min</span><span class="p">)</span>
|
||
<span class="nb">type</span><span class="p">(</span><span class="n">mask</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_plain highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span>pandas.core.series.Series
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">mask</span><span class="o">.</span><span class="n">dtype</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_plain highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span>dtype('bool')
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>The result is a <code class="docutils literal notranslate"><span class="pre">Series</span></code> of Boolean values, that is, <code class="docutils literal notranslate"><span class="pre">True</span></code> and <code class="docutils literal notranslate"><span class="pre">False</span></code>.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">mask</span><span class="o">.</span><span class="n">head</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_plain highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span>0 False
|
||
1 False
|
||
2 False
|
||
3 False
|
||
4 False
|
||
Name: phi2, dtype: bool
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>A Boolean <code class="docutils literal notranslate"><span class="pre">Series</span></code> is sometimes called a “mask” because we can use it to mask out some of the rows in a <code class="docutils literal notranslate"><span class="pre">DataFrame</span></code> and select the rest, like this:</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">selected</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="n">mask</span><span class="p">]</span>
|
||
<span class="nb">type</span><span class="p">(</span><span class="n">selected</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_plain highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span>pandas.core.frame.DataFrame
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p><code class="docutils literal notranslate"><span class="pre">selected</span></code> is a <code class="docutils literal notranslate"><span class="pre">DataFrame</span></code> that contains only the rows from <code class="docutils literal notranslate"><span class="pre">df</span></code> that correspond to <code class="docutils literal notranslate"><span class="pre">True</span></code> values in <code class="docutils literal notranslate"><span class="pre">mask</span></code>.</p>
|
||
<p>The previous mask selects all stars where <code class="docutils literal notranslate"><span class="pre">phi2</span></code> exceeds <code class="docutils literal notranslate"><span class="pre">phi2_min</span></code>; now we’ll select stars where <code class="docutils literal notranslate"><span class="pre">phi2</span></code> falls between <code class="docutils literal notranslate"><span class="pre">phi2_min</span></code> and <code class="docutils literal notranslate"><span class="pre">phi2_max</span></code>.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">phi_mask</span> <span class="o">=</span> <span class="p">((</span><span class="n">df</span><span class="p">[</span><span class="s1">'phi2'</span><span class="p">]</span> <span class="o">></span> <span class="n">phi2_min</span><span class="p">)</span> <span class="o">&</span>
|
||
<span class="p">(</span><span class="n">df</span><span class="p">[</span><span class="s1">'phi2'</span><span class="p">]</span> <span class="o"><</span> <span class="n">phi2_max</span><span class="p">))</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">&</span></code> operator computes “logical AND”, which means the result is true where elements from both Boolean <code class="docutils literal notranslate"><span class="pre">Series</span></code> are true.</p>
|
||
<p>The sum of a Boolean <code class="docutils literal notranslate"><span class="pre">Series</span></code> is the number of <code class="docutils literal notranslate"><span class="pre">True</span></code> values, so we can use <code class="docutils literal notranslate"><span class="pre">sum</span></code> to see how many stars are in the selected region.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">phi_mask</span><span class="o">.</span><span class="n">sum</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_plain highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span>25084
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>And we can use <code class="docutils literal notranslate"><span class="pre">phi1_mask</span></code> to select stars near the centerline, which are more likely to be in GD-1.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">centerline</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="n">phi_mask</span><span class="p">]</span>
|
||
<span class="nb">len</span><span class="p">(</span><span class="n">centerline</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_plain highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span>25084
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>Here’s a scatter plot of proper motion for the selected stars.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">pm1</span> <span class="o">=</span> <span class="n">centerline</span><span class="p">[</span><span class="s1">'pm_phi1'</span><span class="p">]</span>
|
||
<span class="n">pm2</span> <span class="o">=</span> <span class="n">centerline</span><span class="p">[</span><span class="s1">'pm_phi2'</span><span class="p">]</span>
|
||
|
||
<span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">pm1</span><span class="p">,</span> <span class="n">pm2</span><span class="p">,</span> <span class="s1">'ko'</span><span class="p">,</span> <span class="n">markersize</span><span class="o">=</span><span class="mf">0.1</span><span class="p">,</span> <span class="n">alpha</span><span class="o">=</span><span class="mf">0.1</span><span class="p">)</span>
|
||
|
||
<span class="n">plt</span><span class="o">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s1">'Proper motion phi1 (GD1 frame)'</span><span class="p">)</span>
|
||
<span class="n">plt</span><span class="o">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s1">'Proper motion phi2 (GD1 frame)'</span><span class="p">);</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<img alt="_images/03_motion_79_0.png" src="_images/03_motion_79_0.png" />
|
||
</div>
|
||
</div>
|
||
<p>Looking at these results, we see a large cluster around (0, 0), and a smaller cluster near (0, -10).</p>
|
||
<p>We can use <code class="docutils literal notranslate"><span class="pre">xlim</span></code> and <code class="docutils literal notranslate"><span class="pre">ylim</span></code> to set the limits on the axes and zoom in on the region near (0, 0).</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">pm1</span> <span class="o">=</span> <span class="n">centerline</span><span class="p">[</span><span class="s1">'pm_phi1'</span><span class="p">]</span>
|
||
<span class="n">pm2</span> <span class="o">=</span> <span class="n">centerline</span><span class="p">[</span><span class="s1">'pm_phi2'</span><span class="p">]</span>
|
||
|
||
<span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">pm1</span><span class="p">,</span> <span class="n">pm2</span><span class="p">,</span> <span class="s1">'ko'</span><span class="p">,</span> <span class="n">markersize</span><span class="o">=</span><span class="mf">0.3</span><span class="p">,</span> <span class="n">alpha</span><span class="o">=</span><span class="mf">0.3</span><span class="p">)</span>
|
||
|
||
<span class="n">plt</span><span class="o">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s1">'Proper motion phi1 (GD1 frame)'</span><span class="p">)</span>
|
||
<span class="n">plt</span><span class="o">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s1">'Proper motion phi2 (GD1 frame)'</span><span class="p">)</span>
|
||
|
||
<span class="n">plt</span><span class="o">.</span><span class="n">xlim</span><span class="p">(</span><span class="o">-</span><span class="mi">12</span><span class="p">,</span> <span class="mi">8</span><span class="p">)</span>
|
||
<span class="n">plt</span><span class="o">.</span><span class="n">ylim</span><span class="p">(</span><span class="o">-</span><span class="mi">10</span><span class="p">,</span> <span class="mi">10</span><span class="p">);</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<img alt="_images/03_motion_81_0.png" src="_images/03_motion_81_0.png" />
|
||
</div>
|
||
</div>
|
||
<p>Now we can see the smaller cluster more clearly.</p>
|
||
<p>You might notice that our figure is less dense than the one in the paper. That’s because we started with a set of stars from a relatively small region. The figure in the paper is based on a region about 10 times bigger.</p>
|
||
<p>In the next lesson we’ll go back and select stars from a larger region. But first we’ll use the proper motion data to identify stars likely to be in GD-1.</p>
|
||
</div>
|
||
<div class="section" id="filtering-based-on-proper-motion">
|
||
<h2>Filtering based on proper motion<a class="headerlink" href="#filtering-based-on-proper-motion" title="Permalink to this headline">¶</a></h2>
|
||
<p>The next step is to select stars in the “overdense” region of proper motion, which are candidates to be in GD-1.</p>
|
||
<p>In the original paper, Price-Whelan and Bonaca used a polygon to cover this region, as shown in this figure.</p>
|
||
<a class="reference internal image-reference" href="https://github.com/datacarpentry/astronomy-python/raw/gh-pages/fig/gd1-1.png"><img alt="https://github.com/datacarpentry/astronomy-python/raw/gh-pages/fig/gd1-1.png" src="https://github.com/datacarpentry/astronomy-python/raw/gh-pages/fig/gd1-1.png" style="width: 300px;" /></a>
|
||
<p>We’ll use a simple rectangle for now, but in a later lesson we’ll see how to select a polygonal region as well.</p>
|
||
<p>Here are bounds on proper motion we chose by eye,</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">pm1_min</span> <span class="o">=</span> <span class="o">-</span><span class="mf">8.9</span>
|
||
<span class="n">pm1_max</span> <span class="o">=</span> <span class="o">-</span><span class="mf">6.9</span>
|
||
<span class="n">pm2_min</span> <span class="o">=</span> <span class="o">-</span><span class="mf">2.2</span>
|
||
<span class="n">pm2_max</span> <span class="o">=</span> <span class="mf">1.0</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>To draw these bounds, we’ll make two lists containing the coordinates of the corners of the rectangle.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">pm1_rect</span> <span class="o">=</span> <span class="p">[</span><span class="n">pm1_min</span><span class="p">,</span> <span class="n">pm1_min</span><span class="p">,</span> <span class="n">pm1_max</span><span class="p">,</span> <span class="n">pm1_max</span><span class="p">,</span> <span class="n">pm1_min</span><span class="p">]</span> <span class="o">*</span> <span class="n">u</span><span class="o">.</span><span class="n">mas</span><span class="o">/</span><span class="n">u</span><span class="o">.</span><span class="n">yr</span>
|
||
<span class="n">pm2_rect</span> <span class="o">=</span> <span class="p">[</span><span class="n">pm2_min</span><span class="p">,</span> <span class="n">pm2_max</span><span class="p">,</span> <span class="n">pm2_max</span><span class="p">,</span> <span class="n">pm2_min</span><span class="p">,</span> <span class="n">pm2_min</span><span class="p">]</span> <span class="o">*</span> <span class="n">u</span><span class="o">.</span><span class="n">mas</span><span class="o">/</span><span class="n">u</span><span class="o">.</span><span class="n">yr</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>Here’s what the plot looks like with the bounds we chose.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">pm1</span><span class="p">,</span> <span class="n">pm2</span><span class="p">,</span> <span class="s1">'ko'</span><span class="p">,</span> <span class="n">markersize</span><span class="o">=</span><span class="mf">0.3</span><span class="p">,</span> <span class="n">alpha</span><span class="o">=</span><span class="mf">0.3</span><span class="p">)</span>
|
||
<span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">pm1_rect</span><span class="p">,</span> <span class="n">pm2_rect</span><span class="p">,</span> <span class="s1">'-'</span><span class="p">)</span>
|
||
|
||
<span class="n">plt</span><span class="o">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s1">'Proper motion phi1 (GD1 frame)'</span><span class="p">)</span>
|
||
<span class="n">plt</span><span class="o">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s1">'Proper motion phi2 (GD1 frame)'</span><span class="p">)</span>
|
||
|
||
<span class="n">plt</span><span class="o">.</span><span class="n">xlim</span><span class="p">(</span><span class="o">-</span><span class="mi">12</span><span class="p">,</span> <span class="mi">8</span><span class="p">)</span>
|
||
<span class="n">plt</span><span class="o">.</span><span class="n">ylim</span><span class="p">(</span><span class="o">-</span><span class="mi">10</span><span class="p">,</span> <span class="mi">10</span><span class="p">);</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<img alt="_images/03_motion_88_0.png" src="_images/03_motion_88_0.png" />
|
||
</div>
|
||
</div>
|
||
<p>To select rows that fall within these bounds, we’ll use the following function, which uses Pandas operators to make a mask that selects rows where <code class="docutils literal notranslate"><span class="pre">series</span></code> falls between <code class="docutils literal notranslate"><span class="pre">low</span></code> and <code class="docutils literal notranslate"><span class="pre">high</span></code>.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">between</span><span class="p">(</span><span class="n">series</span><span class="p">,</span> <span class="n">low</span><span class="p">,</span> <span class="n">high</span><span class="p">):</span>
|
||
<span class="sd">"""Make a Boolean Series.</span>
|
||
<span class="sd"> </span>
|
||
<span class="sd"> series: Pandas Series</span>
|
||
<span class="sd"> low: lower bound</span>
|
||
<span class="sd"> high: upper bound</span>
|
||
<span class="sd"> </span>
|
||
<span class="sd"> returns: Boolean Series</span>
|
||
<span class="sd"> """</span>
|
||
<span class="k">return</span> <span class="p">(</span><span class="n">series</span> <span class="o">></span> <span class="n">low</span><span class="p">)</span> <span class="o">&</span> <span class="p">(</span><span class="n">series</span> <span class="o"><</span> <span class="n">high</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>The following mask select stars with proper motion in the region we chose.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">pm_mask</span> <span class="o">=</span> <span class="p">(</span><span class="n">between</span><span class="p">(</span><span class="n">df</span><span class="p">[</span><span class="s1">'pm_phi1'</span><span class="p">],</span> <span class="n">pm1_min</span><span class="p">,</span> <span class="n">pm1_max</span><span class="p">)</span> <span class="o">&</span>
|
||
<span class="n">between</span><span class="p">(</span><span class="n">df</span><span class="p">[</span><span class="s1">'pm_phi2'</span><span class="p">],</span> <span class="n">pm2_min</span><span class="p">,</span> <span class="n">pm2_max</span><span class="p">))</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>Again, the sum of a Boolean series is the number of <code class="docutils literal notranslate"><span class="pre">True</span></code> values.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">pm_mask</span><span class="o">.</span><span class="n">sum</span><span class="p">()</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_plain highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span>1049
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>Now we can use this mask to select rows from <code class="docutils literal notranslate"><span class="pre">df</span></code>.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">selected</span> <span class="o">=</span> <span class="n">df</span><span class="p">[</span><span class="n">pm_mask</span><span class="p">]</span>
|
||
<span class="nb">len</span><span class="p">(</span><span class="n">selected</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_plain highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span>1049
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>These are the stars we think are likely to be in GD-1. Let’s see what they look like, plotting their coordinates (not their proper motion).</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">phi1</span> <span class="o">=</span> <span class="n">selected</span><span class="p">[</span><span class="s1">'phi1'</span><span class="p">]</span>
|
||
<span class="n">phi2</span> <span class="o">=</span> <span class="n">selected</span><span class="p">[</span><span class="s1">'phi2'</span><span class="p">]</span>
|
||
|
||
<span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">phi1</span><span class="p">,</span> <span class="n">phi2</span><span class="p">,</span> <span class="s1">'ko'</span><span class="p">,</span> <span class="n">markersize</span><span class="o">=</span><span class="mf">0.5</span><span class="p">,</span> <span class="n">alpha</span><span class="o">=</span><span class="mf">0.5</span><span class="p">)</span>
|
||
|
||
<span class="n">plt</span><span class="o">.</span><span class="n">xlabel</span><span class="p">(</span><span class="s1">'ra (degree GD1)'</span><span class="p">)</span>
|
||
<span class="n">plt</span><span class="o">.</span><span class="n">ylabel</span><span class="p">(</span><span class="s1">'dec (degree GD1)'</span><span class="p">);</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<img alt="_images/03_motion_98_0.png" src="_images/03_motion_98_0.png" />
|
||
</div>
|
||
</div>
|
||
<p>Now that’s starting to look like a tidal stream!</p>
|
||
</div>
|
||
<div class="section" id="saving-the-dataframe">
|
||
<h2>Saving the DataFrame<a class="headerlink" href="#saving-the-dataframe" title="Permalink to this headline">¶</a></h2>
|
||
<p>At this point we have run a successful query and cleaned up the results; this is a good time to save the data.</p>
|
||
<p>To save a Pandas <code class="docutils literal notranslate"><span class="pre">DataFrame</span></code>, one option is to convert it to an Astropy <code class="docutils literal notranslate"><span class="pre">Table</span></code>, like this:</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">selected_table</span> <span class="o">=</span> <span class="n">Table</span><span class="o">.</span><span class="n">from_pandas</span><span class="p">(</span><span class="n">selected</span><span class="p">)</span>
|
||
<span class="nb">type</span><span class="p">(</span><span class="n">selected_table</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_plain highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span>astropy.table.table.Table
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>Then we could write the <code class="docutils literal notranslate"><span class="pre">Table</span></code> to a FITS file, as we did in the previous lesson.</p>
|
||
<p>But Pandas provides functions to write DataFrames in other formats; to see what they are <a class="reference external" href="https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.html">find the functions here that begin with <code class="docutils literal notranslate"><span class="pre">to_</span></code></a>.</p>
|
||
<p>One of the best options is HDF5, which is Version 5 of <a class="reference external" href="https://en.wikipedia.org/wiki/Hierarchical_Data_Format">Hierarchical Data Format</a>.</p>
|
||
<p>HDF5 is a binary format, so files are small and fast to read and write (like FITS, but unlike XML).</p>
|
||
<p>An HDF5 file is similar to an SQL database in the sense that it can contain more than one table, although in HDF5 vocabulary, a table is called a Dataset. (<a class="reference external" href="https://www.stsci.edu/itt/review/dhb_2011/Intro/intro_ch23.html">Multi-extension FITS files</a> can also contain more than one table.)</p>
|
||
<p>And HDF5 stores the metadata associated with the table, including column names, row labels, and data types (like FITS).</p>
|
||
<p>Finally, HDF5 is a cross-language standard, so if you write an HDF5 file with Pandas, you can read it back with many other software tools (more than FITS).</p>
|
||
<p>Before we write the HDF5, let’s delete the old one, if it exists.</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="o">!</span>rm -f gd1_dataframe.hdf5
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>We can write a Pandas <code class="docutils literal notranslate"><span class="pre">DataFrame</span></code> to an HDF5 file like this:</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">filename</span> <span class="o">=</span> <span class="s1">'gd1_dataframe.hdf5'</span>
|
||
|
||
<span class="n">df</span><span class="o">.</span><span class="n">to_hdf</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="s1">'df'</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>Because an HDF5 file can contain more than one Dataset, we have to provide a name, or “key”, that identifies the Dataset in the file.</p>
|
||
<p>We could use any string as the key, but in this example I use the variable name <code class="docutils literal notranslate"><span class="pre">df</span></code>.</p>
|
||
<p><strong>Exercise:</strong> We’re going to need <code class="docutils literal notranslate"><span class="pre">centerline</span></code> and <code class="docutils literal notranslate"><span class="pre">selected</span></code> later as well. Write a line or two of code to add it as a second Dataset in the HDF5 file.</p>
|
||
<div class="cell tag_hide-cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="c1"># Solution</span>
|
||
|
||
<span class="n">centerline</span><span class="o">.</span><span class="n">to_hdf</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="s1">'centerline'</span><span class="p">)</span>
|
||
<span class="n">selected</span><span class="o">.</span><span class="n">to_hdf</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="s1">'selected'</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p><strong>Detail:</strong> Reading and writing HDF5 tables requires a library called <code class="docutils literal notranslate"><span class="pre">PyTables</span></code> that is not always installed with Pandas. You can install it with pip like this:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">pip</span> <span class="n">install</span> <span class="n">tables</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>If you install it using Conda, the name of the package is <code class="docutils literal notranslate"><span class="pre">pytables</span></code>.</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">conda</span> <span class="n">install</span> <span class="n">pytables</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>We can use <code class="docutils literal notranslate"><span class="pre">ls</span></code> to confirm that the file exists and check the size:</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="o">!</span>ls -lh gd1_dataframe.hdf5
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output stream highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span>-rw-rw-r-- 1 downey downey 17M Oct 19 12:05 gd1_dataframe.hdf5
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>If you are using Windows, <code class="docutils literal notranslate"><span class="pre">ls</span></code> might not work; in that case, try:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>!dir gd1_dataframe.hdf5
|
||
</pre></div>
|
||
</div>
|
||
<p>We can read the file back like this:</p>
|
||
<div class="cell docutils container">
|
||
<div class="cell_input docutils container">
|
||
<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="n">read_back_df</span> <span class="o">=</span> <span class="n">pd</span><span class="o">.</span><span class="n">read_hdf</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="s1">'df'</span><span class="p">)</span>
|
||
<span class="n">read_back_df</span><span class="o">.</span><span class="n">shape</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="cell_output docutils container">
|
||
<div class="output text_plain highlight-myst-ansi notranslate"><div class="highlight"><pre><span></span>(140340, 12)
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>Pandas can write a variety of other formats, <a class="reference external" href="https://pandas.pydata.org/pandas-docs/stable/user_guide/io.html">which you can read about here</a>.</p>
|
||
</div>
|
||
<div class="section" id="summary">
|
||
<h2>Summary<a class="headerlink" href="#summary" title="Permalink to this headline">¶</a></h2>
|
||
<p>In this lesson, we re-loaded the Gaia data we saved from a previous query.</p>
|
||
<p>We transformed the coordinates and proper motion from ICRS to a frame aligned with GD-1, and stored the results in a Pandas <code class="docutils literal notranslate"><span class="pre">DataFrame</span></code>.</p>
|
||
<p>Then we replicated the selection process from the Price-Whelan and Bonaca paper:</p>
|
||
<ul class="simple">
|
||
<li><p>We selected stars near the centerline of GD-1 and made a scatter plot of their proper motion.</p></li>
|
||
<li><p>We identified a region of proper motion that contains stars likely to be in GD-1.</p></li>
|
||
<li><p>We used a Boolean <code class="docutils literal notranslate"><span class="pre">Series</span></code> as a mask to select stars whose proper motion is in that region.</p></li>
|
||
</ul>
|
||
<p>So far, we have used data from a relatively small region of the sky. In the next lesson, we’ll write a query that selects stars based on proper motion, which will allow us to explore a larger region.</p>
|
||
</div>
|
||
<div class="section" id="best-practices">
|
||
<h2>Best practices<a class="headerlink" href="#best-practices" title="Permalink to this headline">¶</a></h2>
|
||
<ul class="simple">
|
||
<li><p>When you make a scatter plot, adjust the size of the markers and their transparency so the figure is not overplotted; otherwise it can misrepresent the data badly.</p></li>
|
||
<li><p>For simple scatter plots in Matplotlib, <code class="docutils literal notranslate"><span class="pre">plot</span></code> is faster than <code class="docutils literal notranslate"><span class="pre">scatter</span></code>.</p></li>
|
||
<li><p>An Astropy <code class="docutils literal notranslate"><span class="pre">Table</span></code> and a Pandas <code class="docutils literal notranslate"><span class="pre">DataFrame</span></code> are similar in many ways and they provide many of the same functions. They have pros and cons, but for many projects, either one would be a reasonable choice.</p></li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<script type="text/x-thebe-config">
|
||
{
|
||
requestKernel: true,
|
||
binderOptions: {
|
||
repo: "binder-examples/jupyter-stacks-datascience",
|
||
ref: "master",
|
||
},
|
||
codeMirrorConfig: {
|
||
theme: "abcdef",
|
||
mode: "python"
|
||
},
|
||
kernelOptions: {
|
||
kernelName: "python3",
|
||
path: "./."
|
||
},
|
||
predefinedOutput: true
|
||
}
|
||
</script>
|
||
<script>kernelName = 'python3'</script>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<div class='prev-next-bottom'>
|
||
|
||
<a class='left-prev' id="prev-link" href="02_coords.html" title="previous page">Chapter 2</a>
|
||
<a class='right-next' id="next-link" href="04_select.html" title="next page">Chapter 4</a>
|
||
|
||
</div>
|
||
<footer class="footer mt-5 mt-md-0">
|
||
<div class="container">
|
||
<p>
|
||
|
||
By Allen B. Downey<br/>
|
||
|
||
© Copyright 2020.<br/>
|
||
</p>
|
||
</div>
|
||
</footer>
|
||
</main>
|
||
|
||
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<script src="_static/js/index.30270b6e4c972e43c488.js"></script>
|
||
|
||
|
||
|
||
</body>
|
||
</html> |