【问题标题】:Subclassing numpy ndarray problem子类化numpy ndarray问题
【发布时间】:2011-03-01 00:38:08
【问题描述】:

我想继承 numpy ndarray。但是,我无法更改数组。为什么self = ... 不改变数组?谢谢。

import numpy as np

class Data(np.ndarray):

    def __new__(cls, inputarr):
        obj = np.asarray(inputarr).view(cls)
        return obj

    def remove_some(self, t):
        test_cols, test_vals = zip(*t)
        test_cols = self[list(test_cols)]
        test_vals = np.array(test_vals, test_cols.dtype)

        self = self[test_cols != test_vals] # Is this part correct?

        print len(self) # correct result

z = np.array([(1,2,3), (4,5,6), (7,8,9)],
    dtype=[('a', int), ('b', int), ('c', int)])
d = Data(z)
d.remove_some([('a',4)])

print len(d)  # output the same size as original. Why?

【问题讨论】:

  • 请提供您的预期输出,目前尚不清楚您想要实现什么。
  • 我想从 Data 实例中删除行。
  • 好的,你可以使用掩码,但如果你问另一个问题更好,因为这与子类化 ndarray 没有太大关系
  • [已发布另一个问题][1] 在子类化 ndarray 时遇到同样的问题。 [1]:stackoverflow.com/questions/16049437/…

标签: python numpy subclass recarray


【解决方案1】:

您没有得到预期结果的原因是您在方法remove_some 中重新分配self。您只是在创建一个新的局部变量self。如果您的数组形状不改变,您可以简单地执行 self[:] = ... 并且您可以保留对 self 的引用,一切都会好起来的,但是您正在尝试更改 self 的形状。这意味着我们需要重新分配一些新内存并更改引用self 时指向的位置。

我不知道该怎么做。我认为可以通过__array_finalize____array____array_wrap__ 来实现。但是我尝试过的一切都失败了。

现在,还有另一种方法可以解决这个问题,它不会继承 ndarray。您可以创建一个新类来保留一个 ndarray 属性,然后覆盖所有常见的 __add____mul__ 等。像这样:

Class Data(object):
    def __init__(self, inarr):
        self._array = np.array(inarr)
    def remove_some(x):
        self._array = self._array[x]
    def __add__(self, other):
        return np.add(self._array, other)

好吧,你明白了。覆盖所有操作符很痛苦,但从长远来看,我认为更灵活。

您必须彻底阅读this 才能正确操作。像__array_finalize__ 这样的方法需要在合适的时间调用才能进行“清理”。

【讨论】:

  • 我认为__array_finalize__ 用于启动新实例时,例如添加额外属性。
  • 我认为每当您像 OP 一样重新分配数组时都必须调用它。但老实说,这超出了我的想象。 __array_wrap__ 似乎更接近于想要的,但只有在被 ufunc 调用时才会返回。
【解决方案2】:

也许把它变成一个函数,而不是一个方法:

import numpy as np

def remove_row(arr,col,val):
    return arr[arr[col]!=val]

z = np.array([(1,2,3), (4,5,6), (7,8,9)],
    dtype=[('a', int), ('b', int), ('c', int)])

z=remove_row(z,'a',4)
print(repr(z))

# array([(1, 2, 3), (7, 8, 9)], 
#       dtype=[('a', '<i4'), ('b', '<i4'), ('c', '<i4')])

或者,如果你想要它作为一种方法,

import numpy as np

class Data(np.ndarray):

    def __new__(cls, inputarr):
        obj = np.asarray(inputarr).view(cls)
        return obj

    def remove_some(self, col, val):
        return self[self[col] != val]

z = np.array([(1,2,3), (4,5,6), (7,8,9)],
    dtype=[('a', int), ('b', int), ('c', int)])
d = Data(z)
d = d.remove_some('a', 4)
print(d)

这里的关键区别在于remove_some 不会尝试修改self,它只是返回Data 的新实例。

【讨论】:

  • 这可能是对这个问题的有用回答,但不是答案。为什么 self = ... 不改变值?也许答案在下面?否则我会重新发布。
  • 想象一下d,你的Data 实例。它指向一个保存底层数据的内存块。要就地删除列,您必须将其他列一起移动,然后调整数组的大小。当您说self = some_other_array 时,您将变量名称self 重定向到另一个内存块。然而,在remove_row 方法之外,变量名d 仍然指向原始内存块。所以无法修改d
  • 我读过的关于 numpy 的所有建议都说不应该尝试调整 numpy 数组的大小。可以这样做,但是所有的复制都使它变慢。最好使用切片来创建视图,或者使用精美的索引来创建具有所需数据的新数组。是的,创建一个新数组也涉及复制,但至少您可以避免就地移动数据的复杂性。
【解决方案3】:

我也尝试过,但是继承ndarray真的很复杂。

如果你只需要添加一些功能,我建议创建一个将数组存储为属性的类。

class Data(object):

    def __init__(self, array):
        self.array = array

    def remove_some(self, t):
        //operate on self.array
        pass

d = Data(z)
print(d.array)

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-03
  • 2015-09-25
  • 2011-07-18
  • 2021-02-16
  • 2022-01-15
  • 2013-10-13
相关资源
最近更新 更多