Python 努比:为什么不';t';a+;=a、 T';工作

Python 努比:为什么不';t';a+;=a、 T';工作,python,numpy,Python,Numpy,正如scipy课堂讲稿中所述,这将不会像预期的那样起作用: a = np.random.randint(0, 10, (1000, 1000)) a += a.T assert np.allclose(a, a.T) 但是为什么呢?作为视图如何影响这种行为 断言np.allclose(a,a.T) 我现在才明白,通过将a和它的transponsea.T求和生成对称矩阵,可以生成对称矩阵) 这使我们正确地期望np.allclose(a,a.T)返回true(得到的矩阵是对称的,所以它应该等于它的

正如scipy课堂讲稿中所述,这将不会像预期的那样起作用:

a = np.random.randint(0, 10, (1000, 1000))
a += a.T
assert np.allclose(a, a.T)
但是为什么呢?作为视图如何影响这种行为

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

我现在才明白,通过将
a
和它的transponse
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(我使用的是python2.7和numpy1.11.0,所以至少有两个环境可以产生这种值)


这个问题是由于numpy的内部设计造成的。 它基本上可以归结为inplace操作符将在运行时更改值,然后这些更改的值将在您实际打算使用原始值的地方使用

这在中进行了讨论,但似乎无法解决

它之所以适用于较小大小的阵列,似乎是因为在处理数据时如何缓冲数据

要准确理解为什么会出现这个问题,恐怕你必须深入了解numpy的内部

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]来计算[0999],但[999,0]已经有了一个[999,0]+a[0999]的和——因此,在主对角线下方,您可以添加两次值。

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

>>> 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的结果。换句话说,应用于左下角元素的加法是加法后数组的右上角元素。您会注意到,第一行下面的所有其他元素也是如此


我认为这样做是因为您不需要制作阵列的副本。(不过如果阵列很小,它看起来会复制。)

作为视图并不影响结果
b=a.T+0
(因此
b
不是a的视图,而是副本)仍然导致
np。allclose(a,b)
False
。我没有
python
会话来测试这一点,但我怀疑这是一个缓冲问题。在一个小数组中会发生什么,在这个数组中可以检查实际值?我认为OP是疯狂的,然后我运行了一些示例。在我的系统上,上述功能适用于
n
多达90个左右。那么它不会,除非你改为
a=a+a.t
。然后它(如预期的)工作到任何
n
a
不是任何矩阵。它是对称的,因为它是一个方阵及其转置的和。另外,当您的示例适用于
n=4
时,请尝试将其设置为
n=1000
,并查看OP实际上有一个点。但是,是的,I a+=a.T将导致对称性,但如果您完全按照OP描述的那样做,则不是这样。即:
a=np.random.randint(0,10,size=(10001000));a+=a.T;print(np.allclose(a,a.T)
。这是Python3.5和Numpy1.10.4的例子。你的例子没有使用就地加法,这让事情变得很奇怪。
a=a+a.T
足以避免我的答案中所显示的问题/bug,
a+=np.array(a.T)
a+=np.copy(a.T)
似乎没有必要。这取决于实现。在选择正确的实现之前,需要仔细检查文档。我建议的方法是一种安全的方法——它将始终有效。我非常确定
a=a+a.t
应该始终有效,因为它正在创建一个新数组,并且根本不使用就地添加。我主要关注d解释它是如何工作的。从技术上讲,使用numpy powers解决这个问题的所有选项都差不多,它们都会创建一个额外的1000x1000数组。是的,你是对的,a=…在Python中应该总是做正确的事情,因为它必须创建额外的数组。这就是它的实现方式。但一般来说,人们不能相信编程语言这就是为什么我说它是依赖于实现的。GitHub问题链接+1!并且同意GH问题op,某种形式的警告会是更好的用户体验,因为我们从他的示例中知道问题是在加法级别,而不是比较级别(就像这个SO问题如何显示问题)
>>> 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]])