-
-
Notifications
You must be signed in to change notification settings - Fork 180
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes to the FITS-header Tutorial #559
base: main
Are you sure you want to change the base?
Changes from all commits
97dd141
df22283
cc0529c
a8cc73b
0458920
9e25787
ecb88c1
58f5890
f3900c4
1746eeb
616ef9d
c774a05
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -2,12 +2,14 @@ | |||||
"cells": [ | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"metadata": {}, | ||||||
"metadata": { | ||||||
"id": "UxTXN1HZmv2R" | ||||||
}, | ||||||
"source": [ | ||||||
"# Edit a FITS header\n", | ||||||
"\n", | ||||||
"## Authors\n", | ||||||
"Adrian Price-Whelan, Adam Ginsburg, Stephanie T. Douglas, Kelle Cruz\n", | ||||||
"Adrian Price-Whelan, Adam Ginsburg, Stephanie T. Douglas, Kelle Cruz, Saima Siddiqui, Zihao Chen, Lúthien Liu\n", | ||||||
"\n", | ||||||
"## Learning Goals\n", | ||||||
"* Read a FITS file \n", | ||||||
|
@@ -18,138 +20,196 @@ | |||||
"## Keywords\n", | ||||||
"FITS, file input/output\n", | ||||||
"\n", | ||||||
"## Companion Content\n", | ||||||
"To learn more about the FITS file format check out: https://docs.astropy.org/en/stable/io/fits/index.html#\n", | ||||||
"\n", | ||||||
"## Summary\n", | ||||||
"This tutorial describes how to read in and edit a FITS header, and then write \n", | ||||||
"it back out to disk. For this example we're going to change the `OBJECT` \n", | ||||||
"keyword." | ||||||
"FITS is a useful way of storing and archiving information through multidimensional arrays. Within a FITS file format, the header contains \"cards\" of information including a keyword, a value, and a comment as a way to introduce the data block that follows. Together, the header and data are contained together as a header data unit, or HDU. \n", | ||||||
"\n", | ||||||
"In this tutorial we will begin with exploring the different ways we can read information in an FITS file, how to read and edit the header, and then write it out back to disk. For this example we're going to change the `OBJECT` keyword.\n" | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "code", | ||||||
"execution_count": null, | ||||||
"metadata": {}, | ||||||
"metadata": { | ||||||
"id": "FMaYcumxmv2U" | ||||||
}, | ||||||
"outputs": [], | ||||||
"source": [ | ||||||
"from astropy.io import fits" | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"metadata": {}, | ||||||
"metadata": { | ||||||
"id": "kZZYBsrSmv2Y" | ||||||
}, | ||||||
"source": [ | ||||||
"``astropy.io.fits`` provides a lot of flexibility for reading FITS \n", | ||||||
"files and headers, but most of the time the convenience functions are\n", | ||||||
"the easiest way to access the data. ``fits.getdata()`` reads only the \n", | ||||||
"data from a FITS file, but with the `header=True` keyword argument will\n", | ||||||
"also read the header. " | ||||||
"The `astropy.io.fits` package provides a lot of flexibility for reading FITS files and headers. `fits.getheader()` is a dedicated function for reading the header. In this example, we are reading the primary HDU (which has the HDU number 0). The output will show you all the cards that the primary HDU has. " | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "code", | ||||||
"execution_count": null, | ||||||
"metadata": {}, | ||||||
"metadata": { | ||||||
"id": "rYWSDikzmv2Z" | ||||||
}, | ||||||
"outputs": [], | ||||||
"source": [ | ||||||
"data, header = fits.getdata(\"input_file.fits\", header=True)" | ||||||
"hdu_number = 0 # HDU means header data unit\n", | ||||||
"fits.getheader('input_file.fits', hdu_number)" | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"metadata": {}, | ||||||
"metadata": { | ||||||
"id": "MGu5Yf7qoF9c" | ||||||
}, | ||||||
"source": [ | ||||||
"There is also a dedicated function for reading only the \n", | ||||||
"header:" | ||||||
"**Thought Question:** *How many cards does the primary HDU have?* " | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"metadata": { | ||||||
"id": "qkFwDQUCmv2Y" | ||||||
}, | ||||||
"source": [ | ||||||
"Most of the time, the convenience functions are the easiest way to access the data. `fits.getdata()` reads only the data from a FITS file, but with the `header=True` keyword argument will also read the header. " | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "code", | ||||||
"execution_count": null, | ||||||
"metadata": {}, | ||||||
"metadata": { | ||||||
"id": "i6xdEI3wmv2Y" | ||||||
}, | ||||||
"outputs": [], | ||||||
"source": [ | ||||||
"hdu_number = 0 # HDU means header data unit\n", | ||||||
"fits.getheader('input_file.fits', hdu_number)" | ||||||
"data, header = fits.getdata(\"input_file.fits\", header=True)" | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"metadata": {}, | ||||||
"metadata": { | ||||||
"id": "ZcYnmVjOmv2Z" | ||||||
}, | ||||||
"source": [ | ||||||
"But `getdata()` can get both the data and the header, so it's a useful \n", | ||||||
"command to remember. Since the primary HDU of a FITS file must contain image data, \n", | ||||||
"the data is now stored in a ``numpy`` array. The header is stored in an \n", | ||||||
"object that acts like a standard Python dictionary. " | ||||||
"Now we know `getdata()` can get both the data and the header, so it's a useful \n", | ||||||
"command to remember. The primary HDU of a FITS file can contain image data, \n", | ||||||
"so the data is now stored in a ``numpy`` array. The header is stored in an \n", | ||||||
"object that acts like a standard Python dictionary, except that the keys are case-insensitive. \n", | ||||||
"\n", | ||||||
"If the FITS file you're examining and editing might have multiple HDU's (extensions), you can specify the extension like this:" | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "code", | ||||||
"execution_count": null, | ||||||
"metadata": {}, | ||||||
"metadata": { | ||||||
"id": "saJh59aemv2c" | ||||||
}, | ||||||
"outputs": [], | ||||||
"source": [ | ||||||
"# But hdu_number = 0 is the PRIMARY HDU.How many HDUs are in this file?\n", | ||||||
"fits_inf = fits.open(\"input_file.fits\")\n", | ||||||
"fits_inf.info() \n", | ||||||
"fits_inf[0].header" | ||||||
"data1, header1 = fits.getdata(\"input_file.fits\", ext=1, header=True)" | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"metadata": {}, | ||||||
"metadata": { | ||||||
"id": "iAa7NjKtmv2d" | ||||||
}, | ||||||
"source": [ | ||||||
"Using ``fits.open`` allows us to look more generally at our data. ``fits_inf[0].header`` gives us the same output as ``fits.getheader``. What will you learn if you type ``fits_inf[1].header``? Based on ``fits_inf.info()`` can you guess what will happen if you type ``fits_inf[2].header``?" | ||||||
"This will get you the data and header associated with the `index=1` extension \n", | ||||||
"in the FITS file. Without specifying a number, `getdata()` will get the \n", | ||||||
"0th extension (equivalent to saying `ext=0`)." | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"metadata": {}, | ||||||
"metadata": { | ||||||
"id": "ml2DVqgRlJwQ" | ||||||
}, | ||||||
"source": [ | ||||||
"Now let's change the header to give it the correct object:" | ||||||
"There are alot of other useful commands to remember for reading FITS files:" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "code", | ||||||
"execution_count": null, | ||||||
"metadata": {}, | ||||||
"outputs": [], | ||||||
"cell_type": "markdown", | ||||||
"metadata": { | ||||||
"id": "LXnCQLIqltzv" | ||||||
}, | ||||||
"source": [ | ||||||
"header['OBJECT'] = \"M31\"" | ||||||
"1. `fits.open` allows us to look more generally at our data." | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"metadata": {}, | ||||||
"metadata": { | ||||||
"id": "tbmPXHehmO8q" | ||||||
}, | ||||||
"source": [ | ||||||
"Finally, we have to write out the FITS file. Again, the convenience \n", | ||||||
"function for this is the most useful command to remember:" | ||||||
"2. `fits.getheader` will give you the same output as `fits_inf[0].header`. By changing the HDU number to `fits_inf[1]header`, what will the output be? " | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "code", | ||||||
"execution_count": null, | ||||||
"metadata": {}, | ||||||
"metadata": { | ||||||
"id": "YKrZaxbRmv2a" | ||||||
}, | ||||||
"outputs": [], | ||||||
"source": [ | ||||||
"fits.writeto('output_file.fits', data, header, overwrite=True)" | ||||||
"# But hdu_number = 0 is the PRIMARY HDU. How many HDUs are in this file?\n", | ||||||
"fits_inf = fits.open(\"input_file.fits\")\n", | ||||||
"fits_inf.info() \n", | ||||||
"fits_inf[0].header" | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"metadata": {}, | ||||||
"metadata": { | ||||||
"id": "z_ddsyRUoVu8" | ||||||
}, | ||||||
"source": [ | ||||||
"That's it; you're done!" | ||||||
"**Thought Question:** *How many HDUS are in this file? Were you right about the number of cards in the primary HDU?*" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like the thought questions -- is the intent to have answers for them somewhere in the notebook, or to have code snippets suggesting how to get to the answer? This one was a little confusing because you ask both about the number of HDUs and about the number of cards. Maybe change the bold bit from Thought question to Thought questions |
||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"metadata": { | ||||||
"id": "AKTWFAyeopR4" | ||||||
}, | ||||||
"source": [ | ||||||
"**Thought Question:** *What will happen if you type* `fits_inf[2].header`*?*" | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"metadata": { | ||||||
"id": "UFYUH450mv2b" | ||||||
}, | ||||||
"source": [ | ||||||
"Now that we know how to read a FITS file, let's change the header. The header is always changed via it’s keyword. The keywords are the 8 letter word to the left when you extract the header info. Let's change the header to give the correct OBJECT:" | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "code", | ||||||
"execution_count": null, | ||||||
"metadata": { | ||||||
"id": "LOP18OR0mv2b" | ||||||
}, | ||||||
"outputs": [], | ||||||
"source": [ | ||||||
"header['OBJECT'] = \"M31\"" | ||||||
eblur marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"metadata": {}, | ||||||
"source": [ | ||||||
"Two common and more complicated cases are worth mentioning (but if your needs \n", | ||||||
"are much more complex, you should consult the full documentation http://docs.astropy.org/en/stable/io/fits/). \n", | ||||||
"\n", | ||||||
"The first complication is that the FITS file you're examining and \n", | ||||||
"editing might have multiple HDU's (extensions), in which case you can \n", | ||||||
"specify the extension like this:" | ||||||
"The keyword OBJECT was in the primary HDU. We can also change a header in another HDU like this:" | ||||||
] | ||||||
}, | ||||||
{ | ||||||
|
@@ -158,47 +218,72 @@ | |||||
"metadata": {}, | ||||||
"outputs": [], | ||||||
"source": [ | ||||||
"data1, header1 = fits.getdata(\"input_file.fits\", ext=1, header=True)" | ||||||
"hdul = fits.open(\"input_file.fits\")\n", | ||||||
"hdr = hdul[1].header\n", | ||||||
"hdr['newcard'] = 'a value'" | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"metadata": {}, | ||||||
"source": [ | ||||||
"This will get you the data and header associated with the `index=1` extension \n", | ||||||
"in the FITS file. Without specifying a number, `getdata()` will get the \n", | ||||||
"0th extension (equivalent to saying `ext=0`)." | ||||||
"Since the keyword \"newcard\" didn't exist in HDU 1, it is created by attempting to change its value." | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"metadata": {}, | ||||||
"metadata": { | ||||||
"id": "wXTmxWCSmv2d" | ||||||
}, | ||||||
"source": [ | ||||||
"Another useful tip is if you want to overwrite an existing FITS \n", | ||||||
"file. By default, `writeto()` won't let you do this, so you need to \n", | ||||||
"explicitly give it permission using the `clobber` keyword argument:" | ||||||
"Finally, we have to write out the FITS file. Again, the convenience function for this is the most useful command to remember. Note that by default, `writeto()` won't let you overwrite an existing FITS \n", | ||||||
"file, so you need to explicitly give it permission to do so using the `overwrite` keyword argument:" | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "code", | ||||||
"execution_count": null, | ||||||
"metadata": {}, | ||||||
"metadata": { | ||||||
"id": "Sif3LGBgmv2d" | ||||||
}, | ||||||
"outputs": [], | ||||||
"source": [ | ||||||
"fits.writeto('output_file.fits', data, header, overwrite=True)" | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"metadata": {}, | ||||||
"metadata": { | ||||||
"id": "2HujAZ7Amv2c" | ||||||
}, | ||||||
"source": [ | ||||||
"That's it; you're done!" | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"metadata": { | ||||||
"id": "51Y--P-Zmv2c" | ||||||
}, | ||||||
"source": [ | ||||||
"For more complicated cases, you should consult the full documentation http://docs.astropy.org/en/stable/io/fits/. \n" | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"metadata": { | ||||||
"id": "-CxqVv7Vmv2d" | ||||||
}, | ||||||
"source": [ | ||||||
"A final example is if you want to make a small change to a FITS file, like updating a header keyword, but you don't want to read in and write out the whole file, which can take a while. Instead you can use the `mode='update'` read mode to do this:" | ||||||
"A helpful tip if you want to make a small change to a FITS file, like updating a header keyword, but you don't want to read in and write out the whole file, which can take a while, is to use `mode='update'`:" | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "code", | ||||||
"execution_count": null, | ||||||
"metadata": {}, | ||||||
"metadata": { | ||||||
"id": "KbM8RWxOmv2d" | ||||||
}, | ||||||
"outputs": [], | ||||||
"source": [ | ||||||
"with fits.open('input_file.fits', mode='update') as filehandle:\n", | ||||||
|
@@ -207,14 +292,18 @@ | |||||
}, | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"metadata": {}, | ||||||
"metadata": { | ||||||
"id": "K1_WE8Obmv2e" | ||||||
}, | ||||||
"source": [ | ||||||
"## Exercise" | ||||||
] | ||||||
}, | ||||||
{ | ||||||
"cell_type": "markdown", | ||||||
"metadata": {}, | ||||||
"metadata": { | ||||||
"id": "RSdvjG_1mv2e" | ||||||
}, | ||||||
"source": [ | ||||||
"Read in the file you just wrote and add three header keywords:\n", | ||||||
"\n", | ||||||
|
@@ -228,7 +317,9 @@ | |||||
{ | ||||||
"cell_type": "code", | ||||||
"execution_count": null, | ||||||
"metadata": {}, | ||||||
"metadata": { | ||||||
"id": "6YjEp_temv2e" | ||||||
}, | ||||||
"outputs": [], | ||||||
"source": [] | ||||||
} | ||||||
|
@@ -242,6 +333,11 @@ | |||||
"name": "", | ||||||
"published": true | ||||||
}, | ||||||
"colab": { | ||||||
"collapsed_sections": [], | ||||||
"name": "FITS-header.ipynb", | ||||||
"provenance": [] | ||||||
}, | ||||||
"language_info": { | ||||||
"codemirror_mode": { | ||||||
"name": "ipython", | ||||||
|
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I agree that this is the easiest -- if you just need the header or data alone and don't need to modify the file, then maybe it is more convenient. If you want to change the file, though, I think
fits.open
is probably preferable.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! I'll change how I wrote that and add a little more about the functions for clarity.