我正在尝试减少代码中的复制量,并且在处理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
答案 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被迫复制数据而不是仅创建视图。