numpy数组切片的意外结果(view vs copy)

时间:2013-08-09 21:32:05

标签: python arrays numpy scipy slice

我正在尝试减少代码中的复制量,并且在处理numpy数组切片和视图时遇到了令人惊讶的行为,如下所述:

Scipy wiki page on copying numpy arrays

我偶然发现了以下行为,这对我来说意外:

案例1。:

import numpy as np
a = np.ones((3,3))
b = a[:,1:2]
b += 5
print a
print b.base is a

正如预期的那样,输出:

array([[ 1.,  6.,  1.],
       [ 1.,  6.,  1.],
       [ 1.,  6.,  1.]])
True

案例2 :在一行中执行切片和添加时,情况会有所不同:

import numpy as np
a = np.ones((3,3))
b = a[:,1:2] + 5
print a
print b.base is a

对我来说令人惊讶的是,[:,1:2]似乎没有创建一个视图,然后将其用作左侧参数,因此,输出:

array([[ 1.,  1.,  1.],
       [ 1.,  1.,  1.],
       [ 1.,  1.,  1.]])
False

也许有人可以解释为什么这两种情况不同,我想我错过了什么。

解决方案:我错过了一个显而易见的事实,即“+”运算符,除了就地运算符“+ =”将始终创建一个副本,所以它实际上没有相关但切片其他如何为numpy数组定义就地运算符。

为了说明这一点,以下内容生成与案例2相同的输出:

import numpy as np
a = np.ones((3,3))
b = a[:,1:2]
b = b + 5
print a
print b.base is a

3 个答案:

答案 0 :(得分:3)

上述情况与以下情况没有什么不同:

>>> a=np.arange(5)
>>> b=a
>>> b
array([0, 1, 2, 3, 4])

>>> b+=5
>>> a
array([5, 6, 7, 8, 9])
>>> b
array([5, 6, 7, 8, 9])

>>> b=b+5
>>> b
array([10, 11, 12, 13, 14])
>>> a
array([5, 6, 7, 8, 9])

至少在我看来,这似乎是完全预期的行为。 b+=x运算符调用__iadd__,其中重要的是首先尝试修改数组,因此更新 b仍然是{{1}的视图}。 a运算符调用b=b+x创建新临时数据,然后分配给__add__

对于b,序列是(在numpy中):

a[i] +=b

答案 1 :(得分:2)

a[:, 1:2]会创建一个视图,但您不会在第二个示例中修改视图。相反,+从其参数创建一个新数组。假设你做了

a = np.ones((3, 3))
b = a + 5

在这种情况下,您不会期望更改为a,因为这不是就地添加。运算符为+,而不是+=。第二个例子也是如此。

b = a[:, 1:2] + 5

不会修改a[:, 1:2],因为这不是就地添加。

答案 2 :(得分:1)

切片numpy数组时要做的默认操作是创建b作为a的视图,因此当你更改b时,也会发生更改,这是由你的第一个案例确认的。

第二种情况比较棘手。你不是说b是a的一部分,然后添加一个数字。你正在做的是将b创建为与a不一致的东西,因此numpy被迫复制数据而不是仅创建视图。