高效地对大型拆分数组执行 numpy.sum(或 scipy.integrate.simps())

Perform numpy.sum (or scipy.integrate.simps()) on large splitted array efficiently

让我们考虑一个非常大的 numpy 数组 a (M, N)。 其中 M 通常可以是 1 或 100,N 可以是 10-100,000,000

我们有索引数组,可以沿轴=1 将其拆分为多个 (K = 1,000,000)。

我们希望在每个子数组和 return (M, K) 数组上高效地执行类似沿轴=1 积分(np.sum 采取最简单形式)的操作。

有问题的@Divakar 提出了一个优雅而有效的解决方案[41920367],但我的理解是它只适用于所有子数组具有相同形状的情况,这允许重塑。

但在我们的例子中,子数组的形状不同,到目前为止,这迫使我在索引上循环……请让我摆脱痛苦……

例子

a = np.random.random((10, 100000000))
ind = np.sort(np.random.randint(10, 9000000, 1000000))

子数组的大小不均匀:

sizes = np.diff(ind)
print(sizes.min(), size.max())
2, 8732

到目前为止,我找到的最好的是:

output = np.concatenate([np.sum(vv, axis=1)[:, None] for vv in np.split(a, ind, axis=1)], axis=1)

numpy 和 scipy 的可能功能请求:

如果循环确实不可避免,至少在 C 语言中的 numpy 和 scipy.integrate.simps(或 romb)函数中完成它可能会加快输出速度。 像

output = np.sum(a, axis=1, split_ind=ind)
output = scipy.integrate.simps(a, x=x, axis=1, split_ind=ind)
output = scipy.integrate.romb(a, x=x, axis=1, split_ind=ind)

非常欢迎! (其中 x 本身可以拆分,也可以不拆分)

旁注:

在尝试这个例子时,我注意到这些数字几乎总是有一个元素的大小等于 0(sizes.min() 几乎总是零)。 这对我来说很奇怪,因为我们在 10 到 9,000,000 之间选择 10,000 个整数,相同数字出现两次的几率(例如 diff = 0)应该接近 0。它似乎非常接近 1。 这是由于 np.random.randint 背后的算法吗?

你要的是np.add.reduceat

output = np.add.reduceat(a, ind, axis = 1)

output.shape
Out[]: (10, 1000000)

Universal Functions (ufunc)numpy

中非常强大的工具

至于重复索引,那只是Birthday Problem的突然出现。

太棒了!

谢谢!在我的 VM Cent OS 6.9 上,我得到以下结果:

在[71]中:a = np.random.random((10, 10000000))

在 [72] 中:ind = np.unique(np.random.randint(10, 9000000, 100000))

在 [73] 中:ind2 = np.append([0], ind)

In [74]: out = np.concatenate([np.sum(vv, axis=1)[:, None] for vv in np.split(a , ind, 轴=1)], 轴=1)

In [75]: out2 = np.add.reduceat(a, ind2, axis=1)

在 [83] 中:np.allclose(out, out2)
输出[83]:真

In [84]: %timeit out = np.concatenate([np.sum(vv, axis=1)[:, None] for vv in np.split (a, ind, axis=1)], axis=1)
每个循环 2.7 s ± 40.4 ms(7 次运行的平均值 ± 标准偏差,每次 1 个循环)

在 [85] 中:%timeit out2 = np.add.reduceat(a, ind2, axis=1)
每个循环 179 毫秒 ± 15.9 毫秒(7 次运行的平均值 ± 标准偏差,每次 1 个循环)

与列表串联相比,这是一个很好的 93% 的速度增益(或更快的 15 倍):-) 太棒了!