# Licensed under a 3-clause BSD style license - see LICENSE.rst
# This module implements the base NDData class.
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import collections
import numpy as np
from .nddata_base import NDDataBase
from ..units import Unit, Quantity
from .. import log
from ..utils.compat.odict import OrderedDict
from ..config import ConfigAlias
__all__ = ['NDData']
__doctest_skip__ = ['NDData']
WARN_UNSUPPORTED_CORRELATED = ConfigAlias(
'0.4', 'WARN_UNSUPPORTED_CORRELATED', 'warn_unsupported_correlated',
'astropy.nddata.nddata', 'astropy.nddata')
[docs]class NDData(NDDataBase):
"""
A basic class for array-based data.
The key distinction from raw numpy arrays is the presence of
additional metadata such as uncertainties, a mask, units,
and/or a coordinate system.
Parameters
-----------
data : `~numpy.ndarray`, `~numpy.ndarray`-like, or `NDData`
The actual data contained in this `NDData` object. If possible, data
will not be copied`data`, so you should make copy the ``data`` before
passing it in if that's the desired behavior.
uncertainty : any type, optional
Uncertainty on the data. The uncertainty *must* have a string attribute
named ``uncertainty_type``, but there is otherwise no restriction.
mask : `~numpy.ndarray`-like, optional
Mask for the data. The values must be ``False`` where
the data is *valid* and ``True`` when it is not (like Numpy
masked arrays). If ``data`` is a numpy masked array, providing
``mask`` here will causes the mask from the masked array to be
ignored.
wcs : undefined, optional
WCS-object containing the world coordinate system for the data.
meta : `dict`-like object, optional
Metadata for this object. Must be dict-like but no further restriction
is placed on meta.
unit : `~astropy.units.UnitBase` instance or str, optional
The units of the data. If data is an `~astropy.units.Quantity` then
``unit`` is set to the unit of the data; is a unit is also explicitly
provided an error is raised.
Notes
-----
The data in a `NDData` object should be accessed through the data
attribute.
For example::
>>> from astropy.nddata import NDData
>>> x = NDData([1,2,3])
>>> x.data
array([1, 2, 3])
"""
def __init__(self, data, uncertainty=None, mask=None, wcs=None,
meta=None, unit=None):
super(NDData, self).__init__()
if isinstance(data, self.__class__):
# No need to check the data because data must have successfully
# initialized.
self._data = data._data
self.uncertainty = data.uncertainty
self._mask = data.mask
self._wcs = data.wcs
self._meta = data.meta
self._unit = data.unit
if uncertainty is not None:
self._uncertainty = uncertainty
log.info("Overwriting NDData's current uncertainty being"
" overwritten with specified uncertainty")
if mask is not None:
self._mask = mask
log.info("Overwriting NDData's current "
"mask with specified mask")
if wcs is not None:
self._wcs = wcs
log.info("Overwriting NDData's current wcs with specified wcs")
if meta is not None:
self._meta = meta
log.info("Overwriting NDData's current meta "
"with specified meta")
if unit is not None and unit is not data.unit:
raise ValueError('Unit provided in initializer does not '
'match data unit.')
else:
if hasattr(data, 'mask'):
self._data = np.array(data.data, subok=True, copy=False)
if mask is not None:
self._mask = mask
log.info("NDData was created with a masked array, and a "
"mask was explicitly provided to NDData. The "
"explicitly passed-in mask will be used and the "
"masked array's mask will be ignored.")
else:
self._mask = data.mask
elif isinstance(data, Quantity):
self._data = np.array(data.value, subok=True, copy=False)
self._mask = mask
elif (not hasattr(data, 'shape') or
not hasattr(data, '__getitem__') or
not hasattr(data, '__array__')):
# Data doesn't look like a numpy array, try converting it to
# one.
self._data = np.array(data, subok=True, copy=False)
# Quick check to see if what we got out looks like an array
# rather than an object (since numpy will convert a
# non-numerical input to an array of objects).
if self._data.dtype == 'O':
raise TypeError("Could not convert data to numpy array.")
self._mask = mask
else:
self._data = data # np.array(data, subok=True, copy=False)
self._mask = mask
self._wcs = wcs
if meta is None:
self._meta = OrderedDict()
elif not isinstance(meta, collections.Mapping):
raise TypeError("meta attribute must be dict-like")
else:
self._meta = meta
if isinstance(data, Quantity):
if unit is not None:
raise ValueError("Cannot use the unit argument when data "
"is a Quantity")
else:
self._unit = data.unit
else:
if unit is not None:
self._unit = Unit(unit)
else:
self._unit = None
# This must come after self's unit has been set so that the unit
# of the uncertainty, if any, can be converted to the unit of the
# unit of self.
self.uncertainty = uncertainty
def __str__(self):
return str(self.data)
def __repr__(self):
prefix = self.__class__.__name__ + '('
body = np.array2string(self.data, separator=', ', prefix=prefix)
return ''.join([prefix, body, ')'])
@property
def data(self):
return self._data
@property
def mask(self):
return self._mask
@mask.setter
def mask(self, value):
self._mask = value
@property
def unit(self):
return self._unit
@property
def wcs(self):
return self._wcs
@property
def meta(self):
return self._meta