在NumPy数组上使用+ =时出现意外结果

时间:2014-10-09 12:17:50

标签: python arrays numpy

我使用标准方法使用NumPy在Python中创建对称矩阵/数组:

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

行...

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数组的疯狂事情?

3 个答案:

答案 0 :(得分:24)

transpose operation返回数组的视图,这意味着没有分配新数组。反过来,这意味着您正在同时读取和修改数组。很难说为什么某些大小或结果的某些区域可以工作,但很可能它与numpy如何处理数组加法(可能是副本的子矩阵)和/或数组视图(可能是为了创建的小尺寸)有关一个新阵列。)

x = x + x.T操作有效,因为您正在创建新数组,然后分配给x

答案 1 :(得分: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

这对于正确性来说当然是一件危险的事情,因为它确实是一个可以改变的实现细节。

答案 2 :(得分:4)

问题是添加不是“立即”; x.Tx视图,因此当您开始添加x的每个元素时,x.T会发生变异。这会混淆以后的添加。

它适用于(91, 91)以下尺寸的事实几乎肯定是一个实现细节。使用

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

解决了这个问题,但是你没有任何空间利益。