子类别化#

NDData#

该类用作使用 numpy.ndarray (或是某事物 numpy -like interface)作为 data 属性。

备注

每个属性都保存为带有一个前导下划线的属性。例如 data 被保存为 _data 以及 mask 作为 _mask 等等。

添加其他属性#

>>> from astropy.nddata import NDData
>>> class NDDataWithFlags(NDData):
...     def __init__(self, *args, **kwargs):
...         # Remove flags attribute if given and pass it to the setter.
...         self.flags = kwargs.pop('flags') if 'flags' in kwargs else None
...         super().__init__(*args, **kwargs)
...
...     @property
...     def flags(self):
...         return self._flags
...
...     @flags.setter
...     def flags(self, value):
...         self._flags = value
>>> ndd = NDDataWithFlags([1,2,3])
>>> ndd.flags is None
True
>>> ndd = NDDataWithFlags([1,2,3], flags=[0, 0.2, 0.3])
>>> ndd.flags
[0, 0.2, 0.3]

备注

为了简化子类化,每个setter(除了 data )调用期间 __init__ 因此,对任何属性设置限制可以在setter内部完成,并且在实例创建期间也适用。

自定义属性的Setter#

>>> import numpy as np
>>> class NDDataMaskBoolNumpy(NDData):
...
...     @NDData.mask.setter
...     def mask(self, value):
...         # Convert mask to boolean numpy array.
...         self._mask = np.array(value, dtype=np.bool_)
>>> ndd = NDDataMaskBoolNumpy([1,2,3])
>>> ndd.mask = [True, False, True]
>>> ndd.mask
array([ True, False,  True]...)

扩展属性的Setter#

unitmetauncertainty 在它们的setter中实现一些额外的逻辑,这样子类可以定义对超类的调用,然后让超级属性设置属性:

>>> import numpy as np

>>> class NDDataUncertaintyShapeChecker(NDData):
...
...     @NDData.uncertainty.setter
...     def uncertainty(self, value):
...         value = np.asarray(value)
...         if value.shape != self.data.shape:
...             raise ValueError('uncertainty must have the same shape as the data.')
...         # Call the setter of the super class in case it might contain some
...         # important logic (only True for meta, unit and uncertainty)
...         super(NDDataUncertaintyShapeChecker, self.__class__).uncertainty.fset(self, value)
...         # Unlike "super(cls_name, cls_name).uncertainty.fset" or
...         # or "NDData.uncertainty.fset" this will respect Pythons method
...         # resolution order.

>>> ndd = NDDataUncertaintyShapeChecker([1,2,3], uncertainty=[2,3,4])
INFO: uncertainty should have attribute uncertainty_type. [astropy.nddata.nddata]
>>> ndd.uncertainty
UnknownUncertainty([2, 3, 4])

有一个数据设置器#

>>> class NDDataWithDataSetter(NDData):
...
...     @NDData.data.setter
...     def data(self, value):
...         self._data = np.asarray(value)
>>> ndd = NDDataWithDataSetter([1,2,3])
>>> ndd.data = [3,2,1]
>>> ndd.data
array([3, 2, 1])

NDDataRef#

NDDataRef 它本身继承自 NDData 所以所有的可能性也适用于NDDataRef。但是NDDataRef也继承了mixin:

允许额外的操作。

添加另一个算术运算#

如果 dataunit 允许在 Quantity .

实例#

要添加幂函数:

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

>>> class NDDataPower(NDDataRef):
...     @sharedmethod # sharedmethod to allow it also as classmethod
...     def pow(self, operand, operand2=None, **kwargs):
...         # the uncertainty doesn't allow propagation so set it to None
...         kwargs['propagate_uncertainties'] = None
...         # Call the _prepare_then_do_arithmetic function with the
...         # numpy.power ufunc.
...         return self._prepare_then_do_arithmetic(np.power, operand,
...                                                 operand2, **kwargs)

这可以像其他算术方法一样使用 add() . 所以在类或实例上调用它时,它是有效的:

>>> ndd = NDDataPower([1,2,3])

>>> # using it on the instance with one operand
>>> ndd.pow(3)  
NDDataPower([ 1,  8, 27]...)

>>> # using it on the instance with two operands
>>> ndd.pow([1,2,3], [3,4,5])  
NDDataPower([ 1, 16, 243]...)

>>> # or using it as classmethod
>>> NDDataPower.pow(6, [1,2,3])  
NDDataPower([ 6, 36, 216]...)

允许传播 uncertainty 请参见子类化 NDUncertainty .

这个 _prepare_then_do_arithmetic 在类或实例上实现相关检查,如果给定了一个或两个操作数,则在必要时将操作数转换为适当的类。压倒一切 _prepare_then_do_arithmetic 如果可能的话,应该避免使用in子类。

对现有属性的运算#

通过函数调用的一些参数,如 add() ,但也可以对行为进行硬编码。对属性的实际操作(除了 unit )是在一个方法中完成的 _arithmetic_* 在哪里? * 属性的名称。

