【问题标题】:Array of a subclass of numpynumpy 子类的数组
【发布时间】:2014-07-22 23:03:13
【问题描述】:

我有问题。问题是:我想创建一个 numpy 数组的子类,然后创建一个该类型的对象数组。当我引用该数组中的一个项目时,我希望它仍然是该子类的一个实例。相反,它是一个 numpy 数组的实例。

这是一个失败的测试:

import numpy as np


class ImageWrapper(np.ndarray):

    def __new__(cls, image_data):
        assert image_data.ndim in (2, 3)
        return image_data.view(cls)

    @property
    def n_colours(self): 
        return 1 if self.ndim==2 else self.shape[2]


n_frames = 10
frames = [ImageWrapper(np.random.randint(255, size = (20, 15, 3)).astype('uint8')) for _ in xrange(n_frames)]
video = np.array(frames)
assert video[0].n_colours == 3

给我:AttributeError: 'numpy.ndarray' 对象没有属性 'n_colours'

我怎样才能做到这一点?

已经尝试过的事情:

  • 在构造视频时设置 subok=True - 这仅适用于从子类对象的单个实例而不是列表构造数组时。
  • 设置 dtype=object 或 dtype=ImageWrapper 不起作用

我知道我可以将视频制作成一个列表,但出于其他原因,最好将其保留为 numpy 数组。

【问题讨论】:

  • 问题是当您在 3D 数组列表上调用 array 时,您会得到一个 4D 数组,而不是一个充满 3D 数组的 1D 数组。显然,这个 4D 数组不可能是 ImageWrapper,所以它是 ndarray,所以它的任何切片也是 ndarray,无论数据最初来自哪里。问题是,为什么你希望它是一个数组? object 的一维数组并没有失去 numpy 优于原生列表的 所有 优点,但它失去了 很多 它们,如果你能告诉我们最后一句中的“其他原因”,它可能会有所帮助。
  • 另外,您的设计是否有理由要求 4D 数组不能成为ImageWrapper?如果 N>3(或只是引发异常),您的 n_colours 将不得不返回一个 N-3 维数组而不是一个标量,否则,会有什么问题?因为那会让事情变得简单得多……
  • “其他原因”不是一个很好的原因,只是这是接口的一部分,其中数组是预期的数据类型。在这种情况下,Jaime 建议的对象数组就可以了。
  • 但是 4D 数组子类也不能工作吗?在我看来,如果您的代码需要一个数组,它会想要在该数组上广播 n_colourscropdownsample 等等。 4D 数组免费为您提供所有这些(至少在调用方;您必须在实现方面小心,如 Bi Rico 所示);对于数组对象数组,您必须手动将未绑定的方法包装在 ufunc 中才能执行任何操作。 (除非您打算只迭代对象,在这种情况下,为什么要使用数组?)

标签: python arrays numpy subclassing multidimensional-array


【解决方案1】:

无论你想要实现什么,可能有比继承 ndarray 更好的方法。但是考虑到这一点,你可以让你的数组的类型为object,尽管在创建它时你必须小心。这有效:

>>> video = np.empty((len(frames),), dtype=object)
>>> video[:] = frames
>>> video[0].n_colours
3

但这不是:

>>> video = np.array(frames, dtype=object)
>>> video[0].n_colours
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'numpy.ndarray' object has no attribute 'n_colours'

【讨论】:

  • 这对我有用,谢谢。造成这种情况的真正原因是我们有一个奇怪的图像格式 (YUV),我们希望有绑定到它的方法来执行裁剪和下采样等操作。子类化而不是包装的原因更值得商榷——主要是因为我们在 python 中工作但想假装我们在 Java 中,并且我们的接口要求模块之间传递的数据是数组类型。
  • @Peter:YUV 并不是那么奇怪或不常见。此外,如果ImageWrapper 可以保存一个 >3D 数组,对您来说似乎更好,在这种情况下,调用它的方法将有效地在每个 3D 子数组上广播调用,而不是使您手动迭代每个 3D 子数组。 (这就是我在评论中的建议,我很确定 Bi Rico 在他的回答中的想法。)相反,如果你真的想强迫自己迭代数组,你实际上最好使用一个列表它们比数组。
【解决方案2】:

numpy.array 只是不够复杂,无法处理这种情况。 subok=True 告诉函数通过子类传递,但你没有传递给它一个 ndarray 的子类,你传递给它一个列表(恰好填充了 ndarray 子类的实例)。你可以通过这样做得到你所期望的东西:

import numpy as np


class ImageWrapper(np.ndarray):

    def __new__(cls, image_data):
        assert 2 <= image_data.ndim <= 4
        return image_data.view(cls)

    @property
    def n_colours(self): 
        return 1 if self.ndim==2 else self.shape[-1]


n_frames = 10
frame_shape = (20, 15, 3)
video = ImageWrapper(np.empty((n_frames,) + frame_shape, dtype='uint8'))
for i in xrange(n_frames):
    video[i] = np.random.randint(255, size=(20, 15, 3))
assert video[0].n_colours == 3

请注意,我必须更新 ImageWrapper 以允许 4d 数组作为输入。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-18
    • 2012-03-17
    • 1970-01-01
    • 2019-03-02
    相关资源
    最近更新 更多