在@ajcr's great answer之后,我想确定哪种方法最快,所以我使用了timeit:
import timeit
setup_code = """
import numpy as np
i,j,k = (300,200,400)
ind = np.ones((i,j,k)) #shape=(3L, 2L, 4L)
dist = np.random.rand(i,j) #shape=(3L, 2L)
"""
basic ="np.array([np.dot(dist[l],ind[l]) for l in xrange(dist.shape[0])])"
einsum = "np.einsum('ijk,ij->ik', ind, dist)"
tensor= "np.tensordot(ind, dist, axes=[1, 1])[0].T"
print "tensor - total time:", min(timeit.repeat(stmt=tensor,setup=setup_code,number=10,repeat=3))
print "basic - total time:", min(timeit.repeat(stmt=basic,setup=setup_code,number=10,repeat=3))
print "einsum - total time:", min(timeit.repeat(stmt=einsum,setup=setup_code,number=10,repeat=3))
令人惊讶的结果是:
tensor - total time: 6.59519493952
basic - total time: 0.159871203461
einsum - total time: 0.263569731028
所以显然使用 tensordot 是错误的做法(更不用说在更大的例子中memory error,正如@ajcr 所说)。
由于这个例子很小,我将矩阵大小更改为i,j,k = (3000,200,400),翻转顺序以确保它没有效果并设置另一个重复次数更高的测试:
print "einsum - total time:", min(timeit.repeat(stmt=einsum,setup=setup_code,number=50,repeat=3))
print "basic - total time:", min(timeit.repeat(stmt=basic,setup=setup_code,number=50,repeat=3))
结果与第一次运行一致:
einsum - total time: 13.3184077671
basic - total time: 8.44810031351
但是,测试另一种类型的大小增长 - i,j,k = (30000,20,40) 导致以下结果:
einsum - total time: 0.325594117768
basic - total time: 0.926416766397
有关这些结果的解释,请参见 cmets。
道德是,在为特定问题寻找最快的解决方案时,尝试生成在类型和形状方面尽可能与原始数据相似的数据。在我的情况下,i 比 j,k 小得多,所以我选择了丑陋的版本,这也是这种情况下最快的版本。