NumPy:为什么需要明确复制一个值?

时间:2015-07-23 07:52:34

标签: python numpy

arr = np.arange(0,11)
slice_of_arr = arr[0:6]
slice_of_arr[:]=99

# slice_of_arr returns
array([99, 99, 99, 99, 99, 99])
# arr returns
array([99, 99, 99, 99, 99, 99,  6,  7,  8,  9, 10])

如上所示,您无法直接更改slice_of_arr的值,因为它是arr的视图,而不是新变量。

我的问题是:

  1. 为什么NumPy会这样设计?每次需要.copy再分配值时,这不是很乏味吗?
  2. 我能做些什么来摆脱.copy?如何更改NumPy的默认行为?

2 个答案:

答案 0 :(得分:2)

我认为您在其他评论中也有答案,但更具体地说:

1.a。为什么NumPy这样设计?
因为创建视图而不是创建整个数组(线性时间)要更快(固定时间)。

1.b。每次需要复制然后分配值时,都不会很乏味吗?
实际上,创建副本并不常见。所以不,这并不乏味。即使起初可能会感到惊讶,但这种设计还是很好的。

2.a。我能做些什么来摆脱.copy的问题?
如果不使用实际代码,我无法真正分辨。在给出的玩具示例中,无法避免创建副本,但是在实际代码中,通常将函数应用于数据,这些函数会返回另一个数组,因此不需要副本。
您能否举一个需要重复调​​用.copy的真实代码示例?

2.b。如何更改NumPy的默认行为?
你不能尝试习惯它,您会发现它功能强大。

答案 1 :(得分:1)

What does (numpy) __array_wrap__ do?

讨论ndarray子类和__array_wrap__之类的钩子。 np.array获取copy参数,强制结果为副本,即使其他注意事项不需要它也是如此。 ravel返回一个视图,flatten一个副本。因此,构建强制复制的ndarray子类可能也许并不太困难。它可能涉及修改像__array_wrap__这样的钩子。

或者可能修改.__getitem__方法。与slice_of_arr = arr[0:6]中的索引相关的是对__getitem__的调用。对于ndarray,这是编译的,但是对于一个屏蔽数组,它可以用作示例的python代码:

/usr/lib/python3/dist-packages/numpy/ma/core.py

可能就像

一样简单
def __getitem__(self, indx):
    """x.__getitem__(y) <==> x[y]
    """
    # _data = ndarray.view(self, ndarray) # change to:
    _data = ndarray.copy(self, ndarray)
    dout = ndarray.__getitem__(_data, indx)
    return dout

但我怀疑,当你开发并完全测试这样一个子类时,你可能会爱上默认的无副本方法。虽然这个视图复制业务引起了许多新人的注意(特别是如果来自MATLAB),我还没有看到有经验的用户提出的投诉。看看其他numpy SO问题;你不会看到很多copy()个电话。

即使是普通的Python用户也会问自己,引用或切片是否是副本,以及某些内容是否可变。

例如列表:

In [754]: ll=[1,2,[3,4,5],6]
In [755]: llslice=ll[1:-1]
In [756]: llslice[1][1:2]=[10,11,12]
In [757]: ll
Out[757]: [1, 2, [3, 10, 11, 12, 5], 6]

修改切片内的项目会修改原始列表中的相同项目。与numpy相反,列表切片是副本。但这是一个浅薄的副本。您必须付出额外的努力来制作深层副本(import copy)。

/usr/lib/python3/dist-packages/numpy/lib/index_tricks.py包含一些索引函数,旨在使某些索引操作更方便。实际上有几个是使用自定义__getitem__方法的类或类实例。它们也可以作为如何自定义切片和索引的模型。