diff --git a/tutorials/FITS-tables/FITS-tables.ipynb b/tutorials/FITS-tables/FITS-tables.ipynb old mode 100755 new mode 100644 index beeee957..80897956 --- a/tutorials/FITS-tables/FITS-tables.ipynb +++ b/tutorials/FITS-tables/FITS-tables.ipynb @@ -2,17 +2,19 @@ "cells": [ { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "Mb0-v-tzcpN6" + }, "source": [ - "# Viewing and manipulating data from FITS tables\n", + "# Working with Astronomy Data using FITS tables\n", "\n", "## Authors\n", - "Lia Corrales, Kris Stern\n", + "Lia Corrales, Kris Stern, LĂșthien Liu, Zihao Chen, Saima Siddiqui\n", "\n", "## Learning Goals\n", "* Download a FITS table file from a URL \n", "* Open a FITS table file and view table contents\n", - "* Make a 2D histogram with the table data\n", + "* Make a 2D histogram with the event list data\n", "* Close the FITS file after use\n", "\n", "## Keywords\n", @@ -21,13 +23,17 @@ "\n", "## Summary\n", "\n", - "This tutorial demonstrates the use of `astropy.utils.data` to download a data file, then uses `astropy.io.fits` and `astropy.table` to open the file. Lastly, `matplotlib` is used to visualize the data as a histogram." + "Chandra image data is stored in FITS files, frequently referred to as \"event lists\". Any time a photon interacts with the detector, the position, time, and energy of the photon (referred to as an \"event\") is stored. Thus Chandra event lists are stored as table data, where the position and energy information is stored in the columns and each row corresponds to a separate photon interaction.\n", + "\n", + "In this tutorial, we will use `astropy.utils.data` to download a Chandra FITS file, then use `astropy.io.fits` and `astropy.table` to open the file. Lastly, we will use `matplotlib` to visualize the Chandra event list as a histogram, effectively producing an X-ray image of the sky. The data we are downloading has several tables, so we need to go through several steps to find and open the table we want." ] }, { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "id": "vfrMqJd9cpN9" + }, "outputs": [], "source": [ "import numpy as np\n", @@ -42,7 +48,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "IexsOCvKcpN-" + }, "source": [ "The following line is needed to download the example FITS files used in this tutorial." ] @@ -50,7 +58,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "id": "0zp1lvcEcpN_" + }, "outputs": [], "source": [ "from astropy.utils.data import download_file" @@ -58,7 +68,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "6u1ChU47cpN_" + }, "source": [ "FITS files often contain large amounts of multi-dimensional data and tables. \n", "\n", @@ -68,7 +80,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "id": "r6F-JgTCcpN_" + }, "outputs": [], "source": [ "event_filename = download_file('http://data.astropy.org/tutorials/FITS-tables/chandra_events.fits', \n", @@ -77,14 +91,18 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "me6HB0RrcpOA" + }, "source": [ "## Opening the FITS file and viewing table contents" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "Cp_kqCg4cpOA" + }, "source": [ "Since the file is big, let's open it with `memmap=True` to prevent RAM storage issues." ] @@ -92,7 +110,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "id": "2ABOyWmxcpOB" + }, "outputs": [], "source": [ "hdu_list = fits.open(event_filename, memmap=True)" @@ -101,7 +121,23 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 189, + "status": "ok", + "timestamp": 1654881155348, + "user": { + "displayName": "Lia Corrales", + "userId": "03737332005355202815" + }, + "user_tz": 240 + }, + "id": "yEuBPS8ycpOB", + "outputId": "8bc4c565-e09c-4de0-d99b-1d05fcfb00a3" + }, "outputs": [], "source": [ "hdu_list.info()" @@ -109,14 +145,18 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "FCoyC8sIcpOC" + }, "source": [ "In this case, we're interested in reading EVENTS, which contains information about each X-ray photon that hit the detector." ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "kTHnKQPicpOC" + }, "source": [ "To find out what information the table contains, let's print the column names." ] @@ -124,7 +164,23 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "executionInfo": { + "elapsed": 137, + "status": "ok", + "timestamp": 1654881156156, + "user": { + "displayName": "Lia Corrales", + "userId": "03737332005355202815" + }, + "user_tz": 240 + }, + "id": "CfQyzNPNcpOC", + "outputId": "244569b7-9235-43e1-f471-d0789a538b0a" + }, "outputs": [], "source": [ "print(hdu_list[1].columns)" @@ -132,7 +188,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "YpH2-4qPcpOC" + }, "source": [ "Now we'll take this data and convert it into an [astropy table](http://docs.astropy.org/en/stable/table/). While it's possible to access FITS tables directly from the ``.data`` attribute, using [Table](http://docs.astropy.org/en/stable/api/astropy.table.Table.html#astropy.table.Table) tends to make a variety of common tasks more convenient." ] @@ -140,7 +198,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "id": "n7zMyhRhcpOD" + }, "outputs": [], "source": [ "evt_data = Table(hdu_list[1].data)" @@ -148,7 +208,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "sEmGEMR1cpOD" + }, "source": [ "For example, a preview of the table is easily viewed by simply running a cell with the table as the last line:" ] @@ -156,7 +218,24 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "executionInfo": { + "elapsed": 159, + "status": "ok", + "timestamp": 1654881158017, + "user": { + "displayName": "Lia Corrales", + "userId": "03737332005355202815" + }, + "user_tz": 240 + }, + "id": "c00cCz9McpOD", + "outputId": "86eda857-3fff-4960-f488-477ee510a85c" + }, "outputs": [], "source": [ "evt_data" @@ -164,7 +243,9 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "86Qa6PUucpOD" + }, "source": [ "We can extract data from the table by referencing the column name. Let's try making a histogram for the energy of each photon, which will give us a sense for the spectrum (folded with the detector's efficiency)." ] @@ -172,97 +253,183 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 296 + }, + "executionInfo": { + "elapsed": 1297, + "status": "ok", + "timestamp": 1654881162366, + "user": { + "displayName": "Lia Corrales", + "userId": "03737332005355202815" + }, + "user_tz": 240 + }, + "id": "9YkZY2wqcpOE", + "outputId": "8422589a-217b-4b12-b9d0-d9f8520b0c0c" + }, "outputs": [], "source": [ - "energy_hist = plt.hist(evt_data['energy'], bins='auto')" + "energy_hist = plt.hist(evt_data['energy'], bins='auto')\n", + "plt.xlabel('Energy (eV)')\n", + "plt.ylabel('Number of photon events')" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "Zd7bVk4IcpOE" + }, "source": [ "## Making a 2D histogram with some table data" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "E8U-se8zcpOE" + }, "source": [ - "We'll make an image by binning the x and y coordinates of the events into a 2D histogram." + "A one dimensional histogram, as shown above, shows the number of events within each bin corresponding to one axis of information. In the plot above, we chose histogram bins in the energy, shown on the x-axis.\n", + "\n", + "Next we'll make an image by binning the x and y coordinates of the events into a 2D histogram. \n", + "\n", + "A two dimensional histogram finds the number of events binned according to two dimensions. To make an image, we will bin the number of events by x and y position on the sky." ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "cEqsE2U1cpOE" + }, "source": [ - "This particular observation spans five CCD chips. First, we determine the events that only fell on the main (ACIS-I) chips, which have number ids 0, 1, 2, and 3." + "This particular observation spans five CCD chips. First, we determine the events that only fell on the main (ACIS-I) chips, which have number ids 0, 1, 2, and 3. We can do this by creating an array of True and False values (`ii` below) to filter out events that only fall on those chips." ] }, { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "id": "Mb4RHcJLcpOE" + }, "outputs": [], "source": [ - "ii = np.in1d(evt_data['ccd_id'], [0, 1, 2, 3])\n", - "np.sum(ii)" + "ii = np.isin(evt_data['ccd_id'], [0, 1, 2, 3])" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "2z9PQUh9cpOF" + }, "source": [ - "### Method 1: Use numpy to make a 2D histogram and imshow to display it" + "### Method 1: Use hist2d with a log-normal color scheme" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "UQwNh3ppn6c_" + }, "source": [ - "This method allows us to create an image without stretching:" + "We can make a 2D histogram plot directly with the function `matplotlib.pyplot.hist2d`, as shown below.\n", + "\n", + "In this example, we choose the `matplotlib` color map named \"viridis\", and we choose to distribute the colors logarithmically using `matplotlib.colors.LogNorm()`.\n", + "\n", + "You can find more about `matplotlib` here: https://matplotlib.org/stable/plot_types/index.html\n" ] }, { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 296 + }, + "executionInfo": { + "elapsed": 1170, + "status": "ok", + "timestamp": 1654881707422, + "user": { + "displayName": "Lia Corrales", + "userId": "03737332005355202815" + }, + "user_tz": 240 + }, + "id": "tjB8Rxa1cpOG", + "outputId": "c97a7a5d-972d-451c-f5ea-a92454983570" + }, "outputs": [], "source": [ "NBINS = (100,100)\n", + "img_zero_mpl = plt.hist2d(evt_data['x'][ii], evt_data['y'][ii], NBINS, \n", + " cmap='viridis', norm=LogNorm())\n", "\n", - "img_zero, yedges, xedges = np.histogram2d(evt_data['x'][ii], evt_data['y'][ii], NBINS)\n", - "\n", - "extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]\n", - "\n", - "plt.imshow(img_zero, extent=extent, interpolation='nearest', cmap='gist_yarg', origin='lower')\n", + "# Show the color bar scale next to the plot. The color corresponds to number \n", + "# of photon events (counts) in each pixel.\n", + "cbar = plt.colorbar(label='Counts')\n", "\n", "plt.xlabel('x')\n", - "plt.ylabel('y')\n", - "\n", - "# To see more color maps\n", - "# http://wiki.scipy.org/Cookbook/Matplotlib/Show_colormaps" + "plt.ylabel('y')" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "zUYM11V5cpOF" + }, "source": [ - "### Method 2: Use hist2d with a log-normal color scheme" + "### Method 2: Use numpy to make a 2D histogram and imshow to display it" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "fh5bDy-gcpOF" + }, + "source": [ + "When we plot with `matplotlib.pyplot.hist2d`, it forces the plot into the default figure size, which could cause your image to appear stretched. \n", + "\n", + "By using `matplotlib.pyplot.imshow`, we can avoid stretching the image. To do that, we need to make a 2D array containing the number of counts per pixel bin using `numpy.histogram2d`, as shown below." ] }, { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 296 + }, + "executionInfo": { + "elapsed": 265, + "status": "ok", + "timestamp": 1654882383776, + "user": { + "displayName": "Lia Corrales", + "userId": "03737332005355202815" + }, + "user_tz": 240 + }, + "id": "hw-xj7pdcpOF", + "outputId": "5b992c7f-5797-45d1-984b-6d3b4f608faa" + }, "outputs": [], "source": [ "NBINS = (100,100)\n", - "img_zero_mpl = plt.hist2d(evt_data['x'][ii], evt_data['y'][ii], NBINS, \n", - " cmap='viridis', norm=LogNorm())\n", "\n", - "cbar = plt.colorbar(ticks=[1.0,3.0,6.0])\n", - "cbar.ax.set_yticklabels(['1','3','6'])\n", + "img_zero, xedges, yedges = np.histogram2d(evt_data['y'][ii], evt_data['x'][ii], NBINS)\n", + "\n", + "# This array describes how to map the position of the 2D array containing the image\n", + "# to the x and y positions on the sky\n", + "extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]\n", + "\n", + "plt.imshow(img_zero, extent=extent, interpolation='nearest', \n", + " cmap='gist_yarg', origin='lower', norm=LogNorm())\n", "\n", "plt.xlabel('x')\n", "plt.ylabel('y')" @@ -270,14 +437,18 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "Gggs4qvFcpOG" + }, "source": [ "## Close the FITS file" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "KNYSKxLucpOG" + }, "source": [ "When you're done using a FITS file, it's often a good idea to close it. That way you can be sure it won't continue using up excess memory or file handles on your computer. (This happens automatically when you close Python, but you never know how long that might be...)" ] @@ -285,7 +456,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "id": "AI8mrIGWcpOG" + }, "outputs": [], "source": [ "hdu_list.close()" @@ -293,14 +466,18 @@ }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "cwSittwlcpOG" + }, "source": [ "## Exercises" ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "zX5Gr3CdcpOH" + }, "source": [ "Make a scatter plot of the same data you histogrammed above. The [plt.scatter](http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.scatter) function is your friend for this. What are the pros and cons of doing it this way?" ] @@ -308,13 +485,34 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 265 + }, + "executionInfo": { + "elapsed": 1028, + "status": "ok", + "timestamp": 1654882483292, + "user": { + "displayName": "Lia Corrales", + "userId": "03737332005355202815" + }, + "user_tz": 240 + }, + "id": "haxhlHMIcpOH", + "outputId": "22fd37cb-a934-4296-dfea-7872324e18ad" + }, "outputs": [], - "source": [] + "source": [ + "energy_scatter = plt.scatter(evt_data['x'][ii], evt_data['y'][ii], s=1, alpha=0.1, color='b')" + ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "84C-9XGUcpOH" + }, "source": [ "Try the same with the [plt.hexbin](http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.hexbin) plotting function. Which do you think looks better for this kind of data?" ] @@ -322,13 +520,34 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 265 + }, + "executionInfo": { + "elapsed": 798, + "status": "ok", + "timestamp": 1654882511407, + "user": { + "displayName": "Lia Corrales", + "userId": "03737332005355202815" + }, + "user_tz": 240 + }, + "id": "s-aWczHzcpOH", + "outputId": "5d50aeba-7cc1-479b-e88b-bb9720c329fe" + }, "outputs": [], - "source": [] + "source": [ + "energy_hex = plt.hexbin(evt_data['x'][ii], evt_data['y'][ii], norm=LogNorm())" + ] }, { "cell_type": "markdown", - "metadata": {}, + "metadata": { + "id": "sB7gbTBBcpOH" + }, "source": [ "Choose an energy range to make a slice of the FITS table, then plot it. How does the image change with different energy ranges?" ] @@ -336,7 +555,44 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 296 + }, + "executionInfo": { + "elapsed": 791, + "status": "ok", + "timestamp": 1654882615039, + "user": { + "displayName": "Lia Corrales", + "userId": "03737332005355202815" + }, + "user_tz": 240 + }, + "id": "Hq1VDm35cpOH", + "outputId": "041e1abe-28c3-4209-9454-609aea4786a3" + }, + "outputs": [], + "source": [ + "NBINS = (100,100)\n", + "ii = (evt_data['energy'] > 3500) & (evt_data['energy'] < 5000)\n", + "img_zero_mpl = plt.hist2d(evt_data['x'][ii], evt_data['y'][ii], NBINS,\n", + " cmap='viridis', norm=LogNorm())\n", + "\n", + "cbar = plt.colorbar(ticks=[1.0,3.0,6.0])\n", + "cbar.ax.set_yticklabels(['1','3','6'])\n", + "\n", + "plt.xlabel('x')\n", + "plt.ylabel('y')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "uFehWSaZsLrV" + }, "outputs": [], "source": [] } @@ -350,6 +606,11 @@ "name": "", "published": true }, + "colab": { + "collapsed_sections": [], + "name": "FITS-tables.ipynb", + "provenance": [] + }, "language_info": { "codemirror_mode": { "name": "ipython", @@ -363,5 +624,5 @@ } }, "nbformat": 4, - "nbformat_minor": 2 + "nbformat_minor": 1 } diff --git a/tutorials/FITS-tables/FITS_tables.ipynb b/tutorials/FITS-tables/FITS_tables.ipynb new file mode 100644 index 00000000..43719daf --- /dev/null +++ b/tutorials/FITS-tables/FITS_tables.ipynb @@ -0,0 +1,538 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "Mb0-v-tzcpN6" + }, + "source": [ + "# Working with Chandra FITS tables\n", + "\n", + "## Authors\n", + "Lia Corrales, Kris Stern, LĂșthien Liu, Zihao Chen, Saima Siddiqui\n", + "\n", + "## Learning Goals\n", + "* Download a Chandra FITS table file from a URL \n", + "* Open a Chandra FITS table file and view table contents\n", + "* Make a 2D histogram with the event list data\n", + "* Close the FITS file after use\n", + "\n", + "## Keywords\n", + "FITS, file input/output, table, numpy, matplotlib, histogram\n", + "\n", + "\n", + "## Summary\n", + "\n", + "Chandra image data is stored as a table in FITS files, frequently referred to as \"event lists\". Any time a photon interacts with the detector, the position, time, and energy of the photon (referred to as an \"event\") is stored. Thus Chandra event lists are stored as table data, where the position and energy information is stored in the columns and each row corresponds to a separate photon interaction.\n", + "\n", + "In this tutorial, we will use `astropy.utils.data` to download a Chandra FITS file, then use `astropy.io.fits` and `astropy.table` to open the file. Lastly, we will use `matplotlib` to visualize the Chandra event list as a histogram, effectively producing an X-ray image of the sky." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "vfrMqJd9cpN9" + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "from astropy.io import fits\n", + "from astropy.table import Table\n", + "from matplotlib.colors import LogNorm\n", + "\n", + "# Set up matplotlib\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "IexsOCvKcpN-" + }, + "source": [ + "The following line is needed to download the example FITS files used in this tutorial." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "0zp1lvcEcpN_" + }, + "outputs": [], + "source": [ + "from astropy.utils.data import download_file" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6u1ChU47cpN_" + }, + "source": [ + "FITS files often contain large amounts of multi-dimensional data and tables. \n", + "\n", + "In this particular example, we'll open a FITS file from a Chandra observation of the Galactic Center. The file contains a list of events with x and y coordinates, energy, and various other pieces of information." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "r6F-JgTCcpN_" + }, + "outputs": [], + "source": [ + "event_filename = download_file('http://data.astropy.org/tutorials/FITS-tables/chandra_events.fits', \n", + " cache=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "me6HB0RrcpOA" + }, + "source": [ + "## Opening the FITS file and viewing table contents" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Cp_kqCg4cpOA" + }, + "source": [ + "It's possible to open a FITS file directly from the URL using the `Table.read` method (see [Getting Started with Table I/O](https://docs.astropy.org/en/stable/io/unified.html)). However, the Chandra event files contain multiple FITS table extensions. As part of this tutorial, we want you to get familiar with exploring FITS file contents, so we will open the FITS file directly with the `fits.open` function. \n \n Since the file is big, let's open it with `memmap=True` to prevent RAM storage issues." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "2ABOyWmxcpOB" + }, + "outputs": [], + "source": [ + "hdu_list = fits.open(event_filename, memmap=True)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "yEuBPS8ycpOB", + "outputId": "8bc4c565-e09c-4de0-d99b-1d05fcfb00a3" + }, + "outputs": [], + "source": [ + "hdu_list.info()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FCoyC8sIcpOC" + }, + "source": [ + "In this case, we're interested in reading EVENTS, which contains information about each X-ray photon that hit the detector." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kTHnKQPicpOC" + }, + "source": [ + "To find out what information the table contains, let's print the column names." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "CfQyzNPNcpOC", + "outputId": "244569b7-9235-43e1-f471-d0789a538b0a" + }, + "outputs": [], + "source": [ + "print(hdu_list[1].columns)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "YpH2-4qPcpOC" + }, + "source": [ + "Now we'll take this data and convert it into an [astropy table](http://docs.astropy.org/en/stable/table/). While it's possible to access FITS tables directly from the ``.data`` attribute, using [Table](http://docs.astropy.org/en/stable/api/astropy.table.Table.html#astropy.table.Table) tends to make a variety of common tasks more convenient." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "n7zMyhRhcpOD" + }, + "outputs": [], + "source": [ + "evt_data = Table(hdu_list[1].data)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "sEmGEMR1cpOD" + }, + "source": [ + "For example, you can use the `pprint` method to preview a text-formatted version of the table. By using the `max_lines` keyword in the example below, we can limit how much space the text takes up." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "id": "c00cCz9McpOD", + "outputId": "86eda857-3fff-4960-f488-477ee510a85c" + }, + "outputs": [], + "source": [ + "evt_data.pprint(max_lines=10)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "86Qa6PUucpOD" + }, + "source": [ + "We can extract data from the table by referencing the column name. Let's try making a histogram for the energy of each photon, which will give us a sense for the spectrum (folded with the detector's efficiency)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 296 + }, + "id": "9YkZY2wqcpOE", + "outputId": "8422589a-217b-4b12-b9d0-d9f8520b0c0c" + }, + "outputs": [], + "source": [ + "energy_hist = plt.hist(evt_data['energy'], bins='auto')\n", + "plt.xlabel('Energy (eV)')\n", + "plt.ylabel('Number of photon events')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Zd7bVk4IcpOE" + }, + "source": [ + "## Making a 2D histogram with some table data" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "E8U-se8zcpOE" + }, + "source": [ + "Next we'll make an image by binning the x and y coordinates of the events into a 2D histogram. \n", + "\n", + "A one dimensional histogram, as shown above, shows the number of events within each bin corresponding to one axis of information. In the plot above, we chose histogram bins in the energy, shown on the x-axis.\n", + "\n", + "A two dimensional histogram finds the number of events binned according to two dimensions. To make an image, we will bin the number of events by x and y position on the sky." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cEqsE2U1cpOE" + }, + "source": [ + "This particular observation spans five CCD chips. First, we determine the events that only fell on the main (ACIS-I) chips, which have number ids 0, 1, 2, and 3. We can do this by creating an array of True and False values (`ii` below) to filter out events that only fall on those chips." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Mb4RHcJLcpOE" + }, + "outputs": [], + "source": [ + "ii = np.isin(evt_data['ccd_id'], [0, 1, 2, 3])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2z9PQUh9cpOF" + }, + "source": [ + "### Method 1: Use hist2d with a log-normal color scheme" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "UQwNh3ppn6c_" + }, + "source": [ + "We can make a 2D histogram plot directly with the function `matplotlib.pyplot.hist2d`, as shown below.\n", + "\n", + "In this example, we choose the `matplotlib` color map named \"viridis\", and we choose to distribute the colors logarithmically using `matplotlib.colors.LogNorm()`.\n", + "\n", + "To see what colormaps are available with `matplotlib`, see http://wiki.scipy.org/Cookbook/Matplotlib/Show_colormaps\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 296 + }, + "id": "tjB8Rxa1cpOG", + "outputId": "c97a7a5d-972d-451c-f5ea-a92454983570" + }, + "outputs": [], + "source": [ + "NBINS = (100,100)\n", + "img_zero_mpl = plt.hist2d(evt_data['x'][ii], evt_data['y'][ii], NBINS, \n", + " cmap='viridis', norm=LogNorm())\n", + "\n", + "# Show the color bar scale next to the plot. The color corresponds to number \n", + "# of photon events (counts) in each pixel.\n", + "cbar = plt.colorbar(label='Counts')\n", + "\n", + "plt.xlabel('x')\n", + "plt.ylabel('y')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zUYM11V5cpOF" + }, + "source": [ + "### Method 2: Use numpy to make a 2D histogram and imshow to display it" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "fh5bDy-gcpOF" + }, + "source": [ + "When we plot with `matplotlib.pyplot.hist2d`, it forces the plot into the default figure size, which could cause your image to appear stretched. \n", + "\n", + "By using `matplotlib.pyplot.imshow`, we can avoid stretching the image. To do that, we need to make a 2D array containing the number of counts per pixel bin using `numpy.histogram2d`, as shown below." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 296 + }, + "id": "hw-xj7pdcpOF", + "outputId": "5b992c7f-5797-45d1-984b-6d3b4f608faa" + }, + "outputs": [], + "source": [ + "NBINS = (100,100)\n", + "\n", + "img_zero, xedges, yedges = np.histogram2d(evt_data['y'][ii], evt_data['x'][ii], NBINS)\n", + "\n", + "# This array describes how to map the position of the 2D array containing the image\n", + "# to the x and y positions on the sky\n", + "extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]\n", + "\n", + "plt.imshow(img_zero, extent=extent, interpolation='nearest', \n", + " cmap='gist_yarg', origin='lower', norm=LogNorm())\n", + "\n", + "plt.xlabel('x')\n", + "plt.ylabel('y')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Gggs4qvFcpOG" + }, + "source": [ + "## Close the FITS file" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "KNYSKxLucpOG" + }, + "source": [ + "When you're done using a FITS file, it's often a good idea to close it. That way you can be sure it won't continue using up excess memory or file handles on your computer. (This happens automatically when you close Python, but you never know how long that might be...)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "AI8mrIGWcpOG" + }, + "outputs": [], + "source": [ + "hdu_list.close()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cwSittwlcpOG" + }, + "source": [ + "## Exercises" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zX5Gr3CdcpOH" + }, + "source": [ + "Make a scatter plot of the same data you histogrammed above. The [plt.scatter](http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.scatter) function is your friend for this. What are the pros and cons of doing it this way?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 265 + }, + "id": "haxhlHMIcpOH", + "outputId": "22fd37cb-a934-4296-dfea-7872324e18ad" + }, + "outputs": [], + "source": [ + "energy_scatter = plt.scatter(evt_data['x'][ii], evt_data['y'][ii], s=1, alpha=0.1, color='b')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "84C-9XGUcpOH" + }, + "source": [ + "Try the same with the [plt.hexbin](http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.hexbin) plotting function. Which do you think looks better for this kind of data?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 265 + }, + "id": "s-aWczHzcpOH", + "outputId": "5d50aeba-7cc1-479b-e88b-bb9720c329fe" + }, + "outputs": [], + "source": [ + "energy_hex = plt.hexbin(evt_data['x'][ii], evt_data['y'][ii], norm=LogNorm())" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "sB7gbTBBcpOH" + }, + "source": [ + "Choose an energy range to make a slice of the FITS table, then plot it. How does the image change with different energy ranges?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 296 + }, + "id": "Hq1VDm35cpOH", + "outputId": "041e1abe-28c3-4209-9454-609aea4786a3" + }, + "outputs": [], + "source": [ + "NBINS = (100,100)\n", + "ii = (evt_data['energy'] > 3500) & (evt_data['energy'] < 5000)\n", + "img_zero_mpl = plt.hist2d(evt_data['x'][ii], evt_data['y'][ii], NBINS,\n", + " cmap='viridis', norm=LogNorm())\n", + "\n", + "cbar = plt.colorbar(ticks=[1.0,3.0,6.0])\n", + "cbar.ax.set_yticklabels(['1','3','6'])\n", + "\n", + "plt.xlabel('x')\n", + "plt.ylabel('y')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "uFehWSaZsLrV" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "astropy-tutorials": { + "author": "Lia R. Corrales ", + "date": "January 2014", + "description": "astropy.utils.data to download the file, astropy.io.fits to open and view the file, matplotlib for making both 1D and 2D histograms of the data.", + "link_name": "Viewing and manipulating data from FITS tables", + "name": "", + "published": true + }, + "colab": { + "collapsed_sections": [], + "name": "FITS-tables.ipynb", + "provenance": [] + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/tutorials/FITS-tables/requirements.txt b/tutorials/FITS-tables/requirements.txt deleted file mode 100644 index d1d0ba14..00000000 --- a/tutorials/FITS-tables/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -astropy -matplotlib -numpy