astro py:docs

- Index
- Modules

### Navigation

- next »
- « previous |
- Astropy v5.2.1 »
- N-Dimensional Datasets (`astropy.nddata`) »
- NDData

# NDData

## Overview

`NDData` is based on `numpy.ndarray`-like `data` with additional meta attributes:

- `meta` for general metadata

- `unit` represents the physical unit of the data

- `uncertainty` for the uncertainty of the data

- `mask` indicates invalid points in the data

- `wcs` represents the relationship between the data grid and world coordinates

- `psf` holds an image representation of the point spread function (PSF)

Each of these attributes can be set during initialization or directly on the instance. Only the `data` cannot be directly set after creating the instance.

## Data

The data is the base of `NDData` and is required to be `numpy.ndarray`-like. It is the only property that is required to create an instance and it cannot be directly set on the instance.

### Example

To create an instance:

    >>> import numpy as np
    >>> from astropy.nddata import NDData
    >>> array = np.array([[0, 1, 0], [1, 0, 1], [0, 1, 0]])
    >>> ndd = NDData(array)
    >>> ndd
    NDData([[0, 1, 0],
            [1, 0, 1],
            [0, 1, 0]])

And access by the `data` attribute:

    >>> ndd.data
    array([[0, 1, 0],
           [1, 0, 1],
           [0, 1, 0]])

As already mentioned, it is not possible to set the data directly. So `ndd.data = np.arange(9)` will raise an exception. But the data can be modified in place:

    >>> ndd.data[1,1] = 100
    >>> ndd.data
    array([[  0,   1,   0],
           [  1, 100,   1],
           [  0,   1,   0]])

### Data During Initialization

During initialization it is possible to provide data that is not a `numpy.ndarray` but convertible to one.

#### Examples

To provide data that is convertible to a `numpy.ndarray`, you can pass a `list` containing numerical values:

    >>> alist = [1, 2, 3, 4]
    >>> ndd = NDData(alist)
    >>> ndd.data  # data will be a numpy-array:
    array([1, 2, 3, 4])

A nested `list` or `tuple` is possible, but if these contain non-numerical values the conversion might fail.

Besides input that is convertible to such an array, you can also use the `data` parameter to pass implicit additional information. For example, if the data is another `NDData` object it implicitly uses its properties:

    >>> ndd = NDData(ndd, unit = 'm')
    >>> ndd2 = NDData(ndd)
    >>> ndd2.data  # It has the same data as ndd
    array([1, 2, 3, 4])
    >>> ndd2.unit  # but it also has the same unit as ndd
    Unit("m")

Another possibility is to use a `Quantity` as a `data` parameter:

    >>> import astropy.units as u
    >>> quantity = np.ones(3) * u.cm  # this will create a Quantity
    >>> ndd3 = NDData(quantity)
    >>> ndd3.data
    array([1., 1., 1.])
    >>> ndd3.unit
    Unit("cm")

Or a `numpy.ma.MaskedArray`:

    >>> masked_array = np.ma.array([5,10,15], mask=[False, True, False])
    >>> ndd4 = NDData(masked_array)
    >>> ndd4.data
    array([ 5, 10, 15])
    >>> ndd4.mask
    array([False,  True, False]...)

If such an implicitly passed property conflicts with an explicit parameter, the explicit parameter will be used and an info message will be issued:

    >>> quantity = np.ones(3) * u.cm
    >>> ndd6 = NDData(quantity, unit='m')
    INFO: overwriting Quantity's current unit with specified unit. [astropy.nddata.nddata]
    >>> ndd6.data
    array([1., 1., 1.])
    >>> ndd6.unit
    Unit("m")

The unit of the `Quantity` is being ignored and the unit is set to the explicitly passed one.

It might be possible to pass other classes as a `data` parameter as long as they have the properties `shape`, `dtype`, `__getitem__`, and `__array__`.

The purpose of this mechanism is to allow considerable flexibility in the objects used to store the data while providing a useful default (`numpy` array).

## Mask

The `mask` is being used to indicate if data points are valid or invalid. `NDData` does not restrict this mask in any way but it is expected to follow the `numpy.ma.MaskedArray` convention in that the mask:

- Returns `True` for data points that are considered invalid.

- Returns `False` for those points that are valid.

### Examples

One possibility is to create a mask by using `numpy`’s comparison operators:

    >>> array = np.array([0, 1, 4, 0, 2])

    >>> mask = array == 0  # Mask points containing 0
    >>> mask
    array([ True, False, False,  True, False]...)

    >>> other_mask = array > 1  # Mask points with a value greater than 1
    >>> other_mask
    array([False, False,  True, False,  True]...)

And initialize the `NDData` instance using the `mask` parameter:

    >>> ndd = NDData(array, mask=mask)
    >>> ndd.mask
    array([ True, False, False,  True, False]...)

