2014-10-09 112 views
24

我使用Python創建對稱矩陣/陣列,NumPy的,利用標準方法意外的結果:與+ =上與NumPy陣列

x = rand(500,500) 
x = (x+x.T) 
all(x==x.T) 
> True 

現在,讓我們變得聰明:

x = rand(500,500) 
x += x.T 
all(x==x.T) 
> False 

等待什麼?

x==x.T 
> array([[ True, True, True, ..., False, False, False], 
     [ True, True, True, ..., False, False, False], 
     [ True, True, True, ..., False, False, False], 
     ..., 
     [False, False, False, ..., True, True, True], 
     [False, False, False, ..., True, True, True], 
     [False, False, False, ..., True, True, True]], dtype=bool) 

左上角和右下角段是對稱的。如果我選擇更小的陣列呢?

x = rand(50,50) 
x += x.T 
all(x==x.T) 
> True 

OK ....

x = rand(90,90) 
x += x.T 
all(x==x.T) 
> True 

x = rand(91,91) 
x += x.T 
all(x==x.T) 
> False 

而只是要確定...

x = rand(91,91) 
x = (x+x.T) 
all(x==x.T) 
> True 

這是一個錯誤,還是我要學習的東西瘋狂+=和NumPy數組?

+13

這個問題需要正確的標題。 – plaes 2014-10-09 12:18:33

+0

@AndrewJaffe這是Python 3.4.1上的Numpy 1.9,分佈在Anaconda。 – jeffalstott 2014-10-09 12:34:16

+0

@jeffalstott是的,我誤解了這個問題 - 我也看到了這種行爲。 – 2014-10-09 12:37:47

回答

24

transpose operation返回數組的視圖,這意味着沒有新的數組被分配。這反過來又意味着你正在讀取和修改數組。很難說出爲什麼某些尺寸或結果的某些區域有效,但最有可能與numpy如何處理陣列添加(可能會創建子矩陣的副本)和/或陣列視圖有關(可能適用於它創建的小尺寸一個新的數組)。

x = x + x.T操作工作,因爲那裏你正在創建一個新的數組,然後分配給x,當然。

4

問題是添加不是「立即」完成的; x.T查看x因此,當您開始添加到x的每個元素時,x.T都發生了變異。這會擾亂後來的添加。

它適用於大小低於(91, 91)的事實幾乎肯定是一個實現細節。使用

x = numpy.random.rand(1000, 1000) 
x += x.T.copy() 
numpy.all(x==x.T) 
#>>> True 

解決了這個問題,但是這樣你就沒有任何空間利益。

20

其他人提到的實現細節叫做緩衝。您可以在the docs on array iteration中閱讀更多信息。

如果你看看你的失敗例子更詳細一點:

>>> a = np.random.rand(91, 91) 
>>> a += a.T 
>>> a[:5, -1] 
array([ 0.83818399, 1.06489316, 1.23675312, 0.00379798, 1.08967428]) 
>>> a[-1, :5] 
array([ 0.83818399, 1.06489316, 1.75091827, 0.00416305, 1.76315071]) 

所以第一個錯誤的值是90*91 + 2 = 8192,其中,勿庸置疑,就是我們從得到:

>>> np.getbufsize() 
8192 

而且我們也可以把它設得更高,然後:

>>> np.setbufsize(16384) # Must be a multiple of 16 
8192 # returns the previous buffer size 
>>> a = np.random.rand(91, 91) 
>>> a += a.T 
>>> np.all(a == a.T) 
True 

雖然現在:

>>> a = np.random.rand(129, 129) 
>>> a += a.T 
>>> np.all(a == a.T) 
False 

這當然是一個危險的事情,依靠正確性,因爲它確實是一個實現細節可能會改變。