Subclassing

There are a couple of choices to be made in subclassing from the nddata package. For the greatest flexibility, subclass from NDDataBase, which places no restrictions on any of its attributes. In many cases, subclassing NDData will work instead; it is more straightforward but places some minimal restrictions on how the data can be represented.

NDDataBase

The class NDDataBase is a metaclass – when subclassing it, all properties of NDDataBase must be overriden in the subclass. For an example of how to do this, see the source code for astropy.nddata.NDData.

Subclassing from NDDataBase gives you complete flexibility in how you implement data storage and the other properties. If your data is stored in a numpy array (or something that behaves like a numpy array), it may be more straightforward to subclass NDData instead of NDDataBase.

NDData

This class serves as the base for subclasses that use a numpy array (or something that presents a numpy-like interface) as the data attribute.

For an example of a class that includes mixins and subclasses NDData to add additional functionality, see NDDataArray.

Subclassing NDUncertainty

This is done by using classes to represent the uncertainties of a given type. For example, to set standard deviation uncertainties on the pixel values, you can do:

>>> import numpy as np
>>> from astropy.nddata import NDData, StdDevUncertainty
>>> array = np.zeros((12, 12, 12))  # a 3-dimensional array with all zeros
>>> ndd = NDData(array)
>>> uncertainty = StdDevUncertainty(np.ones((12, 12, 12)) * 0.1)
>>> ndd_uncertainty = NDData(ndd, uncertainty=uncertainty)

New error classes should sub-class from NDUncertainty, and should provide methods with the following API:

class MyUncertainty(NDUncertainty):

    def propagate_add(self, other_nddata, result_data):
        ...
        result_uncertainty = MyUncertainty(...)
        return result_uncertainty

    def propagate_subtract(self, other_nddata, result_data):
        ...
        result_uncertainty = MyUncertainty(...)
        return result_uncertainty

    def propagate_multiply(self, other_nddata, result_data):
        ...
        result_uncertainty = MyUncertainty(...)
        return result_uncertainty

    def propagate_divide(self, other_nddata, result_data):
        ...
        result_uncertainty = MyUncertainty(...)
        return result_uncertainty

All error sub-classes inherit an attribute self.parent_nddata that is automatically set to the parent NDData object that they are attached to. The arguments passed to the error propagation methods are other_nddata, which is the NDData object that is being combined with self.parent_nddata, and result_data, which is a Numpy array that contains the data array after the arithmetic operation. All these methods should return an error instance result_uncertainty, and should not modify parent_nddata directly. For subtraction and division, the order of the operations is parent_nddata - other_nddata and parent_nddata / other_nddata.

To make it easier and clearer to code up the error propagation, you can use variables with more explicit names, e.g:

class MyUncertainty(NDUncertainty):

    def propogate_add(self, other_nddata, result_data):

        left_uncertainty = self.parent.uncertainty.array
        right_uncertainty = other_nddata.uncertainty.array

        ...

Note that the above example assumes that the errors are stored in an array attribute, but this does not have to be the case.

For an example of a complete implementation, see StdDevUncertainty.