2016-01-27 112 views
88

我最近應用了this解決方案來平均每N行矩陣。 雖然解決方案一般在應用於7x1陣列時遇到了問題。我注意到問題在於使用-=操作符時。 要使一個小例子:Python中a - = b和a = a - b的區別

import numpy as np 

a = np.array([1,2,3]) 
b = np.copy(a) 

a[1:] -= a[:-1] 
b[1:] = b[1:] - b[:-1] 

print a 
print b 

,其輸出:

[1 1 2] 
[1 1 1] 

所以,在陣列a -= b的情況下產生不同的結果比a = a - b。我直到現在纔想到這兩種方式是完全一樣的。有什麼不同?

爲什麼我提到的方法是對矩陣中的每N行進行求和,對於7x4矩陣而不是7x1陣列?

回答

79

注:在使用上,在不再是一個問題,在1.13.0版本共享內存以後與NumPy陣列就地操作(見詳情here)。這兩個操作將產生相同的結果。這個答案只適用於早期版本的NumPy。


,當他們在計算中使用可能會導致意想不到的結果變異陣列!

在的問題的例子中,減法以-=修改的a第二元件,然後立即使用該改性秒元件在操作的a第三元件上。

以下是通過與步驟一步a[1:] -= a[:-1]發生:

  • a與數據[1, 2, 3]陣列。

  • 我們對該數據有兩種看法:a[1:][2, 3]a[:-1][1, 2]

  • 就地減法-=開始。從a[1:]的第一個元素中減去a[:-1],1的第一個元素。這已修改a[1, 1, 3]。現在我們有a[1:]是數據[1, 3]的視圖,而a[:-1]是數據[1, 1](數組a的第二個元素已被更改)的視圖。

  • a[:-1]現在是[1, 1]和NumPy的現在必須從a[1:]第二元件中減去其第二元件爲1(未2了!)。這使a[1:]成爲值[1, 2]的視圖。

  • a現在是一個值爲[1, 1, 2]的數組。

b[1:] = b[1:] - b[:-1]因爲b[1:] - b[:-1]首先創建一個新陣列,然後將此數組b[1:]在分配的值不會有這樣的問題。在減法期間它不會自行修改b,因此視圖b[1:]b[:-1]不會更改。


一般的建議是避免修改一個視圖與另一個視圖,如果它們重疊。這包括運營商-=,*=等,並在通用功能(如np.subtractnp.multiply)中使用out參數回寫到其中一個陣列。

+4

我更喜歡這個答覆更多目前接受的。它使用非常清晰的語言來顯示修改可變對象的效果。更重要的是,最後一段直接強調重疊意見就地修改的重要性,這應該成爲從這個問題中帶回家的教訓。 – Reti43

42

內部,所不同的是這樣的:

a[1:] -= a[:-1] 

是相同的:

a[1:] = a[1:].__isub__(a[:-1]) 
a.__setitem__(slice(1, None, None), a.__getitem__(slice(1, None, None)).__isub__(a.__getitem__(slice(1, None, None))) 

,而這一點:

b[1:] = b[1:] - b[:-1] 

映射到這一點:

b[1:] = b[1:].__sub__(b[:-1]) 
b.__setitem__(slice(1, None, None), b.__getitem__(slice(1, None, None)).__sub__(b.__getitem__(slice(1, None, None))) 

在某些情況下,__sub__()__isub__()以類似的方式工作。但是在使用__isub__()時,可變對象應該進行變異並返回自己,而它們應該返回一個新對象__sub__()

對numpy對象應用切片操作會在它們上創建視圖,因此直接使用它們可以訪問「原始」對象的內存。

11

The docs說:

在Python增強分配背後的想法是,它不只是 寫存儲在其左側的二進制運算的 結果的普遍做法更簡單的方法操作數,而且還有一個 的方法可以讓左手操作數知道它應該自己操作,而不是創建 本身的修改副本。

作爲經驗法則,增強減法(x-=y)是x.__isub__(y),爲IN -place操作IF可能的,當正常減法(x = x-y)是x=x.__sub__(y)。在像整數這樣的非可變對象上它是等價的。但對於像數組或列表這樣的可變元素,它們可能是完全不同的東西。

相關問題