为什么我不能用单列矩阵替换 NumPy 数组中的列?

Why i cannot replace a column in NumPy array by a single-column matrix?

我遇到了 NumPy 数组的奇怪行为。我正在处理一些矩阵代数示例,我发现您可以轻松地将数组列(或一行)替换为各种类型的数据,但不能替换为具有正确行数和单列数的数组。

让我们有一个数组:

>>> import numpy as np
>>>
>>> A = np.zeros([4,4])
>>> A
array([[0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.]])

现在让我们给rows/columns赋一些值:

>>> A[0,:] = [1,1,1,1]
>>> A
array([[1., 1., 1., 1.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.]])
>>>
>>> A[:,0] = np.array([2,2,2,2])
>>> A
array([[2., 1., 1., 1.],
       [2., 0., 0., 0.],
       [2., 0., 0., 0.],
       [2., 0., 0., 0.]])
>>>
>>> A[2,:] = np.array([[3,3,3,3]])
>>> A
array([[2., 1., 1., 1.],
       [2., 0., 0., 0.],
       [3., 3., 3., 3.],
       [2., 0., 0., 0.]])
>>>
>>> A[:,2] = np.array([[4,4,4,4]])
>>> A
array([[2., 1., 4., 1.],
       [2., 0., 4., 0.],
       [3., 3., 4., 3.],
       [2., 0., 4., 0.]])

在最后两个替换中,我分配了一个 1x4 数组作为新行,并分配了一个 1x4 数组作为新列。 但是由于我不知道的原因,我无法将 4x1 数组分配给现有数组的列或行:

>>> A[:,3] = np.array([[5],[5],[5],[5]])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: could not broadcast input array from shape (4,1) into shape (4,)

我用谷歌搜索了这个错误,我仍然认为这可能是一个设计缺陷。

是否有实际原因不允许将 Nx1 数组作为列分配给 NxM 数组?

需要广播,使用:

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

>>> A[:,3:4] = np.array([[5],[5],[5],[5]])
>>> A
array([[0., 0., 0., 5.],
       [0., 0., 0., 5.],
       [0., 0., 0., 5.],
       [0., 0., 0., 5.]])

A[:, 3] 的形状是 (n,),即 1-D array,而 A[:, 3:4] 的形状是 (n, 1),这与你的数组相同正在尝试分配。

>>> A[:, 3]
array([0., 0., 0., 0.])
>>> A[:, 3].shape
(4, )

>>> A[:, 3:4]
array([[0.],
       [0.],
       [0.],
       [0.]])

>>> A[:, 3:4].shape
(4, 1)

不同的是,3只是一个整数,而不是一个索引,3:4是一个slice对象,slice(3, 4, None),你可以这样想例如,您正在沿轴 1 切割一部分,而不是只选择一个索引。

编辑:

或者,您可以在列表中沿轴 1 传递索引:

>>> A[:, [3]] = np.array([[5],[5],[5],[5]])
>>> A
array([[0., 0., 0., 5.],
       [0., 0., 0., 5.],
       [0., 0., 0., 5.],
       [0., 0., 0., 5.]])

要查看更多信息,请查看 Broadcasting Rules

作为已接受答案的附录:

创建 2 个数组,一个 1d,另一个 2d(但元素数量相同):

In [73]: x = np.ones((4,),int); y = np.ones((4,1),int)*10
In [74]: x
Out[74]: array([1, 1, 1, 1])
In [75]: y
Out[75]: 
array([[10],
       [10],
       [10],
       [10]])

当我们添加它们时,我们得到 (4,4) 结果:

In [76]: x+y
Out[76]: 
array([[11, 11, 11, 11],
       [11, 11, 11, 11],
       [11, 11, 11, 11],
       [11, 11, 11, 11]])

根据广播规则,2 个数组的维数必须相同。它可以自动添加前导尺寸,将 (4,) 变为 (1,4):

In [77]: x[None,:]+y
Out[77]: 
array([[11, 11, 11, 11],
       [11, 11, 11, 11],
       [11, 11, 11, 11],
       [11, 11, 11, 11]])

根据第 2 条规则,尺寸 1 的尺寸被调整,所以 (1,4)=>(4,4) 和 (4,1)=>(4,4),结果是 (4, 4) 数组。

通过分配,RHS 可以添加前导尺寸并调整 1。但是LHS基本没变:

In [78]: x[:] = y
Traceback (most recent call last):
  File "<ipython-input-78-4f264a106454>", line 1, in <module>
    x[:] = y
ValueError: could not broadcast input array from shape (4,1) into shape (4)

将 RHS 更改为 (1,4) 有效:

In [79]: x[:] = y.T
In [80]: x
Out[80]: array([10, 10, 10, 10])

或将其更改为 (4,):

In [81]: x[:] = y.ravel()

或将 LHS 更改为 (4,1):

In [83]: x[:,None] = y

已接受的答案最后有效地做到了这一点 A[:, [3]]

我本来想说 LHS 不能更改,但是将 (1,4) 分配给 (4,) 的能力让我犹豫了。将 (1,4) 分配给 (4,) 时,是将 LHS 调整为 (1,4)(自动引导尺寸),还是将 RHS (1,4) 更改为 (4,)。细节可能并不重要。关键是不能添加(或删除)尾随维度。

需要前导尺寸和尾随尺寸之间的差异以避免歧义。想象一下尝试添加 (3,) 和 (4,)。这会导致 (3,4) 或 (4,3) 吗? (3,1)+(1,4) 或 (1,3)+(4,1)。使用领先规则,您必须明确,添加 (3,1)+(4,) 或 (3,)+(4,1).