Slicing and Indexing NDData

Introduction

This page only deals with peculiarities applying to NDData-like classes. For a tutorial about slicing/indexing see the python documentation and numpy documentation.

Warning

NDData and NDDataRef enforce almost no restrictions on the properties so it might happen that some valid but unusual combination of properties always results in an IndexError or incorrect results. In this case see Subclassing on how to customize slicing for a particular property.

Slicing NDDataRef

Unlike NDData the class NDDataRef implements slicing or indexing. The result will be wrapped inside the same class as the sliced object.

Getting one element:

>>> import numpy as np
>>> from astropy.nddata import NDDataRef

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

Getting a sliced portion of the original:

>>> ndd[1:3]  # Get element 1 (inclusive) to 3 (exclusive)
NDDataRef([2, 3])

This will return a reference (and as such not a copy) of the original properties so changing a slice will affect the original:

>>> ndd_sliced = ndd[1:3]
>>> ndd_sliced.data[0] = 5
>>> ndd_sliced
NDDataRef([5, 3])
>>> ndd
NDDataRef([1, 5, 3, 4])

except you indexed only one element (for example ndd_sliced = ndd[1]). Then the element is a scalar and changes will not propagate to the original.

Slicing NDDataRef including attributes

In case a wcs, mask or uncertainty is present this attribute will be sliced too:

>>> from astropy.nddata import StdDevUncertainty
>>> data = np.array([1, 2, 3, 4])
>>> mask = data > 2
>>> uncertainty = StdDevUncertainty(np.sqrt(data))
>>> wcs = np.ones(4)
>>> ndd = NDDataRef(data, mask=mask, uncertainty=uncertainty, wcs=wcs)
>>> ndd_sliced = ndd[1:3]

>>> ndd_sliced.data
array([2, 3])

>>> ndd_sliced.mask
array([False,  True]...)

>>> ndd_sliced.uncertainty  
StdDevUncertainty([1.41421356, 1.73205081])

>>> ndd_sliced.wcs  
array([1., 1.])

but unit and meta will be unaffected.

If any of the attributes is set but doesn’t implement slicing an info will be printed and the property will be kept as is:

>>> data = np.array([1, 2, 3, 4])
>>> mask = False
>>> uncertainty = StdDevUncertainty(0)
>>> wcs = {'a': 5}
>>> ndd = NDDataRef(data, mask=mask, uncertainty=uncertainty, wcs=wcs)
>>> ndd_sliced = ndd[1:3]
INFO: uncertainty cannot be sliced. [astropy.nddata.mixins.ndslicing]
INFO: mask cannot be sliced. [astropy.nddata.mixins.ndslicing]
INFO: wcs cannot be sliced. [astropy.nddata.mixins.ndslicing]

>>> ndd_sliced.mask
False

Example: Remove masked data

Warning

If you are using a WCS object as wcs this will NOT be possible. But you could work around it, i.e. set it to None before slicing.

By convention the mask attribute indicates if a point is valid or invalid. So we are able to get all valid data points by slicing with the mask:

>>> data = np.array([[1,2,3],[4,5,6],[7,8,9]])
>>> mask = np.array([[0,1,0],[1,1,1],[0,0,1]], dtype=bool)
>>> uncertainty = StdDevUncertainty(np.sqrt(data))
>>> ndd = NDDataRef(data, mask=mask, uncertainty=uncertainty)
>>> # don't forget that ~ or you'll get the invalid points
>>> ndd_sliced = ndd[~ndd.mask]
>>> ndd_sliced
NDDataRef([1, 3, 7, 8])

>>> ndd_sliced.mask
array([False, False, False, False]...)

>>> ndd_sliced.uncertainty  
StdDevUncertainty([1.        , 1.73205081, 2.64575131, 2.82842712])

or all invalid points:

>>> ndd_sliced = ndd[ndd.mask] # without the ~ now!
>>> ndd_sliced
NDDataRef([2, 4, 5, 6, 9])

>>> ndd_sliced.mask
array([ True,  True,  True,  True,  True]...)

>>> ndd_sliced.uncertainty  
StdDevUncertainty([1.41421356, 2.        , 2.23606798, 2.44948974, 3.        ])

Note

The result of this kind of indexing (boolean indexing) will always be one-dimensional!