Numpy:为什么没有&= 39; a + = a.T'工作?

时间:2016-04-22 06:58:39

标签: python numpy

如scipy讲义中所述,这不会按预期工作:

a = np.random.randint(0, 10, (1000, 1000))
a += a.T
assert np.allclose(a, a.T)

但为什么呢?视图如何影响此行为?

4 个答案:

答案 0 :(得分:5)

这个问题是由于numpy的内部设计造成的。 它基本上归结为inplace操作符将随之更改值,然后这些更改的值将用于您实际打算使用原始值的位置。

这在this bug report中讨论过,似乎无法修复。

它适用于较小尺寸数组的原因似乎是因为在处理数据时如何缓冲数据。

完全理解问题突然出现的原因,我恐怕你将不得不深入研究numpy的内部。

答案 1 :(得分:5)

a += a.T

进行就地汇总(它在处理时使用视图a.T),因此最终得到一个非对称矩阵 你可以很容易地检查这一点,即我得到了:

In [3]: a
Out[3]: 
array([[ 6, 15,  7, ...,  8,  9,  2],
       [15,  6,  9, ..., 14,  9,  7],
       [ 7,  9,  0, ...,  9,  5,  8],
       ..., 
       [ 8, 23, 15, ...,  6,  4, 10],
       [18, 13,  8, ...,  4,  2,  6],
       [ 3,  9,  9, ..., 16,  8,  4]])

你可以看到它不对称,对吧? (比较右上角和左下角的项目)

如果你做了真实的副本:

a += np.array(a.T)

它工作正常,即:

In [6]: a
Out[6]: 
array([[ 2, 11,  8, ...,  9, 15,  5],
       [11,  4, 14, ..., 10,  3, 13],
       [ 8, 14, 14, ..., 10,  9,  3],
       ..., 
       [ 9, 10, 10, ..., 16,  7,  6],
       [15,  3,  9, ...,  7, 14,  1],
       [ 5, 13,  3, ...,  6,  1,  2]])

为了更好地理解它为什么这样做,你可以想象你自己编写循环如下:

In [8]: for i in xrange(1000):
            for j in xrange(1000):
                a[j,i] += a[i,j]
   ....:         

In [9]: a
Out[9]: 
array([[ 4,  5, 14, ..., 12, 16, 13],
       [ 3,  2, 16, ..., 16,  8,  8],
       [ 9, 12, 10, ...,  7,  7, 23],
       ..., 
       [ 8, 10,  6, ..., 14, 13, 23],
       [10,  4,  6, ...,  9, 16, 21],
       [11,  8, 14, ..., 16, 12, 12]])

它增加了一个[999,0]来计算[0,999],但是[999,0]已经有一个[999,0] + a [0,999]的总和 - 所以低于主对角线你添加值两次。

答案 2 :(得分:1)

  

断言np.allclose(a,a.T)

我现在明白你通过将a与它的转发a.T相加来生成对称矩阵,从而产生对称矩阵

这使得我们理所当然地期望np.allclose(a, a.T)返回true(结果矩阵是对称的,所以它应该等于它的转置)

  

a + = a.T#视图如何影响此行为?

我只是把它缩小到这个

TL; DR a = a + a.T适用于较大的矩阵,而a += a.T从91x91大小开始给出奇怪的结果

>>> a = np.random.randint(0, 10, (1000, 1000))
>>> a += a.T  # using the += operator
>>> np.allclose(a, a.T)
False
>>> a = np.random.randint(0, 10, (1000, 1000))
>>> a = a + a.T # using the + operator
>>> np.allclose(a, a.T)
True

我的大小与90x90相同,如@Hannes(我在Python 2.7和Numpy 1.11.0上,所以至少有两个环境可以产生这个)

>>> a = np.random.randint(0, 10, (90, 90))
>>> a += a.T
>>> np.allclose(a, a.T)
True
>>> a = np.random.randint(0, 10, (91, 91))
>>> a += a.T
>>> np.allclose(a, a.T)
False

答案 3 :(得分:1)

对于大型阵列,就地操作员会使您将添加应用于当前正在操作的操作员。例如:

>>> a = np.random.randint(0, 10, (1000, 1000))
>>> a
array([[9, 4, 2, ..., 7, 0, 6],
       [8, 4, 1, ..., 3, 5, 9],
       [6, 4, 9, ..., 6, 9, 7],
       ..., 
       [6, 2, 5, ..., 0, 4, 6],
       [5, 7, 9, ..., 8, 0, 5],
       [2, 0, 1, ..., 4, 3, 5]])

请注意,右上角和左下角的元素是6和2。

>>> a += a.T
>>> a
array([[18, 12,  8, ..., 13,  5,  8],
       [12,  8,  5, ...,  5, 12,  9],
       [ 8,  5, 18, ..., 11, 18,  8],
       ..., 
       [19,  7, 16, ...,  0, 12, 10],
       [10, 19, 27, ..., 12,  0,  8],
       [10,  9,  9, ..., 14, 11, 10]])

现在注意右上角的元素是正确的(8 = 6 + 2)。但是,左下角的元素不是6 + 2的结果,而是8 + 2的结果。换句话说,应用于左下角元素的加法是数组之后的右上角元素。您会注意到第一行下面的所有其他元素也是如此。

我想这可以这样工作,因为你不需要复制你的数组。 (虽然它看起来像 如果数组很小就会复制。)