1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
|
*************************************************
Loading and Defining Custom Spectral File Formats
*************************************************
Loading From a File
-------------------
Specutils leverages the astropy io registry to provide an interface for conveniently
loading data from files. To create a custom loader, the user must define it in
a separate python file and place the file in their ``~/.specutils`` directory.
Loading from a FITS File
------------------------
A spectra with a *Linear Wavelength Solution* can be read using the ``read``
method of the :class:`~specutils.Spectrum` class to parse the file name and
format
.. code-block:: python
import os
from specutils import Spectrum
file_path = os.path.join('path/to/folder', 'file_with_1d_wcs.fits')
spec = Spectrum.read(file_path, format='wcs1d-fits')
This will create a :class:`~specutils.Spectrum` object that you can manipulate later.
For instance, you could plot the spectrum.
.. code-block:: python
import matplotlib.pyplot as plt
plt.title('FITS file with 1D WCS')
plt.xlabel('Wavelength (Angstrom)')
plt.ylabel('Flux (erg/cm2/s/A)')
plt.plot(spec.wavelength, spec.flux)
plt.show()
.. image:: img/read_1d.png
Creating a Custom Loader
------------------------
Defining a custom loader consists of importing the
`~specutils.io.registers.data_loader` decorator from specutils and attaching
it to a function that knows how to parse the user's data. The return object
of this function must be an instance of one of the spectral classes
(:class:`~specutils.Spectrum`, :class:`~specutils.SpectrumCollection`,
:class:`~specutils.SpectrumList`).
Optionally, the user may define an identifier function. This function acts to
ensure that the data file being loaded is compatible with the loader function.
.. code-block:: python
# ~/.specutils/my_custom_loader.py
import os
from astropy.io import fits
from astropy.nddata import StdDevUncertainty
from astropy.table import Table
from astropy.units import Unit
from astropy.wcs import WCS
from specutils.io.registers import data_loader
from specutils import Spectrum
# Define an optional identifier. If made specific enough, this circumvents the
# need to add ``format="my-format"`` in the ``Spectrum.read`` call.
def identify_generic_fits(origin, *args, **kwargs):
return (isinstance(args[0], str) and
os.path.splitext(args[0].lower())[1] == '.fits')
@data_loader("my-format", identifier=identify_generic_fits,
extensions=['fits'])
def generic_fits(file_name, **kwargs):
with fits.open(file_name, **kwargs) as hdulist:
header = hdulist[0].header
tab = Table.read(file_name)
meta = {'header': header}
wcs = WCS(hdulist[0].header)
uncertainty = StdDevUncertainty(tab["err"])
data = tab["flux"] * Unit("Jy")
return Spectrum(flux=data, wcs=wcs, uncertainty=uncertainty, meta=meta)
An ``extensions`` keyword can be provided. This allows for basic filename
extension matching in the case that the ``identifier`` function is not
provided.
It is possible to query the registry to return the list of loaders associated
with a particular extension.
.. code-block:: python
from specutils.io import get_loaders_by_extension
loaders = get_loaders_by_extension('fits')
The returned list contains the format labels that can be fed into the ``format``
keyword argument of the ``Spectrum.read`` method.
After placing this python file in the user's ``~/.specutils`` directory, it
can be utilized by referencing its name in the ``read`` method of the
:class:`~specutils.Spectrum` class
.. code-block:: python
from specutils import Spectrum
spec = Spectrum.read("path/to/data", format="my-format")
.. _multiple_spectra:
Loading Multiple Spectra
^^^^^^^^^^^^^^^^^^^^^^^^
It is possible to create a loader that reads multiple spectra from the same
file. For the general case where none of the spectra are assumed to be the same
length, the loader should return a `~specutils.SpectrumList`. Consider the
custom JWST data loader as an example:
.. literalinclude:: ../specutils/io/default_loaders/jwst_reader.py
:language: python
Note that by default, any loader that uses ``dtype=Spectrum`` will also
automatically add a reader for `~specutils.SpectrumList`. This enables user
code to call `specutils.SpectrumList.read <astropy.nddata.NDIOMixin.read>` in
all cases if it can't make assumptions about whether a loader returns one or
many `~specutils.Spectrum` objects. This method is available since
`~specutils.SpectrumList` makes use of the Astropy IO registry (see
`astropy.io.registry.read`).
.. _custom_writer:
Creating a Custom Writer
------------------------
Similar to creating a custom loader, a custom data writer may also be defined.
This again will be done in a separate python file and placed in the user's
``~/.specutils`` directory to be loaded into the astropy io registry.
.. code-block:: python
# ~/.spectacle/my_writer.py
from astropy.table import Table
from specutils.io.registers import custom_writer
@custom_writer("fits-writer")
def generic_fits(spectrum, file_name, **kwargs):
flux = spectrum.flux.value
disp = spectrum.spectral_axis.value
meta = spectrum.meta
tab = Table([disp, flux], names=("spectral_axis", "flux"), meta=meta)
tab.write(file_name, format="fits")
The custom writer can be used by passing the name of the custom writer to the
``format`` argument of the ``write`` method on the
:class:`~specutils.Spectrum`.
.. code-block:: python
spec = Spectrum(flux=np.random.sample(100) * u.Jy,
spectral_axis=np.arange(100) * u.AA)
spec.write("my_output.fits", format="fits-writer")
Reference/API
-------------
.. automodapi:: specutils.io.registers
:no-heading:
|