实例#

自定义如何使用 meta 将在算术过程中受到影响::

>>> from astropy.nddata import NDDataRef

>>> from copy import deepcopy
>>> class NDDataWithMetaArithmetics(NDDataRef):
...
...     def _arithmetic_meta(self, operation, operand, handle_mask, **kwds):
...         # the function must take the arguments:
...         # operation (numpy-ufunc like np.add, np.subtract, ...)
...         # operand (the other NDData-like object, already wrapped as NDData)
...         # handle_mask (see description for "add")
...
...         # The meta is dict like but we want the keywords exposure to change
...         # Anticipate that one or both might have no meta and take the first one that has
...         result_meta = deepcopy(self.meta) if self.meta else deepcopy(operand.meta)
...         # Do the operation on the keyword if the keyword exists
...         if result_meta and 'exposure' in result_meta:
...             result_meta['exposure'] = operation(result_meta['exposure'], operand.data)
...         return result_meta # return it

若要触发此方法,则 handle_meta 算术方法的参数可以是除 None"first_found" ::

>>> ndd = NDDataWithMetaArithmetics([1,2,3], meta={'exposure': 10})
>>> ndd2 = ndd.add(10, handle_meta='')
>>> ndd2.meta
{'exposure': 20}

>>> ndd3 = ndd.multiply(0.5, handle_meta='')
>>> ndd3.meta
{'exposure': 5.0}

警告

内部使用这些 _arithmetic_* 方法调用操作时对属性有一些限制:

  • maskhandle_mask 不得 None"ff""first_found" .

  • wcscompare_wcs 参数具有与掩码相同的限制。

  • metahandle_meta 参数具有与掩码相同的限制。

  • uncertaintypropagate_uncertainties 必须是 None 或评估为 False . arithmetic_uncertainty 还必须接受不同的参数: operationoperandresultcorrelation**kwargs .

更改算术运算的默认参数#

如果目标是更改算术方法的现有参数的默认值,例如每次调用算术操作时显式指定参数太费劲时,可以通过在的方法签名中更改现有参数的默认值来更改该值 _arithmetic .

例子#

要更改算术方法的现有参数的默认值,请执行以下操作:

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

>>> class NDDDiffAritDefaults(NDDataRef):
...     def _arithmetic(self, *args, **kwargs):
...         # Changing the default of handle_mask to None
...         if 'handle_mask' not in kwargs:
...             kwargs['handle_mask'] = None
...         # Call the original with the updated kwargs
...         return super()._arithmetic(*args, **kwargs)

>>> ndd1 = NDDDiffAritDefaults(1, mask=False)
>>> ndd2 = NDDDiffAritDefaults(1, mask=True)
>>> # No mask handling logic will return no mask:
>>> ndd1.add(ndd2).mask

>>> # But giving other values is still possible:
>>> ndd1.add(ndd2, handle_mask=np.logical_or).mask
True

>>> ndd1.add(ndd2, handle_mask="ff").mask
False

控制如何处理属性的参数都是关键字,因此使用 *args**kwargs 位置方法只允许你改变一个默认的顺序。

具有附加属性的算术#

这还需要重写 _arithmetic 方法。假设我们有一个 flags 再次属性::

>>> from copy import deepcopy
>>> import numpy as np

>>> class NDDataWithFlags(NDDataRef):
...     def __init__(self, *args, **kwargs):
...         # Remove flags attribute if given and pass it to the setter.
...         self.flags = kwargs.pop('flags') if 'flags' in kwargs else None
...         super().__init__(*args, **kwargs)
...
...     @property
...     def flags(self):
...         return self._flags
...
...     @flags.setter
...     def flags(self, value):
...         self._flags = value
...
...     def _arithmetic(self, operation, operand, *args, **kwargs):
...         # take all args and kwargs to allow arithmetic on the other properties
...         # to work like before.
...
...         # do the arithmetic on the flags (pop the relevant kwargs, if any!!!)
...         if self.flags is not None and operand.flags is not None:
...             result_flags = np.logical_or(self.flags, operand.flags)
...             # np.logical_or is just a suggestion you can do what you want
...         else:
...             if self.flags is not None:
...                 result_flags = deepcopy(self.flags)
...             else:
...                 result_flags = deepcopy(operand.flags)
...
...         # Let the superclass do all the other attributes note that
...         # this returns the result and a dictionary containing other attributes
...         result, kwargs = super()._arithmetic(operation, operand, *args, **kwargs)
...         # The arguments for creating a new instance are saved in kwargs
...         # so we need to add another keyword "flags" and add the processed flags
...         kwargs['flags'] = result_flags
...         return result, kwargs # these must be returned

>>> ndd1 = NDDataWithFlags([1,2,3], flags=np.array([1,0,1], dtype=bool))
>>> ndd2 = NDDataWithFlags([1,2,3], flags=np.array([0,0,1], dtype=bool))
>>> ndd3 = ndd1.add(ndd2)
>>> ndd3.flags
array([ True, False,  True]...)

