子类别化#
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#
unit
, meta
和 uncertainty
在它们的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:
允许额外的操作。
添加另一个算术运算#
如果 data
和 unit
允许在 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_*
方法调用操作时对属性有一些限制:
mask
:handle_mask
不得None
,"ff"
或"first_found"
.wcs
:compare_wcs
参数具有与掩码相同的限制。meta
:handle_meta
参数具有与掩码相同的限制。uncertainty
:propagate_uncertainties
必须是None
或评估为False
.arithmetic_uncertainty
还必须接受不同的参数:operation
,operand
,result
,correlation
,**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_*
在哪里? *
可以是 mask
, uncertainty
或 wcs
. 因此重写它们是定制属性切片方式的最方便方法。
备注
如果切片会影响 unit
或 meta
请参见下一个示例。
切片附加属性#
在增加的财产上建造 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])