在numpy中使用间接索引对多维数组求和的快速方法

马科斯蒂亚

计算以下表达式的最佳(最快)方法是什么:

\ sum_ {i \ in I} \ alpha_i \ sum_ {j \ in J} \ beta_j M [:, i,j]对于给定的numpy数组:

  • I,J包含索引;

  • 形状(1,| I |)和形状(1,| J |)的系数的alpha和beta;

  • ndim的M = 3。

在更一般的情况下,我需要针对具有相同M数组的许多alpha,beta,I,J输入进行计算。因此,可以认为alphas nad Is具有形状(N,4),betas和Js具有形状(N,3),我需要为range(N)中的每个n计算此表达式。

先感谢您。

根据一些意见,为了使问题更清楚并添加一些上下文,以下是采用实际大小的天真的方法:

M 有形状 (500, 200000, 20)

I 有形状 (10^6, 4)

J 有形状 (10^6, 3)

alpha 有形状 (10^6, 4)

beta 有形状 (10^6, 3)

N = 10**6
M_new = np.zeros(M.shape[0], N)
for n in range(N):
    for i in range(4):
        for j in range(3):
            M_new[:, n] += alpha[n, i] * beta[n, j] * M[:, I[n, i], J[n, j]]    

因此,问题在于如何尽快计算M_new。

解决方案

到目前为止,最快的解决方案是@jdehesa使用Numba提出的解决方案。

@ Han-KwangNienhuys用其他方法介绍了速度比较。

Jdehesa

编辑:
我首先误解了这个问题。我在下面留下了原始答案,但它没有做什么问题。

冒着听起来明显的危险,您可以随时求助于Numba:

import numpy as np
import numba as nb

# Original loop implementation
def comb_loop(m, ii, jj, alpha, beta):
    n = ii.shape[0]
    m_new = np.zeros((m.shape[0], n))
    for col in range(n):
        for i in range(4):
            for j in range(3):
                m_new[:, col] += alpha[col, i] * beta[col, j] * m[:, ii[col, i], jj[col, j]]
    return m_new

# Numba implementation
@nb.njit(parallel=True)
def comb_nb(m, ii, jj, alpha, beta):
    n = ii.shape[0]
    m_new = np.empty((m.shape[0], n), m.dtype)
    for col in nb.prange(n):
        for row in range(m.shape[0]):
            val = 0
            for i in range(4):
                for j in range(3):
                    val += alpha[col, i] * beta[col, j] * m[row, ii[col, i], jj[col, j]]
            m_new[row, col] = val
    return m_new


# Test
np.random.seed(0)
N = 1_000  # Reduced for testing
m = np.random.rand(500, 200_000, 20)
ii = np.random.randint(m.shape[1], size=(N, 4))
jj = np.random.randint(m.shape[2], size=(N, 3))
alpha = np.random.rand(N, 4)
beta = np.random.rand(N, 3)

# Check results match
print(np.allclose(comb_loop(m, ii, jj, alpha, beta), comb_nb(m, ii, jj, alpha, beta)))
# True

# Timings
%timeit comb_loop(m, ii, jj, alpha, beta)
# 181 ms ± 1.77 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit comb_nb(m, ii, jj, alpha, beta)
# 31.1 ms ± 2.03 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

原始错误答案

您可以使用np.einsum

import numpy as np

def comb(alpha, beta, m):
    return np.einsum('i,j,nij->n', alpha, beta, m)

# Test
np.random.seed(0)
alpha = np.random.rand(10)
beta = np.random.rand(20)
m = np.random.rand(30, 10, 20)
result = comb(alpha, beta, m)
print(result.shape)
# (30,)

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章