我正在寻找计算两个numpy数组(x和y)之间的马氏距离的NumPy方法。
以下代码可以使用Scipy的cdist函数正确计算出相同的值。 由于此函数在我的情况下会计算出不必要的后缀,因此我想仅使用NumPy进行更直接的计算。
import numpy as np
from scipy.spatial.distance import cdist
x = np.array([[[1,2,3,4,5],
[5,6,7,8,5],
[5,6,7,8,5]],
[[11,22,23,24,5],
[25,26,27,28,5],
[5,6,7,8,5]]])
i,j,k = x.shape
xx = x.reshape(i,j*k).T
y = np.array([[[31,32,33,34,5],
[35,36,37,38,5],
[5,6,7,8,5]],
[[41,42,43,44,5],
[45,46,47,48,5],
[5,6,7,8,5]]])
yy = y.reshape(i,j*k).T
results = cdist(xx,yy,'mahalanobis')
results = np.diag(results)
print results
[ 2.28765854 2.75165028 2.75165028 2.75165028 0. 2.75165028
2.75165028 2.75165028 2.75165028 0. 0. 0. 0.
0. 0. ]
我的审判:
VI = np.linalg.inv(np.cov(xx,yy))
print np.sqrt(np.dot(np.dot((xx-yy),VI),(xx-yy).T))
有人可以纠正这种方法吗?
这是它的公式:
http://docs.scipy.org/doc/scipy-0.14.0/reference/generation/scipy.spatial.distance.mahalanobis.html#scipy.spatial.distance.mahalanobis
我想计算[1,11]和[31,41]之间的马氏距离; [2,22]和[32,42]等。
scipy中的实现是纯python代码。您可以将自己的方法与他们的方法进行比较。有关两个向量之间的马哈拉诺比斯距离的计算,请参见此处:github.com/scipy/scipy/blob/有关观察矩阵的距离的计算,您可能必须遍历每个观察向量。
是的,我尝试从该来源进行计算,但是由于我对Python的了解很少,因此尚未完成。你能看看我的审判吗?
您的方法仅与辣味(包括已转置的delta)不同,而辣味的源代码不会在第二次出现时转置delta ...
@jkalden认为这是一个错误,我已在此处提交了报告mail.scipy.org/pipermail/scipy-dev/2014-December/020301.html
@jkalden我的仅基于numpy的试用版无法正常工作,您能解决吗?
在scipy源中,u和v是一维数组,因此进行转置(用ndarray表示)没有区别。
@xnx oho很高兴知道,谢谢。
您能否再说一遍您要在这里做什么:即为什么您输入的是二维数组?您是否要通过向量对广播马氏距离计算?
是的,我想计算[1,11]和[31,41]之间的马氏距离; [2,22]和[32,42]等。
我认为您的问题出在协方差矩阵的构建上。 尝试:
X = np.vstack([xx,yy])
V = np.cov(X.T)
VI = np.linalg.inv(V)
print np.diag(np.sqrt(np.dot(np.dot((xx-yy),VI),(xx-yy).T)))
输出:
[ 2.28765854 2.75165028 2.75165028 2.75165028 0. 2.75165028
2.75165028 2.75165028 2.75165028 0. 0. 0. 0.
0. 0. ]
要在没有隐式创建中间数组的情况下执行此操作,您可能必须为Python一个循环牺牲一个C循环:
A = np.dot((xx-yy),VI)
B = (xx-yy).T
n = A.shape[0]
D = np.empty(n)
for i in range(n):
D[i] = np.sqrt(np.sum(A[i] * B[:,i]))
编辑:实际上,使用np.einsum伏都教,您可以删除Python循环并加快很多速度(在我的系统上,从84.3μs到2.9μs):
D = np.sqrt(np.einsum('ij,ji->i', A, B))
编辑:正如@Warren Weckesser指出的那样,einsum也可以用于消除中间的A和B数组:
delta = xx - yy
D = np.sqrt(np.einsum('nj,jk,nk->n', delta, VI, delta))
感谢您的尝试,已投票。实际上,我希望避免使用np.diag来减少不必要的计算以加快速度。
非常感谢。我可以知道C循环是什么意思吗?
NumPy例程"在幕后"使用已编译的C代码,因此它们比字节码已编译的Python循环要快得多(但您必须预先定义数据类型和数组大小)。这就是为什么我最近的编辑中的einsum解决方案比D的Python循环快得多的原因。
请为我解释np.einsum(ij,ji-> i,A,B)做什么?主要是ij,ji-> i的含义。我希望你不介意。
einsum实现爱因斯坦求和约定。 NumPy文档具有更多详细信息,但此调用基本上通过将A的行乘以B的列来实现矩阵(点)乘积,但仅输出(->)位于结果对角线上的那些元素。
np.cov(X.T)产生协方差矩阵而不是协方差,它真的需要协方差矩阵而不是协方差吗?如果不是,计算协方差是否仅会加速进一步的过程?
您可以将einsum用于完整的产品:delta = xx - yy; D = np.sqrt(np.einsum(nj,jk,nk->n, delta, VI, delta))
@WarrenWeckesser太棒了!您介意解释一下您的代码吗?
将其分为两个步骤:p1 = einsum(nj,jk->nk, delta, VI)与delta.dot(VI)相同。 p2 = einsum(nk,nk->n, p1, delta)是p1和delta行的成对点积。查看其他示例的einsum文档字符串。
与einsum一样快的另一个简单解决方案
e = xx-yy
X = np.vstack([xx,yy])
V = np.cov(X.T)
p = np.linalg.inv(V)
D = np.sqrt(np.sum(np.dot(e,p) * e, axis = 1))