Numpy:一次向量化访问多个列?

Numpy: vectorized access of several columns at once?

我有包含多维数组的脚本,我想使用矢量化实现来解决我的问题(有时包含列操作)而不是 for 循环。

让我们考虑一个简单的例子,矩阵 arr:

> arr = np.arange(12).reshape(3, 4)

> arr
> ([[ 0,  1,  2,  3],
    [ 4,  5,  6,  7],
    [ 8,  9, 10, 11]])

> arr.shape
> (3, 4)

所以我们有一个 3 行 4 列的矩阵 arr

我的脚本中最简单的情况是向数组中的值添加一些东西。例如。我正在为单个或多个 :

> someVector = np.array([1, 2, 3, 4])
> arr[0] += someVector

> arr
> array([[ 1,  3,  5,  7],    <--- successfully added someVector
         [ 4,  5,  6,  7],         to one row
         [ 8,  9, 10, 11]])

> arr[0:2] += someVector

> arr
> array([[ 2,  5,  8, 11],    <--- added someVector to two
         [ 5,  7,  9, 11],    <--- rows at once
         [ 8,  9, 10, 11]])

这很好用。然而,有时我需要操作一个或几个。一次一栏有效:

> arr[:, 0] += [1, 2, 3]

> array([[ 3,  5,  8, 11],
         [ 7,  7,  9, 11],
         [11,  9, 10, 11]])
           ^
           |___ added the values [1, 2, 3] successfully to
                this column

但我正在努力思考为什么这 一次多列 不起作用:

> arr[:, 0:2] += [1, 2, 3]

> ValueError
> Traceback (most recent call last)
> <ipython-input-16-5feef53e53af> in <module>()
> ----> 1 arr[:, 0:2] += [1, 2, 3]

> ValueError: operands could not be broadcast
>             together with shapes (3,2) (3,) (3,2)

这与 的工作方式不一样吗?我在这里做错了什么?

要将一维数组添加到多个列中,您需要将值 broadcast 添加到二维数组中。由于默认情况下广播会在(形状的)左侧添加新轴,因此会自动将行向量广播到多行:

arr[0:2] += someVector

someVector 具有形状 (N,) 并自动广播到形状 (1, N)。如果 arr[0:2] 具有 (2, N) 的形状,则按元素执行求和,就好像 arr[0:2]someVector 都是具有相同形状的数组,(2, N).

但是要将一个列向量广播到多个列需要提示 NumPy 您希望广播发生在右侧的轴上。事实上,您必须使用 someVector[:, np.newaxis] 或等效的 someVector[:, None]:

在右侧显式添加新轴
In [41]: arr = np.arange(12).reshape(3, 4)

In [42]: arr[:, 0:2] += np.array([1, 2, 3])[:, None]

In [43]: arr
Out[43]: 
array([[ 1,  2,  2,  3],
       [ 6,  7,  6,  7],
       [11, 12, 10, 11]])

someVector(例如 np.array([1, 2, 3]))的形状为 (N,)someVector[:, None] 的形状为 (N, 1),所以现在广播发生在右边。如果 arr[:, 0:2] 具有 (N, 2) 的形状,则按元素执行求和,就好像 arr[:, 0:2]someVector[:, None] 都是具有相同形状的数组,(N, 2).

@unutbu 的解释很清楚。

作为补充,换位 (.T) 通常可以通过在第一个维度上工作来简化任务:

In [273]: arr = np.arange(12).reshape(3, 4)

In [274]: arr.T[0:2] += [1, 2, 3]

In [275]: arr
Out[275]: 
array([[ 1,  2,  2,  3],
       [ 6,  7,  6,  7],
       [11, 12, 10, 11]])