如何向量化 Python 中的增量
How to vectorize increments in Python
我有一个二维数组,我有一些数字要添加到一些单元格中。我想对操作进行矢量化以节省时间。问题是当我需要将多个数字添加到同一个单元格时。在这种情况下,矢量化代码只添加最后一个。
'a' 是我的数组,'x' 和 'y' 是我要递增的单元格的坐标,'z' 包含我要添加的数字。
import numpy as np
a=np.zeros((4,4))
x=[1,2,1]
y=[0,1,0]
z=[2,3,1]
a[x,y]+=z
print(a)
如您所见,a[1,0] 应该递增两次:一次递增 2,一次递增 1。因此预期的数组应该是:
[[0. 0. 0. 0.]
[3. 0. 0. 0.]
[0. 3. 0. 0.]
[0. 0. 0. 0.]]
但我得到:
[[0. 0. 0. 0.]
[1. 0. 0. 0.]
[0. 3. 0. 0.]
[0. 0. 0. 0.]]
这个问题用 for 循环很容易解决,但我想知道我是否可以正确地向量化这个操作。
当你在做a[x,y]+=z
时,我们可以将操作分解为:
a[1, 0], a[2, 1], a[1, 0] = [a[1, 0] + 2, a[2, 1] + 3, a[1, 0] + 1]
# Equivalent to :
a[1, 0] = 2
a[2, 1] = 3
a[1, 0] = 1
这就是它不起作用的原因。
但是如果你用每个维度的循环递增你的数组,它应该工作
您可以创建一个大小为 3x4x4 的多维数组,然后将 z
添加到所有 3 个不同的维度,然后将它们相加
import numpy as np
x = [1,2,1]
y = [0,1,0]
z = [2,3,1]
a = np.zeros((3,4,4))
n = range(a.shape[0])
a[n,x,y] += z
print(sum(a))
这将导致
[[0. 0. 0. 0.]
[3. 0. 0. 0.]
[0. 3. 0. 0.]
[0. 0. 0. 0.]]
为此使用 np.add.at
:
import numpy as np
a = np.zeros((4,4))
x = [1, 2, 1]
y = [0, 1, 0]
z = [2, 3, 1]
np.add.at(a, (x, y), z)
print(a)
# [[0. 0. 0. 0.]
# [3. 0. 0. 0.]
# [0. 3. 0. 0.]
# [0. 0. 0. 0.]]
方法 #1:基于 Bincount 的性能方法
我们可以使用np.bincount
for efficient bin-based summation and basically inspired by -
def accumulate_arr(x, y, z, out):
# Get output array shape
shp = out.shape
# Get linear indices to be used as IDs with bincount
lidx = np.ravel_multi_index((x,y),shp)
# Or lidx = coords[0]*(coords[1].max()+1) + coords[1]
# Accumulate arr with IDs from lidx
out += np.bincount(lidx,z,minlength=out.size).reshape(out.shape)
return out
如果您使用的是零初始化输出数组,请将输出形状直接输入函数并获得 bincount 输出作为最终输出。
给定样本的输出 -
In [48]: accumulate_arr(x,y,z,a)
Out[48]:
array([[0., 0., 0., 0.],
[3., 0., 0., 0.],
[0., 3., 0., 0.],
[0., 0., 0., 0.]])
方法 #2:使用稀疏矩阵提高内存效率
In [54]: from scipy.sparse import coo_matrix
In [56]: coo_matrix((z,(x,y)), shape=(4,4)).toarray()
Out[56]:
array([[0, 0, 0, 0],
[3, 0, 0, 0],
[0, 3, 0, 0],
[0, 0, 0, 0]])
如果您不介意稀疏矩阵,请跳过 .toarray()
部分以获得内存高效的解决方案。
我有一个二维数组,我有一些数字要添加到一些单元格中。我想对操作进行矢量化以节省时间。问题是当我需要将多个数字添加到同一个单元格时。在这种情况下,矢量化代码只添加最后一个。 'a' 是我的数组,'x' 和 'y' 是我要递增的单元格的坐标,'z' 包含我要添加的数字。
import numpy as np
a=np.zeros((4,4))
x=[1,2,1]
y=[0,1,0]
z=[2,3,1]
a[x,y]+=z
print(a)
如您所见,a[1,0] 应该递增两次:一次递增 2,一次递增 1。因此预期的数组应该是:
[[0. 0. 0. 0.]
[3. 0. 0. 0.]
[0. 3. 0. 0.]
[0. 0. 0. 0.]]
但我得到:
[[0. 0. 0. 0.]
[1. 0. 0. 0.]
[0. 3. 0. 0.]
[0. 0. 0. 0.]]
这个问题用 for 循环很容易解决,但我想知道我是否可以正确地向量化这个操作。
当你在做a[x,y]+=z
时,我们可以将操作分解为:
a[1, 0], a[2, 1], a[1, 0] = [a[1, 0] + 2, a[2, 1] + 3, a[1, 0] + 1]
# Equivalent to :
a[1, 0] = 2
a[2, 1] = 3
a[1, 0] = 1
这就是它不起作用的原因。 但是如果你用每个维度的循环递增你的数组,它应该工作
您可以创建一个大小为 3x4x4 的多维数组,然后将 z
添加到所有 3 个不同的维度,然后将它们相加
import numpy as np
x = [1,2,1]
y = [0,1,0]
z = [2,3,1]
a = np.zeros((3,4,4))
n = range(a.shape[0])
a[n,x,y] += z
print(sum(a))
这将导致
[[0. 0. 0. 0.]
[3. 0. 0. 0.]
[0. 3. 0. 0.]
[0. 0. 0. 0.]]
为此使用 np.add.at
:
import numpy as np
a = np.zeros((4,4))
x = [1, 2, 1]
y = [0, 1, 0]
z = [2, 3, 1]
np.add.at(a, (x, y), z)
print(a)
# [[0. 0. 0. 0.]
# [3. 0. 0. 0.]
# [0. 3. 0. 0.]
# [0. 0. 0. 0.]]
方法 #1:基于 Bincount 的性能方法
我们可以使用np.bincount
for efficient bin-based summation and basically inspired by
def accumulate_arr(x, y, z, out):
# Get output array shape
shp = out.shape
# Get linear indices to be used as IDs with bincount
lidx = np.ravel_multi_index((x,y),shp)
# Or lidx = coords[0]*(coords[1].max()+1) + coords[1]
# Accumulate arr with IDs from lidx
out += np.bincount(lidx,z,minlength=out.size).reshape(out.shape)
return out
如果您使用的是零初始化输出数组,请将输出形状直接输入函数并获得 bincount 输出作为最终输出。
给定样本的输出 -
In [48]: accumulate_arr(x,y,z,a)
Out[48]:
array([[0., 0., 0., 0.],
[3., 0., 0., 0.],
[0., 3., 0., 0.],
[0., 0., 0., 0.]])
方法 #2:使用稀疏矩阵提高内存效率
In [54]: from scipy.sparse import coo_matrix
In [56]: coo_matrix((z,(x,y)), shape=(4,4)).toarray()
Out[56]:
array([[0, 0, 0, 0],
[3, 0, 0, 0],
[0, 3, 0, 0],
[0, 0, 0, 0]])
如果您不介意稀疏矩阵,请跳过 .toarray()
部分以获得内存高效的解决方案。