NumPy 2D和1D加法扁平化

4

在开始使用NumPy时,参考《NumPy Book》中的示例时,我注意到了一个例子:

a = zeros((4, 5))
b = ones(6)
add(b, b, a[1:3, 0:3].flat)
print(a)

返回值

array([[0, 0, 0, 0, 0]
       [2, 2, 2, 0, 0]
       [2, 2, 2, 0, 0]
       [0, 0, 0, 0, 0]])

然而,当我尝试运行这段代码时,会出现以下错误:

add(b, b, a[1:3, 0:3].flat)

TypeError: 返回的数组必须是ArrayType类型"

请问有谁能解决这个问题吗?

1个回答

3
如果您给numpy.add提供了两个参数,它们将被视为要进行加法的两个操作数。如果您提供了三个参数,则前两个参数将被添加在一起,并且第三个参数是结果所在的数组。实际上不是结果,而是应该保存结果的数组。
所以您想把bb相加,并将其存储在a[1:3, 0:3].flat中。
让我们尝试一下np.add(b, b),得到以下结果:
import numpy as np
a = np.zeros((4, 5))
b = np.ones(6)
np.add(b, b)
# returns array([ 2.,  2.,  2.,  2.,  2.,  2.])

所以现在我尝试使用 a[1:3, 0:3].flat,它返回了 <numpy.flatiter at 0x22204e80c10>。这意味着它返回了一个 iterator,而不是一个数组。但我们不需要一个迭代器,我们需要一个数组。有一个叫做 ravel() 的方法。因此,尝试使用 a[1:3, 0:3].ravel() 返回:

array([ 0.,  0.,  0.,  0.,  0.,  0.])

我们有一个数组。特别地,这个数组也可以用来存储结果(形状相同!)。所以我尝试了:

np.add(b, b, a[1:3, 0:3].ravel())
# array([ 2.,  2.,  2.,  2.,  2.,  2.])

但是让我们看看a是否已经改变:

a
#array([[ 0.,  0.,  0.,  0.,  0.],
#       [ 0.,  0.,  0.,  0.,  0.],
#       [ 0.,  0.,  0.,  0.,  0.],
#       [ 0.,  0.,  0.,  0.,  0.]])

所以a没有改变。这是因为如果可能的话,ravel()仅返回视图(赋值将传播到展开的数组),否则它将返回副本。在副本中保存结果相当无意义,因为out参数的整个目的是在原地完成操作。我只是猜测为什么会制作副本,但我认为这是因为我们从更大的数组中取出一部分,其中该部分在内存中不连续。

因此,在这种情况下,我建议您不使用out参数,而是使用np.add的返回值,并将其存储在a中指定的区域内:

a[1:3, 0:3] = np.add(b, b).reshape(2,3) # You need to reshape here!
a
#array([[ 0.,  0.,  0.,  0.,  0.],
#       [ 2.,  2.,  2.,  0.,  0.],
#       [ 2.,  2.,  2.,  0.,  0.],
#       [ 0.,  0.,  0.,  0.,  0.]])

同时,a[1:3, 0:3].flat = np.add(b, b)也是可行的。

我认为这本书可能过时了,并且它适用于旧版本的numpy,或者它从来没有起作用,只是书中的错误。


1
Python版本-3.5,NumPy-1.10.4。我所参考的书是Travis E. Oliphant于2006年编写的《NumPy指南》。可能是由于Python版本的冲突导致的。无论如何,非常感谢您提供如此详细的答案。非常有帮助! - Psyched
1
感谢您的反馈,这正是我使用的版本,并且我相信这是一个 numpy 的 bug(或者是在 2006 年至今被弃用的某些东西),因为 a[1:3, 0:3].flat = np.add(b, b) 也可以工作。而且这应该等同于您的 np.add(b, b, a[1:3, 0:3].flat) - MSeifert
2
a[...].flat=np.add(b,b) 是可行的。flat文档显示了这种赋值的用法。显然,某些更改已禁用其作为ufunc out参数的使用。 - hpaulj
1
是的,我也注意到了,并在答案中包含了它。我还在numpy上提出了一个问题:https://github.com/numpy/numpy/issues/7244。也许他们知道为什么它不起作用。 - MSeifert
1
感谢@MSeifert。通过分配np.add(b,b)替换数组值是有效的,但它仅替换数组内部的值,而不实际添加其中的值。可以将值引用到另一个数组中,例如“k = a [1:3,0:3] .flat; a [1:3,0:3] .flat = add(b * 2,k)”,但如果模块可以“修复”就更好了。 - Psyched
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接