我有这个函数来计算向量x的平方马氏距离,意思是:
def mahalanobis_sqdist(x, mean, Sigma):
'''
Calculates squared Mahalanobis Distance of vector x
to distibutions' mean
'''
Sigma_inv = np.linalg.inv(Sigma)
xdiff = x - mean
sqmdist = np.dot(np.dot(xdiff, Sigma_inv), xdiff)
return sqmdist
我有一个numpy数组,它的形状是(25, 4)
。所以,我想将该函数应用到数组的所有25行,而不使用for循环。所以,基本上,我如何写出这个循环的矢量形式:
for r in d1:
mahalanobis_sqdist(r[0:4], mean1, Sig1)
其中mean1
和Sig1
是:
>>> mean1
array([ 5.028, 3.48 , 1.46 , 0.248])
>>> Sig1 = np.cov(d1[0:25, 0:4].T)
>>> Sig1
array([[ 0.16043333, 0.11808333, 0.02408333, 0.01943333],
[ 0.11808333, 0.13583333, 0.00625 , 0.02225 ],
[ 0.02408333, 0.00625 , 0.03916667, 0.00658333],
[ 0.01943333, 0.02225 , 0.00658333, 0.01093333]])
我试过以下方法,但没有成功:
>>> vecdist = np.vectorize(mahalanobis_sqdist)
>>> vecdist(d1, mean1, Sig1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python2.7/dist-packages/numpy/lib/function_base.py", line 1862, in __call__
theout = self.thefunc(*newargs)
File "<stdin>", line 6, in mahalanobis_sqdist
File "/usr/lib/python2.7/dist-packages/numpy/linalg/linalg.py", line 445, in inv
return wrap(solve(a, identity(a.shape[0], dtype=a.dtype)))
IndexError: tuple index out of range
要将函数应用于数组的每一行,可以使用:
不过,在这种情况下,还有更好的办法。不必对每一行应用函数。相反,您可以对整个
d1
数组应用NumPy操作来计算相同的结果。np.einsum可以替换for-loop
和对np.dot
的两个调用:以下是一些基准:
因此
mahalanobis_sqdist2
比for-loop
快18倍,比使用np.apply_along_axis
快26倍。注意,
np.apply_along_axis
,np.vectorize
,np.frompyfunc
是Python实用程序函数。在引擎盖下他们使用for-
或while-loop
s。这里没有真正的“矢量化”。它们可以提供语法帮助,但不要期望它们能使代码的性能比您自己编写的代码更好。刚刚在reddit上看到了一个非常好的评论,它可能会让事情更快一点:
@unutbu的答案对于将任何函数应用于数组的行非常有效。 在这种特殊情况下,如果使用大型数组,可以使用一些数学对称性来显著加快速度。
以下是函数的修改版本:
如果您最终使用任何类型的大型
Sigma
,我建议您缓存Sigma_inv
,并将其作为参数传递给函数。 因为在本例中是4x4,所以这无关紧要。 我将展示如何处理大型的Sigma
不管是谁遇到这个。如果您不打算重复使用同一个
Sigma
,您将无法缓存它,因此,您可以使用不同的方法来求解线性系统,而不是反转矩阵。 在这里,我将使用内置到SciPy中的LU分解。 这只会在x
的列数相对于其行数较大时提高时间。下面是一个函数,它显示了这种方法:
这里有一些时间安排。 我将把另一个答案中提到的带有
einsum
的版本包括在内。给予:
但是,更改所涉及阵列的大小会更改计时结果。 例如,让
x = np.random.rand(2500, 4)
,计时如下:让
x = np.random.rand(1000, 1000)
、Sigma1 = np.random.rand(1000, 1000)
和mean1 = np.random.rand(1000)
计时如下:编辑:我注意到其他答案之一使用了Cholesky分解。 假设
Sigma
是对称的正定的,我们实际上可以做得比上面的结果更好。 通过SciPy,BLAS和LAPACK提供了一些很好的例程,可以处理对称正定矩阵。 这里有两个更快的版本。第一个仍然颠倒西格玛。 如果预先计算并重用逆运算,则速度会快得多(在我的机器上,使用预先计算的逆运算,1000x1000的情况需要35.6ms)。 我还使用了einsum来获取产品,然后沿着最后一个轴求和。 结果,这比做
(A * B).sum(axis=-1)
之类的事情快得多。 这两个功能提供以下计时:第一个测试用例:
第二个测试用例:
第三个测试用例:
相关问题 更多 >
编程相关推荐