2016-08-04 81 views
2

如果更改了numpy数组视图,原始数组也会被更改。这是预期的行为。numpy数组视图视图是副本?

arr = np.array([1,2,3]) 
mask = np.array([True, False, False]) 
arr[mask] = 0 
arr 
# Out: array([0, 2, 3]) 

但是,如果我采取这样的视点的视点,并更改,那么原来的数组是改变:

arr = np.array([1,2,3]) 
mask_1 = np.array([True, False, False]) 
mask_1_arr = arr[mask_1] # Becomes: array([1]) 
mask_2 = np.array([True]) 
mask_1_arr[mask_2] = 0 
arr 
# Out: array([1, 2, 3]) 

这意味着,我认为,当你把一个一个视图的观点,你实际上得到一份副本。它是否正确?为什么是这样?

如果我使用数字索引的numpy数组而不是布尔值的numpy数组,则会发生相同的行为。 (例如,arr[np.array([0])][np.array([0])] = 0不会将arr的第一个元素更改为0.)

回答

4

通过basic slicing的选择总是返回一个视图。通过advanced indexing的选择总是返回一份副本。 Selection by boolean mask是一种先进的 索引。 (其他形式的高级索引是selection by integer array)。

但是,赋值通过高级索引影响原始数组。

所以

mask = np.array([True, False, False]) 
arr[mask] = 0 

影响arr因为它是一个任务。与此相反,

mask_1_arr = arr[mask_1] 

是选择由布尔掩码,所以mask_1_arr是的arr一部分的副本。 一旦你有一份副本,夹具就起来了。当Python执行

mask_2 = np.array([True]) 
mask_1_arr[mask_2] = 0 

分配影响mask_1_arr,但由于mask_1_arr是一个副本, 这对arr没有影响。


|   | basic slicing | advanced indexing | 
|------------+------------------+-------------------| 
| selection | view    | copy    | 
| assignment | affects original | affects original | 

引擎盖下,arr[mask] = something导致Python来调用 arr.__setitem__(mask, something)ndarray.__setitem__方法是 实施修改arr。毕竟,这是人们应该期望的自然事情 __setitem__要做。

相反,如表达式arr[indexer]导致Python致电 arr.__getitem__(indexer)。当indexer是切片时, 元素的规则允许NumPy返回视图(通过修改步幅和偏移量)。当indexer 是一个任意布尔掩码或任意整数数组时,通常 对所选元素没有规律性,因此无法返回 视图。因此必须返回一份副本。

+0

这一切都有道理!那么我想,有没有简单的方法来做一个像'arr [x] [y] = 1'这样的单线程。现在我通过分配一个中间值来完成这个工作,例如'int = arr [x]'; 'int [y] = 1'; 'arr [x] = int'。 – acdr

+1

如果'x'是一个布尔掩码,则一行等价物将是'np.put(arr,np.flatnonzero(x)[y],1)'。 'np.flatnonzero(x)'将布尔掩码转换为一维整数数组。然后,您可以使用'np.flatnonzero(x)[y]'选择这些整数的一些子集,其中'y'可以是基本切片或高级索引器。然后'np.put(arr,np.flatnonzero(x)[y],1)'因为它大致等于'arr.flat [np.flatnonzero(x)[y]] = 1'。 – unutbu

+0

如果'arr'是一维的,'arr [np.flatnonzero(x)[y]] = 1'也适用。上面的'np.put'的目的是提供一个即使'arr'是n维也可以工作的答案。 – unutbu