np.apply_along_axis不速度.
没有办法将纯Python函数应用于Numpy数组的每个元素,而不需要多次调用,而不是AST重写.
幸运的是有解决方案:
>矢量化
虽然这通常很困难,但通常是一个简单的解决方案.找到某种方式来表达您的计算方式,以推广这些元素,所以您可以一次在整个矩阵上工作.这将导致循环从Python中升级,并进行大量优化的C和Fortran例程.
> JITing:Numba和Parakeet,在较小程度上PyPy与NumPyPy
Numba和Parakeet都处理了Numpy数据结构中的JITing循环,所以如果你将循环内联到一个函数(这可以是一个包装函数),你可以获得大幅度的加速提升几乎免费.这取决于所使用的数据结构.
>象征评估者,如Theano和numexpr
这些允许您使用嵌入式语言来表达计算,这可能比矢量化版本更快.
> Cython和C扩展
如果所有的东西都丢失了,你可以随时手动挖掘C. Cython隐藏了很多复杂性,并且还有很多可爱的魔法,所以并不总是那么糟糕(尽管它有助于知道你在做什么).
干得好.
这是我的测试“环境”(你应该真的提供这个:P):
import itertools
import numpy
a = numpy.arange(200).reshape((200,1)) ** 2
def my_func(a, i,j):
b = numpy.zeros((2,2))
b[0,0] = a[i]
b[1,0] = a[i]
b[0,1] = a[i]
b[1,1] = a[j]
return numpy.linalg.eigh(b)
eigvals = {}
eigvecs = {}
for i, j in itertools.combinations(range(a.size), 2):
eigvals[i, j], eigvecs[i, j] = my_func(a,i,j)
现在,更容易得到所有的排列而不是组合,因为你可以这样做:
# All *permutations*, not combinations
indexes = numpy.mgrid[:a.size, :a.size]
这可能看起来很浪费,但只有两倍的排列,所以这不是一个大问题.
所以我们要使用这些索引来获取相关元素:
# Remove the extra dimension; it's not wanted here!
subs = a[:,0][indexes]
然后我们可以使我们的矩阵:
target = numpy.array([
[subs[0], subs[0]],
[subs[0], subs[1]]
])
我们需要矩阵在最后两个维度:
target.shape
#>>> (2, 2, 200, 200)
target = numpy.swapaxes(target, 0, 2)
target = numpy.swapaxes(target, 1, 3)
target.shape
#>>> (200, 200, 2, 2)
我们可以检查它是否有效:
target[10, 20]
#>>> array([[100, 100],
#>>> [100, 400]])
好极了!
所以我们只是运行numpy.linalg.eigh:
values, vectors = numpy.linalg.eigh(target)
看,它的作品!
values[10, 20]
#>>> array([ 69.72243623, 430.27756377])
eigvals[10, 20]
#>>> array([ 69.72243623, 430.27756377])
那么我想象你可能想连接这些:
numpy.concatenate([values[row, row+1:] for row in range(len(values))])
#>>> array([[ 0.00000000e+00, 1.00000000e+00],
#>>> [ 0.00000000e+00, 4.00000000e+00],
#>>> [ 0.00000000e+00, 9.00000000e+00],
#>>> ...,
#>>> [ 1.96997462e+02, 7.78160025e+04],
#>>> [ 3.93979696e+02, 7.80160203e+04],
#>>> [ 1.97997475e+02, 7.86070025e+04]])
numpy.concatenate([vectors[row, row+1:] for row in range(len(vectors))])
#>>> array([[[ 1. , 0. ],
#>>> [ 0. , 1. ]],
#>>>
#>>> [[ 1. , 0. ],
#>>> [ 0. , 1. ]],
#>>>
#>>> [[ 1. , 0. ],
#>>> [ 0. , 1. ]],
#>>>
#>>> ...,
#>>> [[-0.70890372, 0.70530527],
#>>> [ 0.70530527, 0.70890372]],
#>>>
#>>> [[-0.71070503, 0.70349013],
#>>> [ 0.70349013, 0.71070503]],
#>>>
#>>> [[-0.70889463, 0.7053144 ],
#>>> [ 0.7053144 , 0.70889463]]])
也可以在numpy.mgrid之后做这个连接循环将工作量减半:
# All *permutations*, not combinations
indexes = numpy.mgrid[:a.size, :a.size]
# Convert to all *combinations* and reduce the dimensionality
indexes = numpy.concatenate([indexes[:, row, row+1:] for row in range(indexes.shape[1])], axis=1)
# Remove the extra dimension; it's not wanted here!
subs = a[:,0][indexes]
target = numpy.array([
[subs[0], subs[0]],
[subs[0], subs[1]]
])
target = numpy.rollaxis(target, 2)
values, vectors = numpy.linalg.eigh(target)
是的,最后一个样本是你需要的.