1.. currentmodule:: astropy.io.fits
2
3.. _astropy-io-fits:
4
5**************************************
6FITS File Handling (`astropy.io.fits`)
7**************************************
8
9Introduction
10============
11
12The :mod:`astropy.io.fits` package provides access to FITS files. FITS
13(Flexible Image Transport System) is a portable file standard widely used in
14the astronomy community to store images and tables. This subpackage was
15originally developed as PyFITS.
16
17.. _tutorial:
18
19Getting Started
20===============
21
22This section provides a quick introduction of using :mod:`astropy.io.fits`. The
23goal is to demonstrate the package's basic features without getting into too
24much detail. If you are a first time user or have never used ``astropy`` or
25PyFITS, this is where you should start. See also the :ref:`FAQ <io-fits-faq>`
26for answers to common questions and issues.
27
28.. note::
29
30    If you want to read or write a single table in FITS format, the
31    recommended method is via the high-level :ref:`table_io`. In particular
32    see the :ref:`Unified I/O FITS <table_io_fits>` section.
33
34Reading and Updating Existing FITS Files
35----------------------------------------
36
37Opening a FITS File
38^^^^^^^^^^^^^^^^^^^
39
40.. note::
41
42    The ``astropy.io.fits.util.get_testdata_filepath()`` function,
43    used in the examples here, is for accessing data shipped with ``astropy``.
44    To work with your own data instead, please use :func:`astropy.io.fits.open`,
45    which takes either the relative or absolute path.
46
47Once the `astropy.io.fits` package is loaded using the standard convention
48[#f1]_, we can open an existing FITS file::
49
50    >>> from astropy.io import fits
51    >>> fits_image_filename = fits.util.get_testdata_filepath('test0.fits')
52
53    >>> hdul = fits.open(fits_image_filename)
54
55The :func:`open` function has several optional arguments which will be
56discussed in a later chapter. The default mode, as in the above example, is
57"readonly". The open function returns an object called an :class:`HDUList`
58which is a `list`-like collection of HDU objects. An HDU (Header Data Unit) is
59the highest level component of the FITS file structure, consisting of a header
60and (typically) a data array or table.
61
62After the above open call, ``hdul[0]`` is the primary HDU, ``hdul[1]`` is
63the first extension HDU, etc. (if there are any extensions), and so on. It
64should be noted that ``astropy`` uses zero-based indexing when referring to
65HDUs and header cards, though the FITS standard (which was designed with
66Fortran in mind) uses one-based indexing.
67
68The :class:`HDUList` has a useful method :meth:`HDUList.info`, which
69summarizes the content of the opened FITS file:
70
71    >>> hdul.info()
72    Filename: ...test0.fits
73    No.    Name      Ver    Type      Cards   Dimensions   Format
74      0  PRIMARY       1 PrimaryHDU     138   ()
75      1  SCI           1 ImageHDU        61   (40, 40)   int16
76      2  SCI           2 ImageHDU        61   (40, 40)   int16
77      3  SCI           3 ImageHDU        61   (40, 40)   int16
78      4  SCI           4 ImageHDU        61   (40, 40)   int16
79
80After you are done with the opened file, close it with the
81:meth:`HDUList.close` method:
82
83    >>> hdul.close()
84
85You can avoid closing the file manually by using :func:`open` as context
86manager::
87
88    >>> with fits.open(fits_image_filename) as hdul:
89    ...     hdul.info()
90    Filename: ...test0.fits
91    No.    Name      Ver    Type      Cards   Dimensions   Format
92      0  PRIMARY       1 PrimaryHDU     138   ()
93      1  SCI           1 ImageHDU        61   (40, 40)   int16
94      2  SCI           2 ImageHDU        61   (40, 40)   int16
95      3  SCI           3 ImageHDU        61   (40, 40)   int16
96      4  SCI           4 ImageHDU        61   (40, 40)   int16
97
98After exiting the ``with`` scope the file will be closed automatically. That is
99(generally) the preferred way to open a file in Python, because it will close
100the file even if an exception happens.
101
102If the file is opened with ``lazy_load_hdus=False``, all of the headers will
103still be accessible after the HDUList is closed. The headers and data may or
104may not be accessible depending on whether the data are touched and if they
105are memory-mapped; see later chapters for detail.
106
107.. _fits-large-files:
108
109Working with large files
110""""""""""""""""""""""""
111
112The :func:`open` function supports a ``memmap=True`` argument that allows the
113array data of each HDU to be accessed with mmap, rather than being read into
114memory all at once. This is particularly useful for working with very large
115arrays that cannot fit entirely into physical memory. Here ``memmap=True`` by
116default, and this value is obtained from the configuration item ``astropy.io.fits.Conf.use_memmap``.
117
118This has minimal impact on smaller files as well, though some operations, such
119as reading the array data sequentially, may incur some additional overhead. On
12032-bit systems, arrays larger than 2 to 3 GB cannot be mmap'd (which is fine,
121because by that point you are likely to run out of physical memory anyways), but
12264-bit systems are much less limited in this respect.
123
124.. warning::
125    When opening a file with ``memmap=True``, because of how mmap works this
126    means that when the HDU data is accessed (i.e., ``hdul[0].data``) another
127    handle to the FITS file is opened by mmap. This means that even after
128    calling ``hdul.close()`` the mmap still holds an open handle to the data so
129    that it can still be accessed by unwary programs that were built with the
130    assumption that the .data attribute has all of the data in-memory.
131
132    In order to force the mmap to close, either wait for the containing
133    ``HDUList`` object to go out of scope, or manually call
134    ``del hdul[0].data``. (This works so long as there are no other references
135    held to the data array.)
136
137Unsigned integers
138"""""""""""""""""
139
140Due to the FITS format's Fortran origins, FITS does not natively support
141unsigned integer data in images or tables. However, there is a common
142convention to store unsigned integers as signed integers, along with a
143*shift* instruction (a ``BZERO`` keyword with value ``2 ** (BITPIX - 1)``) to
144shift up all signed integers to unsigned integers. For example, when writing
145the value ``0`` as an unsigned 32-bit integer, it is stored in the FITS
146file as ``-32768``, along with the header keyword ``BZERO = 32768``.
147
148``astropy`` recognizes and applies this convention by default, so that all data
149that looks like it should be interpreted as unsigned integers is automatically
150converted (this applies to both images and tables).
151
152Even with ``uint=False``, the ``BZERO`` shift is still applied, but the
153returned array is of "float64" type. To disable scaling/shifting entirely, use
154``do_not_scale_image_data=True`` (see :ref:`fits-scaled-data-faq` in the FAQ
155for more details).
156
157Working with compressed files
158"""""""""""""""""""""""""""""
159
160.. note::
161
162    Files that use compressed HDUs within the FITS file are discussed
163    in :ref:`Compressed Image Data <astropy-io-fits-compressedImageData>`.
164
165
166The :func:`open` function will seamlessly open FITS files that have been
167compressed with gzip, bzip2 or pkzip. Note that in this context we are talking
168about a FITS file that has been compressed with one of these utilities (e.g., a
169.fits.gz file).
170
171There are some limitations when working with compressed files. For example,
172with Zip files that contain multiple compressed files, only the first file will
173be accessible. Also bzip2 does not support the append or update access modes.
174
175When writing a file (e.g., with the :func:`writeto` function), compression will
176be determined based on the filename extension given, or the compression used in
177a pre-existing file that is being written to.
178
179
180Working with non-standard files
181"""""""""""""""""""""""""""""""
182When `astropy.io.fits` reads a FITS file which does not conform to the FITS
183standard it will try to make an educated interpretation of non-compliant fields.
184This may not always succeed and may trigger warnings when accessing headers or
185exceptions when writing to file. Verification of fields written to an output
186file can be controlled with the ``output_verify`` parameter of :func:`open`.
187Files opened for reading can be verified and fixed with method
188``HDUList.verify``. This method is typically invoked after opening the file
189but before accessing any headers or data::
190
191    >>> with fits.open(fits_image_filename) as hdul:
192    ...    hdul.verify('fix')
193    ...    data = hdul[1].data
194
195In the above example, the call to ``hdul.verify("fix")`` requests that `astropy.io.fits`
196fix non-compliant fields and print informative messages. Other options in addition to ``"fix"``
197are described under FITS :ref:`fits_io_verification`
198
199.. seealso:: FITS :ref:`fits_io_verification`.
200
201Working with FITS Headers
202^^^^^^^^^^^^^^^^^^^^^^^^^
203
204As mentioned earlier, each element of an :class:`HDUList` is an HDU object with
205``.header`` and ``.data`` attributes, which can be used to access the header
206and data portions of the HDU.
207
208For those unfamiliar with FITS headers, they consist of a list of 80 byte
209"cards", where a card contains a keyword, a value, and a comment. The keyword
210and comment must both be strings, whereas the value can be a string or an
211integer, floating point number, complex number, or ``True``/``False``. Keywords
212are usually unique within a header, except in a few special cases.
213
214The header attribute is a Header instance, another ``astropy`` object. To get
215the value associated with a header keyword, do (à la Python dicts)::
216
217    >>> hdul = fits.open(fits_image_filename)
218    >>> hdul[0].header['DATE']
219    '01/04/99'
220
221to get the value of the keyword "DATE", which is a string '01/04/99'.
222
223Although keyword names are always in upper case inside the FITS file,
224specifying a keyword name with ``astropy`` is case-insensitive for the user's
225convenience. If the specified keyword name does not exist, it will raise a
226`KeyError` exception.
227
228We can also get the keyword value by indexing (à la Python lists)::
229
230    >>> hdul[0].header[7]
231    32768.0
232
233This example returns the eighth (like Python lists, it is 0-indexed) keyword's
234value — a float — 32768.0.
235
236Similarly, it is possible to update a keyword's value in ``astropy``, either
237through keyword name or index::
238
239    >>> hdr = hdul[0].header
240    >>> hdr['targname'] = 'NGC121-a'
241    >>> hdr[27] = 99
242
243Please note however that almost all application code should update header
244values via their keyword name and not via their positional index. This is
245because most FITS keywords may appear at any position in the header.
246
247It is also possible to update both the value and comment associated with a
248keyword by assigning them as a tuple::
249
250    >>> hdr = hdul[0].header
251    >>> hdr['targname'] = ('NGC121-a', 'the observation target')
252    >>> hdr['targname']
253    'NGC121-a'
254    >>> hdr.comments['targname']
255    'the observation target'
256
257Like a dict, you may also use the above syntax to add a new keyword/value pair
258(and optionally a comment as well). In this case the new card is appended to
259the end of the header (unless it is a commentary keyword such as COMMENT or
260HISTORY, in which case it is appended after the last card with that keyword).
261
262Another way to either update an existing card or append a new one is to use the
263:meth:`Header.set` method::
264
265    >>> hdr.set('observer', 'Edwin Hubble')
266
267Comment or history records are added like normal cards, though in their case a
268new card is always created, rather than updating an existing HISTORY or COMMENT
269card::
270
271    >>> hdr['history'] = 'I updated this file 2/26/09'
272    >>> hdr['comment'] = 'Edwin Hubble really knew his stuff'
273    >>> hdr['comment'] = 'I like using HST observations'
274    >>> hdr['history']
275    I updated this file 2/26/09
276    >>> hdr['comment']
277    Edwin Hubble really knew his stuff
278    I like using HST observations
279
280Note: Be careful not to confuse COMMENT cards with the comment value for normal
281cards.
282
283To update existing COMMENT or HISTORY cards, reference them by index::
284
285    >>> hdr['history'][0] = 'I updated this file on 2/27/09'
286    >>> hdr['history']
287    I updated this file on 2/27/09
288    >>> hdr['comment'][1] = 'I like using JWST observations'
289    >>> hdr['comment']
290    Edwin Hubble really knew his stuff
291    I like using JWST observations
292
293
294To see the entire header as it appears in the FITS file (with the END card and
295padding stripped), enter the header object by itself, or
296``print(repr(hdr))``::
297
298    >>> hdr  # doctest: +ELLIPSIS
299    SIMPLE  =                    T / file does conform to FITS standard
300    BITPIX  =                   16 / number of bits per data pixel
301    NAXIS   =                    0 / number of data axes
302    ...
303    >>> print(repr(hdr))  # doctest: +ELLIPSIS
304    SIMPLE  =                    T / file does conform to FITS standard
305    BITPIX  =                   16 / number of bits per data pixel
306    NAXIS   =                    0 / number of data axes
307    ...
308
309Entering only ``print(hdr)`` will also work, but may not be very legible
310on most displays, as this displays the header as it is written in the FITS file
311itself, which means there are no line breaks between cards. This is a common
312source of confusion for new users.
313
314It is also possible to view a slice of the header::
315
316   >>> hdr[:2]
317   SIMPLE  =                    T / file does conform to FITS standard
318   BITPIX  =                   16 / number of bits per data pixel
319
320Only the first two cards are shown above.
321
322To get a list of all keywords, use the :meth:`Header.keys` method just as you
323would with a dict::
324
325    >>> list(hdr.keys())  # doctest: +ELLIPSIS
326    ['SIMPLE', 'BITPIX', 'NAXIS', ...]
327
328.. topic:: Examples:
329
330    See also :ref:`sphx_glr_generated_examples_io_modify-fits-header.py`.
331
332.. _structural_keywords:
333
334Structural Keywords
335"""""""""""""""""""
336
337FITS keywords mix up both metadata and critical information about the file structure
338that is needed to parse the file. These *structural* keywords are managed internally by
339:mod:`astropy.io.fits` and, in general, should not be touched by the user. Instead one
340should use  the related attributes of the `astropy.io.fits` classes (see examples below).
341
342The specific set of structural keywords used by the FITS standard varies with HDU type.
343The following table lists which keywords are associated with each HDU type:
344
345.. csv-table:: Structural Keywords
346   :header: "HDU Type", "Structural Keywords"
347   :widths: 20, 20
348
349   "All", "``SIMPLE``, ``BITPIX``, ``NAXIS``"
350   ":class:`PrimaryHDU`", "``EXTEND``"
351   ":class:`ImageHDU`, :class:`TableHDU`, :class:`BinTableHDU`",  "``PCOUNT``, ``GCOUNT``"
352   ":class:`GroupsHDU`", "``NAXIS1``, ``GCOUNT``, ``PCOUNT``, ``GROUPS``"
353   ":class:`TableHDU`, :class:`BinTableHDU`", "``TFIELDS``, ``TFORM``, ``TBCOL``"
354
355There are many other reserved keywords, for instance for the data scaling, or for table's column
356attributes, as described in the  `FITS Standard <https://fits.gsfc.nasa.gov/fits_standard.html>`__.
357Most of these are accessible via attributes of the :class:`Column` or HDU objects, for instance
358``hdu.name`` to set ``EXTNAME``, or ``hdu.ver`` for ``EXTVER``. Structural keywords are checked
359and/or updated as a consequence of common operations. For example, when:
360
3611. Setting the data. The ``NAXIS*`` keywords are set from the data shape (``.data.shape``), and ``BITPIX``
362   from the data type (``.data.dtype``).
3632. Setting the header. Its keywords are updated based on the data properties (as above).
3643. Writing a file. All the necessary keywords are deleted, updated or added to the header.
3654. Calling an HDU's verify method (e.g., :func:`PrimaryHDU.verify`). Some keywords can be fixed automatically.
366
367In these cases any hand-written values users might assign to those keywords will be overwrittten.
368
369Working with Image Data
370^^^^^^^^^^^^^^^^^^^^^^^
371
372If an HDU's data is an image, the data attribute of the HDU object will return
373a ``numpy`` `~numpy.ndarray` object. Refer to the ``numpy`` documentation for
374details on manipulating these numerical arrays::
375
376    >>> data = hdul[1].data
377
378Here, ``data`` points to the data object in the second HDU (the first HDU,
379``hdul[0]``, being the primary HDU) which corresponds to the 'SCI'
380extension. Alternatively, you can access the extension by its extension name
381(specified in the EXTNAME keyword)::
382
383    >>> data = hdul['SCI'].data
384
385If there is more than one extension with the same EXTNAME, the EXTVER value
386needs to be specified along with the EXTNAME as a tuple; for example::
387
388    >>> data = hdul['sci',2].data
389
390Note that the EXTNAME is also case-insensitive.
391
392The returned ``numpy`` object has many attributes and methods for a user to get
393information about the array, for example::
394
395    >>> data.shape
396    (40, 40)
397    >>> data.dtype.name
398    'int16'
399
400Since image data is a ``numpy`` object, we can slice it, view it, and perform
401mathematical operations on it. To see the pixel value at x=5, y=2::
402
403    >>> print(data[1, 4])
404    348
405
406Note that, like C (and unlike Fortran), Python is 0-indexed and the indices
407have the slowest axis first and fastest changing axis last; that is, for a 2D
408image, the fast axis (X-axis) which corresponds to the FITS NAXIS1 keyword, is
409the second index. Similarly, the 1-indexed subsection of x=11 to 20
410(inclusive) and y=31 to 40 (inclusive) would be given in Python as::
411
412    >>> data[30:40, 10:20]
413    array([[350, 349, 349, 348, 349, 348, 349, 347, 350, 348],
414           [348, 348, 348, 349, 348, 349, 347, 348, 348, 349],
415           [348, 348, 347, 349, 348, 348, 349, 349, 349, 349],
416           [349, 348, 349, 349, 350, 349, 349, 347, 348, 348],
417           [348, 348, 348, 348, 349, 348, 350, 349, 348, 349],
418           [348, 347, 349, 349, 350, 348, 349, 348, 349, 347],
419           [347, 348, 347, 348, 349, 349, 350, 349, 348, 348],
420           [349, 349, 350, 348, 350, 347, 349, 349, 349, 348],
421           [349, 348, 348, 348, 348, 348, 349, 347, 349, 348],
422           [349, 349, 349, 348, 350, 349, 349, 350, 348, 350]], dtype=int16)
423
424To update the value of a pixel or a subsection::
425
426    >>> data[30:40, 10:20] = data[1, 4] = 999
427
428This example changes the values of both the pixel \[1, 4] and the subsection
429\[30:40, 10:20] to the new value of 999. See the `Numpy documentation`_ for
430more details on Python-style array indexing and slicing.
431
432The next example of array manipulation is to convert the image data from counts
433to flux::
434
435    >>> photflam = hdul[1].header['photflam']
436    >>> exptime = hdr['exptime']
437    >>> data = data * photflam / exptime
438    >>> hdul.close()
439
440Note that performing an operation like this on an entire image requires holding
441the entire image in memory. This example performs the multiplication in-place
442so that no copies are made, but the original image must first be able to fit in
443main memory. For most observations this should not be an issue on modern
444personal computers.
445
446If at this point you want to preserve all of the changes you made and write it
447to a new file, you can use the :meth:`HDUList.writeto` method (see below).
448
449.. _Numpy documentation: https://numpy.org/doc/stable/reference/arrays.indexing.html
450
451.. topic:: Examples:
452
453    See also :ref:`sphx_glr_generated_examples_io_plot_fits-image.py`.
454
455Working with Table Data
456^^^^^^^^^^^^^^^^^^^^^^^
457
458This section describes reading and writing table data in the FITS format using
459the `~astropy.io.fits` package directly. For some cases, however, the
460high-level :ref:`table_io` will often suffice and is somewhat more convenient
461to use. See the :ref:`Unified I/O FITS <table_io_fits>` section for details.
462
463Like images, the data portion of a FITS table extension is in the ``.data``
464attribute::
465
466    >>> fits_table_filename = fits.util.get_testdata_filepath('tb.fits')
467    >>> hdul = fits.open(fits_table_filename)
468    >>> data = hdul[1].data # assuming the first extension is a table
469    >>> hdul.close()
470
471If you are familiar with ``numpy`` `~numpy.recarray` (record array) objects, you
472will find the table data is basically a record array with some extra
473properties. But familiarity with record arrays is not a prerequisite for this
474guide.
475
476To see the first row of the table::
477
478    >>> print(data[0])
479    (1, 'abc', 3.7000000715255736, False)
480
481Each row in the table is a :class:`FITS_record` object which looks like a
482(Python) tuple containing elements of heterogeneous data types. In this
483example: an integer, a string, a floating point number, and a Boolean value. So
484the table data are just an array of such records. More commonly, a user is
485likely to access the data in a column-wise way. This is accomplished by using
486the :meth:`~FITS_rec.field` method. To get the first column (or "field" in
487NumPy parlance — it is used here interchangeably with "column") of the table,
488use::
489
490    >>> data.field(0)
491    array([1, 2]...)
492
493A ``numpy`` object with the data type of the specified field is returned.
494
495Like header keywords, a column can be referred either by index, as above, or by
496name::
497
498    >>> data.field('c1')
499    array([1, 2]...)
500
501When accessing a column by name, dict-like access is also possible (and even
502preferable)::
503
504    >>> data['c1']
505    array([1, 2]...)
506
507In most cases it is preferable to access columns by their name, as the column
508name is entirely independent of its physical order in the table. As with
509header keywords, column names are case-insensitive.
510
511But how do we know what columns we have in a table? First, we will introduce
512another attribute of the table HDU: the :attr:`~BinTableHDU.columns`
513attribute::
514
515    >>> cols = hdul[1].columns
516
517This attribute is a :class:`ColDefs` (column definitions) object. If we use the
518:meth:`ColDefs.info` method from the interactive prompt::
519
520    >>> cols.info()
521    name:
522        ['c1', 'c2', 'c3', 'c4']
523    format:
524        ['1J', '3A', '1E', '1L']
525    unit:
526        ['', '', '', '']
527    null:
528        [-2147483647, '', '', '']
529    bscale:
530        ['', '', 3, '']
531    bzero:
532        ['', '', 0.4, '']
533    disp:
534        ['I11', 'A3', 'G15.7', 'L6']
535    start:
536        ['', '', '', '']
537    dim:
538        ['', '', '', '']
539    coord_type:
540        ['', '', '', '']
541    coord_unit:
542        ['', '', '', '']
543    coord_ref_point:
544        ['', '', '', '']
545    coord_ref_value:
546        ['', '', '', '']
547    coord_inc:
548        ['', '', '', '']
549    time_ref_pos:
550        ['', '', '', '']
551
552it will show the attributes of all columns in the table, such as their names,
553formats, bscales, bzeros, etc. A similar output that will display the column
554names and their formats can be printed from within a script with::
555
556    >>> hdul[1].columns
557    ColDefs(
558        name = 'c1'; format = '1J'; null = -2147483647; disp = 'I11'
559        name = 'c2'; format = '3A'; disp = 'A3'
560        name = 'c3'; format = '1E'; bscale = 3; bzero = 0.4; disp = 'G15.7'
561        name = 'c4'; format = '1L'; disp = 'L6'
562    )
563
564We can also get these properties individually; for example::
565
566    >>> cols.names
567    ['c1', 'c2', 'c3', 'c4']
568
569returns a (Python) list of field names.
570
571Since each field is a ``numpy`` object, we will have the entire arsenal of
572``numpy`` tools to use. We can reassign (update) the values::
573
574    >>> data['c4'][:] = 0
575
576take the mean of a column::
577
578    >>> data['c3'].mean()  # doctest: +FLOAT_CMP
579    5.19999989271164
580
581and so on.
582
583.. topic:: Examples:
584
585    See also :ref:`sphx_glr_generated_examples_io_fits-tables.py`.
586
587Save File Changes
588^^^^^^^^^^^^^^^^^
589
590As mentioned earlier, after a user opened a file, made a few changes to either
591header or data, the user can use :meth:`HDUList.writeto` to save the changes.
592This takes the version of headers and data in memory and writes them to a new
593FITS file on disk. Subsequent operations can be performed to the data in memory
594and written out to yet another different file, all without recopying the
595original data to (more) memory:
596
597.. code:: python
598
599    hdul.writeto('newtable.fits')
600
601will write the current content of ``hdulist`` to a new disk file newfile.fits.
602If a file was opened with the update mode, the :meth:`HDUList.flush` method can
603also be used to write all of the changes made since :func:`open`, back to the
604original file. The :meth:`~HDUList.close` method will do the same for a FITS
605file opened with update mode:
606
607.. code:: python
608
609    with fits.open('original.fits', mode='update') as hdul:
610        # Change something in hdul.
611        hdul.flush()  # changes are written back to original.fits
612
613    # closing the file will also flush any changes and prevent further writing
614
615
616Creating a New FITS File
617------------------------
618
619Creating a New Image File
620^^^^^^^^^^^^^^^^^^^^^^^^^
621
622So far we have demonstrated how to read and update an existing FITS file. But
623how about creating a new FITS file from scratch? Such tasks are very convenient
624in ``astropy`` for an image HDU. We will first demonstrate how to create a FITS
625file consisting of only the primary HDU with image data.
626
627First, we create a ``numpy`` object for the data part::
628
629    >>> import numpy as np
630    >>> n = np.arange(100.0) # a simple sequence of floats from 0.0 to 99.9
631
632Next, we create a :class:`PrimaryHDU` object to encapsulate the data::
633
634    >>> hdu = fits.PrimaryHDU(n)
635
636We then create an :class:`HDUList` to contain the newly created primary HDU, and write to
637a new file::
638
639    >>> hdul = fits.HDUList([hdu])
640    >>> hdul.writeto('new1.fits')
641
642That is it! In fact, ``astropy`` even provides a shortcut for the last two
643lines to accomplish the same behavior::
644
645    >>> hdu.writeto('new2.fits')
646
647This will write a single HDU to a FITS file without having to manually
648encapsulate it in an :class:`HDUList` object first.
649
650
651Creating a New Table File
652^^^^^^^^^^^^^^^^^^^^^^^^^
653
654.. note::
655
656    If you want to create a **binary** FITS table with no other HDUs,
657    you can use :class:`~astropy.table.Table` instead and then write to FITS.
658    This is less complicated than "lower-level" FITS interface::
659
660    >>> from astropy.table import Table
661    >>> t = Table([[1, 2], [4, 5], [7, 8]], names=('a', 'b', 'c'))
662    >>> t.write('table1.fits', format='fits')
663
664    The equivalent code using ``astropy.io.fits`` would look like this:
665
666    >>> from astropy.io import fits
667    >>> import numpy as np
668    >>> c1 = fits.Column(name='a', array=np.array([1, 2]), format='K')
669    >>> c2 = fits.Column(name='b', array=np.array([4, 5]), format='K')
670    >>> c3 = fits.Column(name='c', array=np.array([7, 8]), format='K')
671    >>> t = fits.BinTableHDU.from_columns([c1, c2, c3])
672    >>> t.writeto('table2.fits')
673
674To create a table HDU is a little more involved than an image HDU, because a
675table's structure needs more information. First of all, tables can only be an
676extension HDU, not a primary. There are two kinds of FITS table extensions:
677ASCII and binary. We will use binary table examples here.
678
679To create a table from scratch, we need to define columns first, by
680constructing the :class:`Column` objects and their data. Suppose we have two
681columns, the first containing strings, and the second containing floating point
682numbers::
683
684    >>> import numpy as np
685    >>> a1 = np.array(['NGC1001', 'NGC1002', 'NGC1003'])
686    >>> a2 = np.array([11.1, 12.3, 15.2])
687    >>> col1 = fits.Column(name='target', format='20A', array=a1)
688    >>> col2 = fits.Column(name='V_mag', format='E', array=a2)
689
690.. note::
691
692    It is not necessary to create a :class:`Column` object explicitly
693    if the data is stored in a
694    `structured array <https://numpy.org/doc/stable/user/basics.rec.html>`_.
695
696Next, create a :class:`ColDefs` (column-definitions) object for all columns::
697
698    >>> cols = fits.ColDefs([col1, col2])
699
700Now, create a new binary table HDU object by using the
701:func:`BinTableHDU.from_columns` function::
702
703    >>> hdu = fits.BinTableHDU.from_columns(cols)
704
705This function returns (in this case) a :class:`BinTableHDU`.
706
707The data structure used to represent FITS tables is called a :class:`FITS_rec`
708and is derived from the :class:`numpy.recarray` interface. When creating
709a new table HDU the individual column arrays will be assembled into a single
710:class:`FITS_rec` array.
711
712You can create a :class:`BinTableHDU` more concisely without creating intermediate
713variables for the individual columns and without manually creating a
714:class:`ColDefs` object::
715
716    >>> hdu = fits.BinTableHDU.from_columns(
717    ...     [fits.Column(name='target', format='20A', array=a1),
718    ...      fits.Column(name='V_mag', format='E', array=a2)])
719
720Now you may write this new table HDU directly to a FITS file like so::
721
722    >>> hdu.writeto('table3.fits')
723
724This shortcut will automatically create a minimal primary HDU with no data and
725prepend it to the table HDU to create a valid FITS file. If you require
726additional data or header keywords in the primary HDU you may still create a
727:class:`PrimaryHDU` object and build up the FITS file manually using an
728:class:`HDUList`, as described in the next section.
729
730Creating a File with Multiple Extensions
731^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
732
733In the previous examples we created files with a single meaningful extension (a
734:class:`PrimaryHDU` or :class:`BinTableHDU`). To create a file with multiple
735extensions we need to create extension HDUs and append them to an :class:`HDUList`.
736
737First, we create some data for Image extensions::
738
739    >>> import numpy as np
740    >>> n = np.ones((3, 3))
741    >>> n2 = np.ones((100, 100))
742    >>> n3 = np.ones((10, 10, 10))
743
744Note that the data shapes of the different extensions do not need to be the same.
745Next, place the data into separate :class:`PrimaryHDU` and :class:`ImageHDU`
746objects::
747
748    >>> primary_hdu = fits.PrimaryHDU(n)
749    >>> image_hdu = fits.ImageHDU(n2)
750    >>> image_hdu2 = fits.ImageHDU(n3)
751
752A multi-extension FITS file is not constrained to be only imaging or table data, we
753can mix them. To show this we'll use the example from the previous section to make a
754:class:`BinTableHDU`::
755
756    >>> c1 = fits.Column(name='a', array=np.array([1, 2]), format='K')
757    >>> c2 = fits.Column(name='b', array=np.array([4, 5]), format='K')
758    >>> c3 = fits.Column(name='c', array=np.array([7, 8]), format='K')
759    >>> table_hdu = fits.BinTableHDU.from_columns([c1, c2, c3])
760
761Now when we create the :class:`HDUList` we list all extensions we want to
762include::
763
764    >>> hdul = fits.HDUList([primary_hdu, image_hdu, table_hdu])
765
766Because :class:`HDUList` acts like a :class:`list` we can also append, for example,
767an :class:`ImageHDU` to an already existing :class:`HDUList`::
768
769    >>> hdul.append(image_hdu2)
770
771Multi-extension :class:`HDUList` are treated just like those with only a
772:class:`PrimaryHDU`, so to save the file use :func:`HDUList.writeto` as shown above.
773
774.. note::
775
776    The FITS standard enforces all files to have exactly one :class:`PrimaryHDU` that
777    is the first HDU present in the file. This standard is enforced during the call to
778    :func:`HDUList.writeto` and an error will be raised if it is not met. See the
779    ``output_verify`` option in :func:`HDUList.writeto` for ways to fix or ignore these
780    warnings.
781
782In the previous example the :class:`PrimaryHDU` contained actual data. In some cases it
783is desirable to have a minimal :class:`PrimaryHDU` with only basic header information.
784To do this, first create a new :class:`Header` object to encapsulate any keywords you want
785to include in the primary HDU, then as before create a :class:`PrimaryHDU`::
786
787    >>> hdr = fits.Header()
788    >>> hdr['OBSERVER'] = 'Edwin Hubble'
789    >>> hdr['COMMENT'] = "Here's some commentary about this FITS file."
790    >>> empty_primary = fits.PrimaryHDU(header=hdr)
791
792When we create a new primary HDU with a custom header as in the above example,
793this will automatically include any additional header keywords that are
794*required* by the FITS format (keywords such as ``SIMPLE`` and ``NAXIS`` for
795example). In general, users should not have to manually manage such keywords,
796and should only create and modify observation-specific informational keywords.
797
798We then create an HDUList containing both the primary HDU and any other HDUs want::
799
800    >>> hdul = fits.HDUList([empty_primary, image_hdu2, table_hdu])
801
802.. topic:: Examples:
803
804    See also :ref:`sphx_glr_generated_examples_io_create-mef.py`.
805
806Convenience Functions
807---------------------
808
809`astropy.io.fits` also provides several high-level ("convenience") functions.
810Such a convenience function is a "canned" operation to achieve one task.
811By using these "convenience" functions, a user does not have to worry about
812opening or closing a file; all of the housekeeping is done implicitly.
813
814.. warning::
815
816    These functions are useful for interactive Python sessions and less complex
817    analysis scripts, but should not be used for application code, as they
818    are highly inefficient. For example, each call to :func:`getval`
819    requires re-parsing the entire FITS file. Code that makes repeated use
820    of these functions should instead open the file with :func:`open`
821    and access the data structures directly.
822
823The first of these functions is :func:`getheader`, to get the header of an HDU.
824Here are several examples of getting the header. Only the file name is required
825for this function. The rest of the arguments are optional and flexible to
826specify which HDU the user wants to access::
827
828    >>> from astropy.io.fits import getheader
829    >>> hdr = getheader(fits_image_filename)  # get default HDU (=0), i.e. primary HDU's header
830    >>> hdr = getheader(fits_image_filename, 0)  # get primary HDU's header
831    >>> hdr = getheader(fits_image_filename, 2)  # the second extension
832    >>> hdr = getheader(fits_image_filename, 'sci')  # the first HDU with EXTNAME='SCI'
833    >>> hdr = getheader(fits_image_filename, 'sci', 2)  # HDU with EXTNAME='SCI' and EXTVER=2
834    >>> hdr = getheader(fits_image_filename, ('sci', 2))  # use a tuple to do the same
835    >>> hdr = getheader(fits_image_filename, ext=2)  # the second extension
836    >>> hdr = getheader(fits_image_filename, extname='sci')  # first HDU with EXTNAME='SCI'
837    >>> hdr = getheader(fits_image_filename, extname='sci', extver=2)
838
839Ambiguous specifications will raise an exception::
840
841    >>> getheader(fits_image_filename, ext=('sci', 1), extname='err', extver=2)
842    Traceback (most recent call last):
843        ...
844    TypeError: Redundant/conflicting extension arguments(s): ...
845
846After you get the header, you can access the information in it, such as getting
847and modifying a keyword value::
848
849    >>> fits_image_2_filename = fits.util.get_testdata_filepath('o4sp040b0_raw.fits')
850    >>> hdr = getheader(fits_image_2_filename, 0)    # get primary hdu's header
851    >>> filter = hdr['filter']                       # get the value of the keyword "filter'
852    >>> val = hdr[10]                                # get the 11th keyword's value
853    >>> hdr['filter'] = 'FW555'                      # change the keyword value
854
855For the header keywords, the header is like a dictionary, as well as a list.
856The user can access the keywords either by name or by numeric index, as
857explained earlier in this chapter.
858
859If a user only needs to read one keyword, the  :func:`getval` function can
860further simplify to just one call, instead of two as shown in the above
861examples::
862
863    >>> from astropy.io.fits import getval
864    >>> # get 0th extension's keyword FILTER's value
865    >>> flt = getval(fits_image_2_filename, 'filter', 0)
866    >>> flt
867    'Clear'
868
869    >>> # get the 2nd sci extension's 11th keyword's value
870    >>> val = getval(fits_image_2_filename, 10, 'sci', 2)
871    >>> val
872    False
873
874The function :func:`getdata` gets the data of an HDU. Similar to
875:func:`getheader`, it only requires the input FITS file name while the
876extension is specified through the optional arguments. It does have one extra
877optional argument header. If header is set to True, this function will return
878both data and header, otherwise only data is returned::
879
880    >>> from astropy.io.fits import getdata
881    >>> # get 3rd sci extension's data:
882    >>> data = getdata(fits_image_filename, 'sci', 3)
883    >>> # get 1st extension's data AND header:
884    >>> data, hdr = getdata(fits_image_filename, 1, header=True)
885
886The functions introduced above are for reading. The next few functions
887demonstrate convenience functions for writing::
888
889    >>> fits.writeto('out.fits', data, hdr)
890
891The :func:`writeto` function uses the provided data and an optional header to
892write to an output FITS file.
893
894::
895
896    >>> fits.append('out.fits', data, hdr)
897
898The :func:`append` function will use the provided data and the optional header
899to append to an existing FITS file. If the specified output file does not
900exist, it will create one.
901
902.. code:: python
903
904    from astropy.io.fits import update
905    update(filename, dat, hdr, 'sci')         # update the 'sci' extension
906    update(filename, dat, 3)                  # update the 3rd extension
907    update(filename, dat, hdr, 3)             # update the 3rd extension
908    update(filename, dat, 'sci', 2)           # update the 2nd SCI extension
909    update(filename, dat, 3, header=hdr)      # update the 3rd extension
910    update(filename, dat, header=hdr, ext=5)  # update the 5th extension
911
912The :func:`update` function will update the specified extension with the input
913data/header. The third argument can be the header associated with the data. If
914the third argument is not a header, it (and other positional arguments) are
915assumed to be the extension specification(s). Header and extension specs can
916also be keyword arguments.
917
918The :func:`printdiff` function will print a difference report of two FITS files,
919including headers and data. The first two arguments must be two FITS
920filenames or FITS file objects with matching data types (i.e., if using strings
921to specify filenames, both inputs must be strings). The third
922argument is an optional extension specification, with the same call format
923of :func:`getheader` and :func:`getdata`. In addition you can add any keywords
924accepted by the :class:`FITSDiff` class.
925
926.. code:: python
927
928    from astropy.io.fits import printdiff
929    # get a difference report of ext 2 of inA and inB
930    printdiff('inA.fits', 'inB.fits', ext=2)
931    # ignore HISTORY and COMMENT keywords
932    printdiff('inA.fits', 'inB.fits', ignore_keywords=('HISTORY','COMMENT')
933
934Finally, the :func:`info` function will print out information of the specified
935FITS file::
936
937    >>> fits.info(fits_image_filename)
938    Filename: ...test0.fits
939    No.    Name      Ver    Type      Cards   Dimensions   Format
940      0  PRIMARY       1 PrimaryHDU     138   ()
941      1  SCI           1 ImageHDU        61   (40, 40)   int16
942      2  SCI           2 ImageHDU        61   (40, 40)   int16
943      3  SCI           3 ImageHDU        61   (40, 40)   int16
944      4  SCI           4 ImageHDU        61   (40, 40)   int16
945
946This is one of the most useful convenience functions for getting an overview of
947what a given file contains without looking at any of the details.
948
949
950Using `astropy.io.fits`
951=======================
952.. toctree::
953   :maxdepth: 2
954
955   usage/headers
956   usage/image
957   usage/table
958   usage/verification
959   usage/unfamiliar
960   usage/scripts
961   usage/misc
962
963Command-Line Utilities
964======================
965
966For convenience, several of ``astropy``'s sub-packages install utility programs
967on your system which allow common tasks to be performed without having
968to open a Python interpreter. These utilities include:
969
970- `~astropy.io.fits.scripts.fitsheader`: prints the headers of a FITS file.
971
972- `~astropy.io.fits.scripts.fitscheck`: verifies and optionally rewrites
973  the CHECKSUM and DATASUM keywords of a FITS file.
974
975- :ref:`fitsdiff`: compares two FITS files and reports the differences.
976
977- :ref:`fits2bitmap`: converts FITS images to bitmaps, including scaling and
978  stretching.
979
980- :ref:`wcslint <wcslint>`: checks the :ref:`WCS <astropy-wcs>` keywords in a
981  FITS file for compliance against the standards.
982
983Other Information
984=================
985
986.. toctree::
987    :maxdepth: 1
988
989    appendix/faq
990    appendix/header_transition
991    appendix/history
992
993.. note that if this section gets too long, it should be moved to a separate
994   doc page - see the top of performance.inc.rst for the instructions on how to do
995   that
996
997.. include:: performance.inc.rst
998
999Reference/API
1000=============
1001
1002.. automodule:: astropy.io.fits
1003
1004.. toctree::
1005    :maxdepth: 3
1006
1007    api/files.rst
1008    api/hdulists.rst
1009    api/hdus.rst
1010    api/headers.rst
1011    api/cards.rst
1012    api/tables.rst
1013    api/images.rst
1014    api/diff.rst
1015    api/verification.rst
1016
1017.. rubric:: Footnotes
1018
1019.. [#f1]  For legacy code only that already depends on PyFITS, it's acceptable to continue using "from astropy.io import fits as pyfits".
1020