在形状和1,之间,为什么我可以执行常规操作而不是就地执行操作?

问题描述

当我尝试从形状(1,)到形状()进行现场广播时,numpy会引发ValueError: non-broadcastable output operand with shape () doesn't match the broadcast shape (1,)。我understand认为 inplace运算符旨在与输入在相同的内存块上进行操作,这就是为什么您不能从形状(5,)就地广播的原因变成形状(1,)。但是,对于形状(1,)(或大小为1的任何数组),其大小与形状为()的标量相同。那么为什么我不能执行就位操作a += b,其中a的形状为()b的形状为(1,)?相反的起作用

代码

a = np.array(0)
b = np.array([0])
a + b  # valid
b += a  # valid
a += b  # ValueError

结果:

ValueError: non-broadcastable output operand with shape () doesn't match the broadcast shape (1,)

解决方法

虽然我们可以从测试中获得一些想法,但它可能取决于实现细节(在编译后的代码中)。

In [81]: a = np.array(0); b = np.array([0])
In [82]: a,b
Out[82]: (array(0),array([0]))

两者之和产生一个(1,)数组。这与广播规则一致。 a广播到(1,),然后求和。

In [83]: a+b
Out[83]: array([0])

我们可以添加()或标量:

In [84]: a += a

但是您的错误情况显然是试图将这个(1,)总和放入()目标:

In [85]: a += b
Traceback (most recent call last):
  File "<ipython-input-85-294cacd62d6f>",line 1,in <module>
    a += b
ValueError: non-broadcastable output operand with shape () doesn't match the broadcast shape (1,)

可以使用正确的索引为a分配一个值:

In [86]: a[:] = 1
Traceback (most recent call last):
  File "<ipython-input-86-aa15caba710a>",in <module>
    a[:] = 1
IndexError: too many indices for array: array is 0-dimensional,but 1 were indexed

In [87]: a[()] =2
In [88]: a
Out[88]: array(2)
In [89]: a[...] = a+b
In [90]: a
Out[90]: array(2)

但是显然+=这种分配确实使用了这种更通用的分配方法。

我们也不能+=将(1,1)变成(1,):

In [92]: b += np.array([[1]])
Traceback (most recent call last):
  File "<ipython-input-92-15b525996e5d>",in <module>
    b += np.array([[1]])
ValueError: non-broadcastable output operand with shape (1,) doesn't match the broadcast shape (1,1)

显然,+=类型的分配无法减少数字维度。

作为旁注,可以将(1,)数组广播为(0,)

In [100]: c = np.zeros((0,))
In [101]: c += b
In [102]: c
Out[102]: array([],dtype=float64)

也就是说,在第二个广播步骤中(在匹配尺寸数之后),可以将尺寸1的尺寸减小为0。但这与将尺寸数从n+1更改为n不同。