Or by replacing the mask:

    >>> ndd.mask = other_mask
    >>> ndd.mask
    array([False, False,  True, False,  True]...)

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` represents the unit of the data values. It is required to be `Unit`-like or a string that can be converted to such a `Unit`:

    >>> import astropy.units as u
    >>> ndd = NDData([1, 2, 3, 4], unit="meter")  # using a string
    >>> ndd.unit
    Unit("m")

..note::

  Setting the `unit` on an instance is not possible.

## Uncertainties

The `uncertainty` represents an arbitrary representation of the error of the data values. To indicate which kind of uncertainty representation is used, the `uncertainty` should have an `uncertainty_type` property. If no such property is found it will be wrapped inside a `UnknownUncertainty`.

The `uncertainty_type` should follow the `StdDevUncertainty` convention in that it returns a short string like `"std"` for an uncertainty given in standard deviation. Other examples are `VarianceUncertainty` and `InverseVariance`.

### Examples

Like the other properties the `uncertainty` can be set during initialization:

    >>> from astropy.nddata import StdDevUncertainty, InverseVariance
    >>> array = np.array([10, 7, 12, 22])
    >>> uncert = StdDevUncertainty(np.sqrt(array))
    >>> ndd = NDData(array, uncertainty=uncert)
    >>> ndd.uncertainty
    StdDevUncertainty([3.16227766, 2.64575131, 3.46410162, 4.69041576])

Or on the instance directly:

    >>> other_uncert = StdDevUncertainty([2,2,2,2])
    >>> ndd.uncertainty = other_uncert
    >>> ndd.uncertainty
    StdDevUncertainty([2, 2, 2, 2])

But it will print an info message if there is no `uncertainty_type`:

    >>> ndd.uncertainty = np.array([5, 1, 2, 10])
    INFO: uncertainty should have attribute uncertainty_type. [astropy.nddata.nddata]
    >>> ndd.uncertainty
    UnknownUncertainty([ 5,  1,  2, 10])

It is also possible to convert between uncertainty types:

    >>> uncert.represent_as(InverseVariance)
    InverseVariance([0.1       , 0.14285714, 0.08333333, 0.04545455])

### WCS

The `wcs` should contain a mapping from the gridded data to world coordinates. There are no restrictions placed on the property currently but it may be restricted to an `WCS` object or a more generalized WCS object in the future.

Note

Like the unit the `wcs` cannot be set on an instance.

## Metadata

The `meta` property contains all further meta information that does not fit any other property.

### Examples

If the `meta` property is given it must be `dict`-like:

    >>> ndd = NDData([1,2,3], meta={'observer': 'myself'})
    >>> ndd.meta
    {'observer': 'myself'}

`dict`-like means it must be a mapping from some keys to some values. This also includes `Header` objects:

    >>> 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'

If the `meta` property is not provided or explicitly set to `None`, it will default to an empty `collections.OrderedDict`:

    >>> ndd.meta = None
    >>> ndd.meta
    OrderedDict()

    >>> ndd = NDData([1,2,3])
    >>> ndd.meta
    OrderedDict()

The `meta` object therefore supports adding or updating these values:

    >>> ndd.meta['exposure_time'] = 340.
    >>> ndd.meta['filter'] = 'J'

Elements of the metadata dictionary can be set to any valid Python object:

    >>> ndd.meta['history'] = ['calibrated', 'aligned', 'flat-fielded']

## Initialization with Copy

The default way to create an `NDData` instance is to try saving the parameters as references to the original rather than as copy. Sometimes this is not possible because the internal mechanics do not allow for this.

### Examples

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 has changed
    10

    >>> ndd2 = NDData(array, copy=True)
    >>> ndd2.data[2] = 3
    >>> array[2]  # Original array hasn't changed.
    10

Note

In some cases setting `copy=True` will copy the `data` twice. Known cases are if the `data` is a `list` or `tuple`.

## Converting NDData to Other Classes

There is limited support to convert a `NDData` instance to other classes. In the process some properties might be lost.

    >>> data = np.array([1, 2, 3, 4])
    >>> mask = np.array([True, False, False, True])
    >>> unit = 'm'
    >>> ndd = NDData(data, mask=mask, unit=unit)

### `numpy.ndarray`

Converting the `data` to an array:

    >>> array = np.asarray(ndd.data)
    >>> array
    array([1, 2, 3, 4])

Though using `np.asarray` is not required, in most cases it will ensure that the result is always a `numpy.ndarray`

### `numpy.ma.MaskedArray`

Converting the `data` and `mask` to a MaskedArray:

    >>> masked_array = np.ma.array(ndd.data, mask=ndd.mask)
    >>> masked_array
    masked_array(data=[--, 2, 3, --],
                 mask=[ True, False, False,  True],
           fill_value=999999)

### `Quantity`

Converting the `data` and `unit` to a Quantity:

    >>> quantity = u.Quantity(ndd.data, unit=ndd.unit)
    >>> quantity
    <Quantity [1., 2., 3., 4.] m>

Note

Ideally, you would construct masked quantities, but these are not properly supported: many operations on them fail.