NDData¶
Overview¶
The NDData
class is a container for gridded N-dimensional
data. It has a data
attribute, which can be any object that presents an
array-like interface, and optional attributes:
meta
, for metadataunit
for thedata
unituncertainty
for the uncertainty of the data (which could be standard deviation, variance or something else),mask
for thedata
wcs
, representing the relationship betweendata
and world coordinates.
Of these, only mask
and uncertainty
may be changed after the NDData
object is created.
Initializing¶
An NDData
object can be instantiated by passing it an
n-dimensional Numpy array:
>>> import numpy as np
>>> from astropy.nddata import NDData
>>> array = np.zeros((12, 12, 12)) # a 3-dimensional array with all zeros
>>> ndd = NDData(array)
Note that the data in ndd
is a reference to the original array
, so
changing the data in ndd
will change the corresponding data in array
in most circumstances.
An NDData
object can also be instantiated by passing it an
NDData
object:
>>> ndd1 = NDData(array)
>>> ndd2 = NDData(ndd1)
As above, the data in``ndd2`` is a reference to the data in ndd1
, so
changes to one will affect the other.
It can also be instantiated by passing in an object that can be converted to a numpy numerical array:
>>> ndd3 = NDData([1, 2, 3, 4])
The final way to instantiate an NDData
object is with a data
object that presents a numpy array-like interface. If the object passed to the
intializer has all three of the attributes shape
, __getitem__
(so it
is indexable) and __array__
(so that it can act like a numpy array in
expression) then the data
attribute will be set to that object.
The purpose of this mechanism is to allow considerable flexibility in the objects used to store the data while providing a useful to default (numpy array).
Mask¶
Values can be masked using the mask
attribute. One straightforward way to
provide a mask is to use a boolean numpy array:
>>> ndd_masked = NDData(ndd, mask = ndd.data > 0.9)
Another is to simply initialize an NDData
object with a
masked numpy array:
>>> masked_array = np.ma.array([1, 2, 3, 4], mask=[1, 0, 0, 1])
>>> ndd_masked = NDData(masked_array)
>>> ndd_masked.mask
array([ True, False, False, True], dtype=bool)
A mask value of True
indicates a value that should be ignored, while a mask
value of False
indicates a valid value.
There is no requirement that the mask actually be a numpy array; for example,
a function which evaluates a mask value as needed is acceptable as long as it
follows the convention that True
indicates a value that should be ignored.
Unit¶
The unit of the data can be set by either explicitly providing an astropy unit
when creating the NDData
object:
>>> import astropy.units as u
>>> ndd_unit = NDData([1, 2, 3, 4], unit="meter")
>>> ndd_unit.unit
Unit("m")
or by initializing with data that is an astropy Quantity
:
>>> q = [1, 2, 3, 4] * u.meter
>>> ndd_unit2 = NDData(q)
>>> ndd_unit2.unit
Unit("m")
Uncertainties¶
NDData
objects have an uncertainty
attribute that can be
used to set the uncertainty on the data values. The uncertainty
must have
an attribute uncertainty_type
otherwise it is wrapped inside an
UnknownUncertainty
.
While not a requirement, the following uncertainty_type
strings
are strongly recommended for common ways of specifying normal
distributions:
"std"
: ifuncertainty
stores the standard deviation/sigma (either a single value or on a per-pixel basis)."var"
: ifuncertainty
stores the variance (either a single value or on a per-pixel basis)."ivar"
: ifuncertainty
stores the inverse variance (either a single value or on a per-pixel basis).
Note
For information on creating your own uncertainty classes, see Subclassing.
Meta-data¶
The NDData
class includes a meta
attribute that
defaults to an empty ordered dictionary, and can be used to set overall meta-
data for the dataset:
ndd.meta['exposure_time'] = 340.
ndd.meta['filter'] = 'J'
Elements of the meta-data dictionary can be set to any valid Python object:
ndd.meta['history'] = ['calibrated', 'aligned', 'flat-fielded']
The metadata can be any python object that presents a dict-like interface. For example, a FITS header can be used as the metadata:
>>> from astropy.io import fits
>>> header = fits.Header()
>>> header['observer'] = 'Edwin Hubble'
>>> ndd = NDData(np.zeros([10, 10]), meta=header)
>>> ndd.meta['observer']
'Edwin Hubble'
WCS¶
At the moment the wcs
attribute can be set to any object, though in the
future it may be restricted to an WCS
object once a generalized
WCS object is developed.
Initialization with copy¶
The default way to create an NDData
instance is to try saving
the parameters as reference rather than as copy. Sometimes this is not possible
because the internal mechanics doesn’t allow for this. For example if the
data
is a list
then during initialization this is copied while converting
to a ndarray
. But it is also possible to enforce copies
during initialization by setting the copy
parameter to True
:
>>> array = np.array([1, 2, 3, 4])
>>> ndd = NDData(array)
>>> ndd.data[2] = 10
>>> array[2] # Original array is changed
10
>>> ndd2 = NDData(array, copy=True)
>>> ndd2.data[2] = 3
>>> array[2] # Original array is not changed.
10
Conflicting parameters¶
It is possible to initialize NDData
with an explicit
parameter in the initialization call and an implicit one in the data
parameter. If such a combination is detected a warning is
issued and the explicit parameter is kept (without any attempt of conversion).
For example with quantities:
>>> import astropy.units as u
>>> quantity = np.array([1, 2, 3, 4]) * u.m
>>> ndd = NDData(quantity, unit="cm") # Explicit unit is cm and implicit is m, keep explicit.
INFO: Overwriting Quantity's current unit with specified unit [astropy.nddata.nddata]
>>> ndd.unit
Unit("cm")
But this can affect the mask if the data
parameter is a MaskedArray
or even all attributes if the data
is another NDData
instance.
Converting to Numpy arrays¶
Data should be accessed through the data
attribute:
>>> array = np.asarray(ndd.data)
Though using np.asarray
is not required it will ensure that an additional
copy of the data is not made if the data is a numpy array.
Note that if the data is masked you must explicitly construct a numpy masked array like this:
>>> input_array = np.ma.array([1, 2, 3, 4], mask=[1, 0, 0, 1])
>>> ndd_masked = NDData(input_array)
>>> masked_array = np.ma.array(ndd_masked.data, mask=ndd_masked.mask)