下一个for-loop如何通过numpy获得加速?我想这里可以使用一些花哨的索引技巧,但我不知道哪一个(可以在这里使用einsum?)。
a=0
for i in range(len(b)):
a+=numpy.mean(C[d,e,f+b[i]])*g[i]
编辑:
C
是一个形状与(20, 1600, 500)
相当的numpy 3D数组。
d,e,f
是“有趣”的点数的索引(d,e,f
的长度相同且大约为900)
b和g具有相同的长度(大约50)。平均值取自C
中具有索引d,e,f+b[i]
答案 0 :(得分:3)
您可以执行以下操作:
C[d, e][:, np.add.outer(f, b)].dot(g).diagonal().mean()
通过过早地采用将形成对角线的元素来提高甚至更多:
C[d, e][np.arange(len(d))[:, None], np.add.outer(f, b)].dot(g).mean()
答案 1 :(得分:1)
它非常类似于loopy版本:
np.sum(np.mean(C[d,e,f+b[:,None]], axis=1) * g)
您可以将求和乘法组合成点积:
C[d,e,f+b[:,None]].mean(1).dot(g)
但是对于似乎并不重要的时间安排;索引操作是迄今为止最耗时的操作(至少在Numpy 1.8.0上)。与此相比,原始代码中的循环开销无关紧要。
答案 2 :(得分:1)
两个会话都是用
初始化的In [1]: C = np.random.rand(20,1600,500)
In [2]: d = np.random.randint(0, 20, size=900)
In [3]: e = np.random.randint(1600, size=900)
In [4]: f = np.random.randint(400, size=900)
In [5]: b = np.random.randint(100, size=50)
In [6]: g = np.random.rand(50)
In [7]: %timeit C[d,e,f + b[:,np.newaxis]].mean(axis=1).dot(g)
1000 loops, best of 3: 942 µs per loop
In [8]: %timeit C[d[:,np.newaxis],e[:, np.newaxis],f[:, np.newaxis] + b].mean(axis=0).dot(g)
1000 loops, best of 3: 762 µs per loop
In [9]: %%timeit
...: a = 0
...: for i in range(len(b)):
...: a += np.mean(C[d, e, f + b[i]]) * g[i]
...:
100 loops, best of 3: 2.25 ms per loop
In [10]: np.__version__
Out[10]: '1.9.0'
In [11]: %%timeit
(C.ravel()[np.ravel_multi_index((d[:,np.newaxis],
e[:,np.newaxis],
f[:,np.newaxis] + b), dims=C.shape)]
.mean(axis=0).dot(g))
....:
1000 loops, best of 3: 940 µs per loop
In [7]: %timeit C[d,e,f + b[:,np.newaxis]].mean(axis=1).dot(g)
100 loops, best of 3: 2.81 ms per loop
In [8]: %timeit C[d[:,np.newaxis],e[:, np.newaxis],f[:, np.newaxis] + b].mean(axis=0).dot(g)
100 loops, best of 3: 2.7 ms per loop
In [9]: %%timeit
...: a = 0
...: for i in range(len(b)):
...: a += np.mean(C[d, e, f + b[i]]) * g[i]
...:
100 loops, best of 3: 4.12 ms per loop
In [10]: np.__version__
Out[10]: '1.8.2'
In [51]: %%timeit
(C.ravel()[np.ravel_multi_index((d[:,np.newaxis],
e[:,np.newaxis],
f[:,np.newaxis] + b), dims=C.shape)]
.mean(axis=0).dot(g))
....:
1000 loops, best of 3: 1.4 ms per loop
您可以使用坐标广播技巧从一开始就充实您的50x900阵列:
In [158]: C[d,e,f + b[:, np.newaxis]].shape
Out[158]: (50, 900)
从那时起,mean
和dot
会将您带到目的地:
In [159]: C[d,e,f + b[:, np.newaxis]].mean(axis=1).dot(g)
Out[159]: 13.582349962518611
In [160]:
a = 0
for i in range(len(b)):
a += np.mean(C[d, e, f + b[i]]) * g[i]
print(a)
.....:
13.5823499625
它比循环版本快3.3倍:
In [161]: %timeit C[d,e,f + b[:, np.newaxis]].mean(axis=1).dot(g)
1000 loops, best of 3: 585 µs per loop
In [162]: %%timeit
a = 0
for i in range(len(b)):
a += np.mean(C[d, e, f + b[i]]) * g[i]
.....:
1000 loops, best of 3: 1.95 ms per loop
数组的大小很大,因此您必须考虑CPU缓存。我不能说我知道np.sum
如何遍历数组,但在2d数组中总有一种更好的方法(当你选择的下一个元素是在内存方面相邻时)和稍微差一些方式(当下一个元素是跨越大步发现)。让我们看看我们是否可以通过在索引编制期间转置数组来赢得更多东西:
In [196]: C[d[:,np.newaxis], e[:,np.newaxis], f[:,np.newaxis] + b].mean(axis=0).dot(g)
Out[196]: 13.582349962518608
In [197]: %timeit C[d[:,np.newaxis], e[:,np.newaxis], f[:,np.newaxis] + b].mean(axis=0).dot(g)
1000 loops, best of 3: 461 µs per loop
这比循环快4.2倍。
答案 3 :(得分:0)
您在结构方面可能希望的唯一速度是使用以下代码:
#Initialize a 4-D array
aggregated = numpy.zeros((len(d), len(e), len(f), len(b)))
#Populate it by the shifted copies of C
for i in range(len(b)):
aggregated[:, :, :, i] = C[d, e, f + b[i]]
#Compute the mean on the first three axes
means = numpy.mean(aggregated, axis=(0, 1, 2))
#Multiply term-by-term by g (be careful that means and g have the same size!) and sum
a = numpy.sum(means * g)
然而这并不能保证计算速度更快,甚至可能因为以下原因而变慢:
无论如何,您应该对两种解决方案进行基准测试您也可以尝试使用像Cython这样的东西来执行for循环,但这似乎有点过头了。