【问题标题】:Why i cannot replace a column in NumPy array by a single-column matrix?为什么我不能用单列矩阵替换 NumPy 数组中的列?
【发布时间】:2021-02-06 16:16:10
【问题描述】:

我遇到了 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.]])

现在让我们为行/列分配一些值:

>>> 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 数组的实际原因?

【问题讨论】:

  • broadcasting的规则你了解多少?
  • 不是真的,因为我现在似乎发现了这个......

标签: python arrays numpy matrix linear-algebra


【解决方案1】:

你需要广播,使用:

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

【讨论】:

  • 谢谢,我已经尝试过这种方法,但对我来说用切片对象来处理单个列似乎很不直观(简单地说:我只是不想用两个数字来处理单个项目) .
  • @JanMelichařík 现在是一个数字。
  • 我明白了,这看起来更好。谢谢!但是我仍然有点困惑为什么它适用于 1x4 阵列。我需要阅读一些关于广播的内容。
  • 广播时,numpy 尝试对齐最右边的维度。所以(4,)(1, 4) 可以,而(4,)(4, 1) 不行。
【解决方案2】:

作为已接受答案的附录:

制作 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]])

根据广播规则,两个数组必须具有相同的维数。它可以自动添加前导维度,将(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]])

并且根据第二条规则,尺寸 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)。

【讨论】:

  • 这是非常有帮助的解释,非常感谢!我没有意识到我可以这样处理尺寸。现在对我来说更有意义了。您认为替换矩阵中大量列的最佳方法是什么(蒙特卡洛算法)?截至目前,我在大部分脚本中都使用A[:, [3]]
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-04-30
  • 1970-01-01
  • 2022-01-16
  • 1970-01-01
  • 2017-04-11
  • 2013-06-08
  • 1970-01-01
相关资源
最近更新 更多