切片现有属性#

假设你有一个类需要2D data 如果你是一维的话,问题就在一维。

>>> from astropy.nddata import NDDataRef
>>> import numpy as np
>>> class NDDataMask1D(NDDataRef):
...     def _slice_mask(self, item):
...         # Multidimensional slices are represented by tuples:
...         if isinstance(item, tuple):
...             # only use the first dimension of the slice
...             return self.mask[item[0]]
...         # Let the superclass deal with the other cases
...         return super()._slice_mask(item)
>>> ndd = NDDataMask1D(np.ones((3,3)), mask=np.ones(3, dtype=bool))
>>> nddsliced = ndd[1:3,1:3]
>>> nddsliced.mask
array([ True,  True]...)

备注

切片属性的方法的前缀是 _slice_* 在哪里? * 可以是 maskuncertaintywcs . 因此重写它们是定制属性切片方式的最方便方法。

备注

如果切片会影响 unitmeta 请参见下一个示例。

切片附加属性#

在增加的财产上建造 flags ,我们希望它们是可切片的:

>>> class NDDataWithFlags(NDDataRef):
...     def __init__(self, *args, **kwargs):
...         # Remove flags attribute if given and pass it to the setter.
...         self.flags = kwargs.pop('flags') if 'flags' in kwargs else None
...         super().__init__(*args, **kwargs)
...
...     @property
...     def flags(self):
...         return self._flags
...
...     @flags.setter
...     def flags(self, value):
...         self._flags = value
...
...     def _slice(self, item):
...         # slice all normal attributes
...         kwargs = super()._slice(item)
...         # The arguments for creating a new instance are saved in kwargs
...         # so we need to add another keyword "flags" and add the sliced flags
...         kwargs['flags'] = self.flags[item]
...         return kwargs # these must be returned
>>> ndd = NDDataWithFlags([1,2,3], flags=[0, 0.2, 0.3])
>>> ndd2 = ndd[1:3]
>>> ndd2.flags
[0.2, 0.3]

如果你想保留原版的话 flags 你可以用 kwargs['flags'] = self.flags 省略 [item] .

NDDataBase#

The class NDDataBase is a metaclass — when subclassing it, all properties of NDDataBase must be overridden in the subclass.

子类化自 NDDataBase 使您在如何实现数据存储和其他属性方面具有完全的灵活性。如果您的数据存储在 numpy 数组(或类似于 numpy 数组),它可能更方便的子类化 NDData 而不是 NDDataBase .

例子#

要通过创建只读容器实现NDDataBase接口:

>>> from astropy.nddata import NDDataBase

>>> class NDDataReadOnlyNoRestrictions(NDDataBase):
...     def __init__(self, data, unit, mask, uncertainty, meta, wcs, psf):
...         self._data = data
...         self._unit = unit
...         self._mask = mask
...         self._uncertainty = uncertainty
...         self._meta = meta
...         self._wcs = wcs
...         self._psf = psf
...
...     @property
...     def data(self):
...         return self._data
...
...     @property
...     def unit(self):
...         return self._unit
...
...     @property
...     def mask(self):
...         return self._mask
...
...     @property
...     def uncertainty(self):
...         return self._uncertainty
...
...     @property
...     def meta(self):
...         return self._meta
...
...     @property
...     def wcs(self):
...         return self._wcs
...
...     @property
...     def psf(self):
...         return self._psf

>>> # A meaningless test to show that creating this class is possible:
>>> NDDataReadOnlyNoRestrictions(1,2,3,4,5,6,7) is not None
True

备注

实际定义 __init__ 属性可以返回任意值,但属性 must 被定义。

子类别化 NDUncertainty#

警告

NDUncertainty和子类的内部接口是实验性的,在将来的版本中可能会改变。

派生自 NDUncertainty 需要实施:

  • 财产 uncertainty_type 应该返回一个描述不确定性的字符串,例如, "ivar" 对于反向方差。

  • 传播方法: _propagate_* 在哪里? * 上使用的通用函数(ufunc)的名称 NDData 起源。

制造不确定性而不传播#

UnknownUncertainty 是一个没有错误传播的最小工作实现。我们可以通过存储系统不确定性来创建不确定性:

>>> from astropy.nddata import NDUncertainty

>>> class SystematicUncertainty(NDUncertainty):
...     @property
...     def uncertainty_type(self):
...         return 'systematic'
...
...     def _data_unit_to_uncertainty_unit(self, value):
...         return None
...
...     def _propagate_add(self, other_uncert, *args, **kwargs):
...         return None
...
...     def _propagate_subtract(self, other_uncert, *args, **kwargs):
...         return None
...
...     def _propagate_multiply(self, other_uncert, *args, **kwargs):
...         return None
...
...     def _propagate_divide(self, other_uncert, *args, **kwargs):
...         return None

>>> SystematicUncertainty([10])
SystematicUncertainty([10])