子类化numpy.ma.MaskedArray

5 投票
1 回答
687 浏览
提问于 2025-04-17 13:56

我正在尝试对numpy的 MaskedArray 进行子类化,想要添加一个属性,但似乎没有得到预期的结果。

一开始我是按照 这个例子 来子类化 numpy.ndarray 的,那个例子运行得很好。

然后我尝试对 numpy.ma.MaskedArray 进行子类化,代码如下:

import numpy as np

class MyMaskedArray(np.ma.MaskedArray):

    def __new__(cls, input_array, info=None):
        obj = np.asarray(input_array).view(cls)
        obj.info = info
        return obj

    def __array_finalize__(self, obj):
        if obj is None: return
        self.info = getattr(obj, 'info', None)
        super(MyMaskedArray, self).__array_finalize__(obj)

arr = np.arange(5)
obj = MyMaskedArray(arr, info='information')
print obj.info
print obj[1:].info

结果是

information
None

我本来期待能看到“information”出现两次。

把这一行 obj = np.asarray(input_array).view(cls) 替换成 obj = np.ma.MaskedArray(input_array).view(cls) 或者 obj = np.ma.MaskedArray.__new__(cls, input_array) 并没有解决这个问题(我这么做是因为我想在将来的子类中传递 *args**kwargs__new__)。

需要注意的是,我还必须在我子类的 __array_finalize__ 中调用 MaskedArray.__array_finalize,这和 ndarray 子类的例子不同;如果不这样做,_mask 属性就找不到了。

也许有人能帮我解答:

  • 如何让 obj[1:].info 保持原来的 obj.info

  • 为什么 ndarray 不需要在 __array_finalize__ 中调用父类,而 MaskedArray 需要(这算是个额外的问题)。

我想对子类 MaskedArray,而不是写一个容器类,因为后者会失去一些 MaskedArray 带来的便利。

(注意:这和 这个问题 不一样,因为我已经“解决”了 __init__ / __new__ 的问题。)

1 个回答

2

为了让你的切片功能正常工作,你可能需要重写一下 __getitem__ 方法:

def __getitem__(self, item):
    out = np.ma.MaskedArray.__getitem__(self, item)
    out.info = self.info
    return out

同样的道理适用于 __setitem__ 方法。

如果你的 info 属性比较简单,比如你举的例子,你可以看看 MaskedArray_optinfo 属性,它就是为这个目的设计的:它其实就是一个字典,用来存储一些必须保留的信息。下面是一个例子:

class MyMaskedArray(np.ma.MaskedArray):

    def __new__(cls, input_array, info=None):
        obj = np.asarray(input_array).view(cls)
        obj._optinfo['info'] = info
        return obj

    @property
    def info(self):
        return self._optinfo.get('info', None)

注意这里的 .info 是一个只读属性,但其实很简单就可以让它变成可写的。

关于 __array_finalize__

我不太明白你的问题:ndarray 本身就是一个父类。MaskedArrayndarray 的子类,因此需要定义一个 __array_finalize__ 方法,特别是用来设置掩码(通过 _mask 参数)。想了解更多关于子类的信息,可以查看这个 链接

在你的例子中,你使用自己的 __array_finalize__ 来设置 .info 属性。在这种情况下,你确实需要调用父类的方法 MaskedArray.__array_finalize__,这是基本的 Python 子类规则。注意,如果你选择使用 _optinfo 的方式,就不需要明确地定义 __array_finalize__ 了……

注意

  • __array_prepare____array_wrap__ 方法实际上是用来在对 ndarray 子类实例应用函数之前准备它,以及处理函数结果的。

撰写回答