一个numpy数组的视图视图是一个副本?

acd*_*cdr 3 python numpy

如果更改numpy数组的视图,则原始数组也会更改。这是预期的行为。

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

但是,如果我对这种视图进行更改,然后更改,则原始数组不会更改:

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])
Run Code Online (Sandbox Code Playgroud)

对我而言,这意味着,当您查看某个视图时,您实际上会获得一份副本。它是否正确?为什么是这样?

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

unu*_*tbu 6

通过基本切片进行选择总是返回一个视图。通过高级索引进行选择始终会返回一个副本。 通过布尔掩码进行选择是高级索引的一种形式。(高级索引的另一种形式是通过整数数组进行选择。)

但是,通过高级索引分配会影响原始数组。

所以

mask = np.array([True, False, False])
arr[mask] = 0
Run Code Online (Sandbox Code Playgroud)

影响,arr因为它是一项任务。相反,

mask_1_arr = arr[mask_1]
Run Code Online (Sandbox Code Playgroud)

是通过布尔掩码选择的,所以mask_1_arr是的一部分的副本arr。拥有副本后,夹具就到了。Python执行时

mask_2 = np.array([True])
mask_1_arr[mask_2] = 0
Run Code Online (Sandbox Code Playgroud)

分配会影响mask_1_arr,但由于mask_1_arr是副本,因此对无效arr


|            | basic slicing    | advanced indexing |
|------------+------------------+-------------------|
| selection  | view             | copy              |
| assignment | affects original | affects original  |
Run Code Online (Sandbox Code Playgroud)

在后台,arr[mask] = something使Python调用 arr.__setitem__(mask, something)ndarray.__setitem__实现该方法进行修改arr。毕竟,这是人们应该__setitem__做的自然的事情 。

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

  • 如果“ 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`。 (2认同)