This tutorial describes how to read in, edit a FITS header, and then write it back out to disk. For this example we’re going to change the OBJECT keyword.

This tutorial uses astropy.io.fits, which was formerly released separately as pyfits. If you have used pyfits to manipulate FITS files then you may already be familiar with the features and syntax of the package. We start by importing the subpackage into our local namespace, and allows us to access the functions and classes as fits.name_of_function(). For example, to access the getdata() function, we don’t have to do astropy.io.fits.getdata() and can instead simple use fits.getdata(). You may run across old documentation or tutorials that use the name pyfits. Such examples will begin with import pyfits and then the command fits.getdata() (for example) would be written as pyfits.getdata().

In[1]:

from astropy.io import fits


astropy.io.fits provides a lot of flexibility for reading FITS files and headers, but most of the time the convenience functions are the easiest way to access the data. fits.getdata() reads just the data from a FITS file, but with the header=True keyword argument will also read the header.

In[2]:

data, header = fits.getdata("input_file.fits", header=True)


In[3]:

hdu_number = 0 # HDU means header data unit


Out[3]:

SIMPLE  =                    T / conforms to FITS standard
BITPIX  =                  -64 / array data type
NAXIS   =                    2 / number of array dimensions
NAXIS1  =                  100
NAXIS2  =                  100
EXTEND  =                    T
OBJECT  = 'KITTEN  '


but getdata() can get both the data and the header, so it is a useful command to remember. Since the primary HDU of a FITS file must contain image data, the data is now stored in a numpy array. The header is stored in an object that acts like a standard Python dictionary.

In[4]:

# But hdu_number = 0 is the PRIMARY HDU.How many HDUs are in this file?
fits_inf = fits.open("input_file.fits")
fits_inf.info()


Out[4]:

Filename: input_file.fits
No.    Name      Ver    Type      Cards   Dimensions   Format
0  PRIMARY       1 PrimaryHDU       7   (100, 100)   float64
1                1 ImageHDU         7   (128, 128)   float64

SIMPLE  =                    T / conforms to FITS standard
BITPIX  =                  -64 / array data type
NAXIS   =                    2 / number of array dimensions
NAXIS1  =                  100
NAXIS2  =                  100
EXTEND  =                    T
OBJECT  = 'KITTEN  '


Using fits.open allowed us to look more generally at our data. fits_inf[0].header gave 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?

Now let’s change the header to give it the correct object:

In[5]:

header['OBJECT'] = "M31"


Finally, we have to write out the FITS file. Again, the convenience function for this is the most useful command to remember:

In[6]:

fits.writeto('output_file.fits', data, header, overwrite=True)


That’s it; you’re done!

Two common more complicated cases are worth mentioning (but if your needs are much more complex, you should consult the full documentation http://docs.astropy.org/en/stable/io/fits/).

The first complication is that the FITS file you’re examining and editing might have multiple HDU’s (extensions), in which case you can specify the extension like this:

In[7]:

data1, header1 = fits.getdata("input_file.fits", ext=1, header=True)


This will get you the data and header associated with the index=1 extension in the FITS file. Without specifying a number, getdata() will get the 0th extension (equivalent to saying ext=0).

Another useful tip is if you want to overwrite an existing FITS file. By default, writeto() won’t let you do this, so you need to explicitly give it permission using the clobber keyword argument:

In[8]:

fits.writeto('output_file.fits', data, header, overwrite=True)


A final example is if you want to make a small change to a FITS file, for example updating a header keyword, but you do not want to read in and write out the whole file, which can take a while. You can use the mode='update' read mode to do this:

In[9]:

with fits.open('input_file.fits', mode='update') as filehandle: