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