我试图减少代码中的复制量,但在处理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
让我感到惊讶的是,[:,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条答案
按热度按时间smtd7mpg1#
上述情况无异于:
至少在我看来,这是完全意料之中的行为。
b+=x
操作符调用__iadd__
,它首先尝试在适当的位置修改数组,所以它将 * 更新 *b
,它仍然是a
的视图。而b=b+x
操作符调用__add__
,它创建新的临时数据,然后 * 分配 * 给b
。对于
a[i] +=b
,序列为(单位为numpy):9njqaruj2#
a[:, 1:2]
创建了一个视图,但在第二个示例中,您没有修改视图,而是从其参数创建了一个新数组。在这种情况下,您不会期望
a
发生变化,因为这不是就地加法。运算符是+
,而不是+=
。第二个示例也是如此。不会修改
a[:, 1:2]
,因为这不是就地加法。fwzugrvs3#
当对numpy数组进行切片时,默认的做法是创建B作为a的视图,因此当你改变b时,a也会改变,这在第一个案例中得到了证实。
第二种情况更复杂,你不是说B是a的一个切片,然后加上一个数字,你所做的是创建b作为与a不一致的东西,所以numpy被迫复制数据,而不仅仅是创建一个视图。