矩阵与向量之间的欧几里得距离

4

计算另一个向量的每个列向量的欧几里得距离。

这样说对吗?

distances=np.sqrt(np.sum(np.square(new_v-val.reshape(10,1)),axis=0))

new_v是一个矩阵。 val.reshape(10,1)是一个列向量。 还有其他/更好的方法来完成它。


new_v的形状是什么? - Divakar
大概是 (10, n) 对于任意的 n。 - wim
1
一个典型的 n 值会激发我们对一些时间的关注 :) - Divakar
你尝试过这些解决方案中的任何一个吗? - Divakar
2个回答

3

你所说的是正确的。在numpy.linalg中有一种更简单的方法:

from numpy.linalg import norm
norm(new_v.T-val, axis=1, ord=2)

如果从性能的角度来看,你可能在转置2D数组时做错了。如果你添加一个新的轴val,然后沿着axis=0使用norm函数,你会看到改进:norm(new_v-val[:,None], axis=0, ord=2) - Divakar
我运行了几次,似乎没有关系,可能是因为它正在使用转置,这只是一个视图而没有在那里进行复制。所以,我想你应该没问题。 - Divakar
是的,我已经注意到将一维向量重塑为二维列向量比转置矩阵略快。但是原帖并没有说最大性能是目标,所以我更喜欢最易读/符合NumPy风格的方法。 - wim

0

你可以利用高效的 np.einsum -

subs = new_v - val[:,None]
out = np.sqrt(np.einsum('ij,ij->j',subs,subs))

或者,使用 (a-b)^2 = a^2 + b^2 - 2ab 公式 -

out = np.sqrt(np.einsum('ij,ij->j',new_v, new_v) + val.dot(val) - 2*val.dot(new_v))

如果new_v的第二个轴是一个大轴,我们也可以使用numexpr模块在最后计算sqrt部分。 运行时测试 方法 -
import numexpr as ne

def einsum_based(new_v, val):
    subs = new_v - val[:,None]
    return np.sqrt(np.einsum('ij,ij->j',subs,subs))

def dot_based(new_v, val):
    return np.sqrt(np.einsum('ij,ij->j',new_v, new_v) + \
                            val.dot(val) - 2*val.dot(new_v))

def einsum_numexpr_based(new_v, val):
    subs = new_v - val[:,None]
    sq_dists = np.einsum('ij,ij->j',subs,subs)
    return ne.evaluate('sqrt(sq_dists)')

def dot_numexpr_based(new_v, val):
    sq_dists = np.einsum('ij,ij->j',new_v, new_v) + val.dot(val) - 2*val.dot(new_v)
    return ne.evaluate('sqrt(sq_dists)')

时间 -

In [85]: # Inputs
    ...: new_v = np.random.randint(0,9,(10,100000))
    ...: val = np.random.randint(0,9,(10))


In [86]: %timeit np.sqrt(np.sum(np.square(new_v-val.reshape(10,1)),axis=0))
    ...: %timeit einsum_based(new_v, val)
    ...: %timeit dot_based(new_v, val)
    ...: %timeit einsum_numexpr_based(new_v, val)
    ...: %timeit dot_numexpr_based(new_v, val)
    ...: 
100 loops, best of 3: 2.91 ms per loop
100 loops, best of 3: 2.1 ms per loop
100 loops, best of 3: 2.12 ms per loop
100 loops, best of 3: 2.26 ms per loop
100 loops, best of 3: 2.43 ms per loop

In [87]: from numpy.linalg import norm

# @wim's solution
In [88]: %timeit norm(new_v.T-val, axis=1, ord=2)
100 loops, best of 3: 5.88 ms per loop

我认为这比原作者的代码慢、长且不够清晰。 - wim
@wim 如果您对性能感兴趣,可以添加计时。至于“清晰度”,如果我对norm一无所知,那么我想其他解决方案也是如此。 - Divakar
我使用了 new_v = np.random.rand(10, 1000)val = np.random.rand(10)。实际上,使用两行的第一种方法更快。我最初尝试了单行的方法。 - wim
时间对于整数输入很奇怪。你应该使用浮点数来表示距离,而不是整数。 - wim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接