In the next three chapters, more detailed information as well as examples will be explained for manipulating the header, the image data, and the table data respectively.
The Header interface was significantly reworked in PyFITS 3.1. This documentation addresses the interface as it works in PyFITS 3.1, though should still be mostly the same for earlier PyFITS versions. See the Header Interface Transition Guide for a complete detailing on the difference between PyFITS 3.1 and the earlier Header interface. This guide may also be useful to advanced users wishing to better understand how the Header interface is implemented.
Every HDU normally has two components: header and data. In PyFITS these two components are accessed through the two attributes of the HDU, header and data.
While an HDU may have empty data, i.e. the .data attribute is None, any HDU will always have a header. When an HDU is created with a constructor, e.g. hdu = PrimaryHDU(data, header), the user may supply the header value from an existing HDU’s header and the data value from a numpy array. If the defaults (None) are used, the new HDU will have the minimal required keywords for an HDU of that type:
>>> hdu = pyfits.PrimaryHDU() >>> hdu.header # show the all of the header cards SIMPLE = T / conforms to FITS standard BITPIX = 8 / array data type NAXIS = 0 / number of array dimensions EXTEND = T
A user can use any header and any data to construct a new HDU. PyFITS will strip the required keywords from the input header first and then add back the required keywords compatible to the new HDU. So, a user can use a table HDU’s header to construct an image HDU and vice versa. The constructor will also ensure the data type and dimension information in the header agree with the data.
As shown in the Quick Tutorial, keyword values can be accessed via keyword name or index of an HDU’s header attribute. Here is a quick summary:
>>> hdulist = pyfits.open('input.fits') # open a FITS file >>> prihdr = hdulist.header # the primary HDU header >>> print prihdr # get the 4th keyword's value 10 >>> prihdr = 20 # change its value >>> prihdr['DARKCORR'] # get the value of the keyword 'darkcorr' 'OMIT' >>> prihdr['darkcorr'] = 'PERFORM' # change darkcorr's value
Keyword names are case-insenstive except in a few special cases (see the sections on HIERARCH card and record-valued cards). Thus, prihdr['abc'], prihdr['ABC'], or prihdr['aBc'] are all equivalent.
Like with python dicts, new keywords can also be added to the header using assignment syntax:
>>> 'DARKCORR' in header # Check for existence False >>> header['DARKCORR'] = 'OMIT' # Add a new DARKCORR keyword
You can also add a new value and comment by assigning them as a tuple:
>>> header['DARKCORR'] = ('OMIT', 'Dark Image Subtraction')
An important point to note when adding new keywords to a header is that by default they are not appended immediately to the end of the file. Rather, they are appended to the last non-commentary keyword. This is in order to support the common use case of always having all HISTORY keywords grouped together at the end of a header. A new non-commentary keyword will be added at the end of the existing keywords, but before any HISTORY/COMMENT keywords at the end of the header.
There are a couple of ways to override this functionality:
Use the Header.append() method with the end=True argument:
>>> header.append(('DARKCORR', 'OMIT', 'Dark Image Subtraction'), end=True)
This forces the new keyword to be added at the actual end of the header.
The Header.insert() method will always insert a new keyword exactly where you ask for it:
>>> header.insert(20, ('DARKCORR', 'OMIT', 'Dark Image Subtraction'))
This inserts the DARKCORR keyword before the 20th keyword in the header no matter what it is.
A keyword (and its corresponding card) can be deleted using the same index/name syntax:
>>> del prihdr # delete the 2nd keyword >>> del prihdr['abc'] # get the value of the keyword 'abc'
Note that, like a regular Python list, the indexing updates after each delete, so if del prihdr is done two times in a row, the 4th and 5th keywords are removed from the original header. Likewise, del prihdr[-1] will delete the last card in the header.
It is also possible to delete an entire range of cards using the slice syntax:
>>> del prihdr[3:5]
The method Header.set() is another way to update they value or comment associated with an existing keyword, or to create a new keyword. Most of its functionality can be duplicated with the dict-like syntax shown above. But in some cases it might be more clear. It also has the advantage of allowing one to either move cards within the header, or specify the location of a new card relative to existing cards:
>>> prihdr.set('target', 'NGC1234', 'target name') >>> # place the next new keyword before the 'target' keyword >>> prihdr.set('newkey', 666, before='target') # comment is optional >>> # place the next new keyword after the 21st keyword >>> prihdr.set('newkey2', 42.0, 'another new key', after=20)
In FITS headers, each keyword may also have a comment associated with it explaining its purpose. The comments associated with each keyword are accessed through the comments attribute:
>>> header['NAXIS'] 2 >>> header.comments['NAXIS'] the number of image axes >>> header.comments['NAXIS'] = 'The number of image axes' # Update
Comments can be accessed in all the same ways that values are accessed, whether by keyword name or card index. Slices are also possible. The only difference is that you go through header.comments instead of just header by itself.
Some operations on header keywords also work on keyword wildcard patterns similar to those used to match files in a command shell–the character '?' may be used to match a single unknown character, and '*' may be used to match zero or more characters. For example:
>>> header = pyfits.Header([('SIMPLE', True), ('NAXIS', 2), ... ('NAXIS1', 1000), ('NAXIS2', 2000)]) >>> header SIMPLE = T NAXIS = 2 NAXIS1 = 1000 NAXIS2 = 2000 >>> header['NAXIS*'] NAXIS = 2 NAXIS1 = 1000 NAXIS2 = 2000
The object from the above example is a new Header object containing only the cards that match the wildcard pattern. It can be thought of like slicing a list, only the slice is non-linear. To return only the cards with NAXISn keywords (that is, “NAXIS” followed by one or more characters):
>>> header['NAXIS?*'] NAXIS1 = 1000 NAXIS2 = 2000
Wildcards also work for assignment and deletion:
>>> header['NAXIS?*'] = 3000 >>> header SIMPLE = T NAXIS = 2 NAXIS1 = 3000 NAXIS2 = 3000 >>> del header['NAXIS?*'] >>> header SIMPLE = T NAXIS = 2
A FITS header consists of card images.
A card image in a FITS header consists of a keyword name, a value, and optionally a comment. Physically, it takes 80 columns (bytes)–without carriage return–in a FITS file’s storage format. In PyFITS, each card image is manifested by a Card object. There are also special kinds of cards: commentary cards (see above) and card images taking more than one 80-column card image. The latter will be discussed later.
Most of the time the details of dealing with cards are handled by the Header object, and it is not necessary to directly manipulate cards. In fact, most Header methods that accept a (keyword, value) or (keyword, value, comment) tuple as an argument can also take a Card object as an argument. Card objects are just wrappers around such tuples that provide the logic for parsing and formatting individual cards in a header. But there’s usually nothing gained by manually using a Card object, except to examine how a card might appear in a header before actually adding it to the header.
A new Card object is created with the Card constructor: Card(key, value, comment). For example:
>>> c1 = pyfits.Card('TEMP', 80.0, 'temperature, floating value') >>> c2 = pyfits.Card('DETECTOR', 1) # comment is optional >>> c3 = pyfits.Card('MIR_REVR', True, 'mirror reversed? Boolean value') >>> c4 = pyfits.Card('ABC', 2+3j, 'complex value') >>> c5 = pyfits.Card('OBSERVER', 'Hubble', 'string value')
>>> print c1; print c2; print c3; print c4; print c5 # show the card images TEMP = 80.0 / temperature, floating value DETECTOR= 1 / MIR_REVR= T / mirror reversed? Boolean value ABC = (2.0, 3.0) / complex value OBSERVER= 'Hubble ' / string value
Cards have the attributes .keyword, .value, and .comment. Both .value and .comment can be changed but not the .keyword attribute.
The Card() constructor will check if the arguments given are conforming to the FITS standard and has a fixed card image format. If the user wants to create a card with a customized format or even a card which is not conforming to the FITS standard (e.g. for testing purposes), the Card.fromstring() class method can be used.
Cards can be verified with Card.verify(). The non-standard card c2 in the example below is flagged by such verification. More about verification in PyFITS will be discussed in a later chapter.
>>> c1 = pyfits.Card.fromstring('ABC = 3.456D023') >>> c2 = pyfits.Card.fromstring("P.I. ='Hubble'") >>> print c1; print c2 ABC = 3.456D023 P.I. ='Hubble' >>> c2.verify() Output verification result: Unfixable error: Illegal keyword name 'P.I.'
A list of the Card objects underlying a Header object can be accessed with the Header.cards attribute. This list is only meant for observing, and should not be directly manipulated. In fact, it is only a copy–modifications to it will not affect the header it came from. Use the methods provided by the Header class instead.
The fact that the FITS standard only allows up to 8 characters for the keyword name and 80 characters to contain the keyword, the value, and the comment is restrictive for certain applications. To allow long string values for keywords, a proposal was made in:
by using the CONTINUE keyword after the regular 80-column containing the keyword. PyFITS does support this convention, even though it is not a FITS standard. The examples below show the use of CONTINUE is automatic for long string values.
>>> header = pyfits.Header() >>> header['abc'] = 'abcdefg' * 20 >>> header ABC = 'abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcd&' CONTINUE 'efgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefga&' CONTINUE 'bcdefg&' >>> header['abc'] 'abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefg' >>> # both value and comments are long >>> header['abc'] = ('abcdefg' * 10, 'abcdefg' * 10) >>> header ABC = 'abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcd&' CONTINUE 'efg&' CONTINUE '&' / abcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefgabcdefga CONTINUE '&' / bcdefg
Note that when a CONTINUE card is used, at the end of each 80-characters card image, an ampersand is present. The ampersand is not part of the string value. Also, there is no “=” at the 9th column after CONTINUE. In the first example, the entire 240 characters is treated by PyFITS as a single card. So, if it is the nth card in a header, the (n+1)th card refers to the next keyword, not the next CONTINUE card. As such, CONTINUE cards are transparently handled by PyFITS as a single logical card, and it’s generally not necessary to worry about the details of the format. Keywords that resolve to a set of CONTINUE cards can be accessed and updated just like regular keywords.
For keywords longer than 8 characters, there is a convention originated at ESO to facilitate such use. It uses a special keyword HIERARCH with the actual long keyword following. PyFITS supports this convention as well.
If a keyword contains more than 8 characters PyFITS will automatically use a HIERARCH card, but will also issue a warning in case this is in error. However, one may explicitly request a HIERARCH card by prepending the keyword with ‘HIERARCH ‘ (just as it would appear in the header). For example, header['HIERARCH abcdefghi'] will create the keyword abcdefghi without displaying a warning. Once created, HIERARCH keywords can be accessed like any other: header['abcdefghi'], without prepending ‘HIERARCH’ to the keyword. HIEARARCH keywords also differ from normal FITS keywords in that they are case-sensitive.
>>> c = pyfits.Card('abcdefghi', 10) Keyword name 'abcdefghi' is greater than 8 characters; a HIERARCH card will be created. >>> print c HIERARCH abcdefghi = 10 >>> c = pyfits.Card('hierarch abcdefghi', 10) >>> print c HIERARCH abcdefghi = 10 >>> h = pyfits.PrimaryHDU() >>> h.header['hierarch abcdefghi'] = 99 >>> h.header['abcdefghi'] 99 >>> h.header['abcdefghi'] = 10 >>> h.header['abcdefghi'] 10 >>> h.header['ABCDEFGHI'] Traceback (most recent call last): File "<stdin>", line 1, in <module> File "pyfits/header.py", line 121, in __getitem__ return self._cards[self._cardindex(key)].value File "pyfits/header.py", line 1106, in _cardindex raise KeyError("Keyword %r not found." % keyword) KeyError: "Keyword 'ABCDEFGI.' not found." >>> h.header SIMPLE = T / conforms to FITS standard BITPIX = 8 / array data type NAXIS = 0 / number of array dimensions EXTEND = T HIERARCH abcdefghi = 1000
A final point to keep in mind about the Header class is that much of its design is intended to abstract away quirks about the FITS format. This is why, for example, it will automatically created CONTINUE and HIEARARCH cards. The Header is just a data structure, and as user you shouldn’t have to worry about how it ultimately gets serialized to a header in a FITS file.
Though there are some areas where it’s almost impossible to hide away the quirks of the FITS format, PyFITS tries to make it so that you have to think about it as little as possible. If there are any areas where you have concern yourself unncessarily about how the header is constructed, then let firstname.lastname@example.org know, as there are probably areas where this can be improved on even more.