4

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

关于复制 numpy 数组的 Scipy wiki 页面

我偶然发现了以下行为,这对我来说是出乎意料的:

情况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

令我惊讶的部分是 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
4

3 回答 3

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运算符调用which 重要的__iadd__是首先尝试就地修改数组,因此它将更新 b仍然是a. 而b=b+x操作员调用__add__which 创建新的临时数据,然后其分配给b.

对于a[i] +=b序列是(在 numpy 中):

a.__setitem__(i, a.__getitem__(i).__iadd__(b))
于 2013-08-09T21:42:07.390 回答
2

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

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

在这种情况下,您不会期望对 进行更改a,因为这不是就地添加。运算符是+,而不是+=。与第二个示例相同。

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

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

于 2013-08-09T21:52:35.250 回答
1

切片 numpy 数组时的默认操作是将 b 创建为 a 的视图,因此当您更改 b 时,a 也会更改,这已在您的第一个案例中得到证实。

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

于 2013-08-09T21:43:37.113 回答