2016-08-04 74 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