以向量形式高效地计算点到线的距离

时间:2018-12-03 15:25:45

标签: python numpy

计算distance of a point to a line in vector form很简单。

但是,我是通过以下方式实现的,这非常慢:

def compute_point_distance_to_line(point):
    dist = np.linalg.norm((a - point) - np.vdot((a - point), n) * n)
    return dist
np.apply_along_axis(compute_point_distance_to_line, 1, xyz)

我使用了维基百科的表示法,xyz的形状是(2521909,3),a,n的形状和点因此是(3,)

我通过以下方式尝试了

def compute_point_distance_to_line2(points):
    _a = np.tile(a, (points.shape[0], 1))
    _n = np.tile(n, (points.shape[0], 1))
    _n_t = np.ascontiguousarray(np.swapaxes(_n, 0, 1))

    diffs = _a - points
    vdots_scaled = np.dot(diffs, _n_t) * n
    diffs = diffs - vdots_scaled

    return np.linalg.norm(diffs, axis=1)

不幸的是,对我来说,这在计算点积时会导致内存错误。

有没有更快的方法?

2 个答案:

答案 0 :(得分:2)

您可以使用以下方式对此向量化:

temp = np.subtract(a, xyz)  # so we only have to compute this once
dist = np.linalg.norm(np.subtract(temp, np.multiply(np.dot(temp, n)[:, None], n)),
                      axis=-1)
# 220 ms ± 6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

与上面第一个代码示例的计时比较:

# 30 s ± 1.89 s per loop (mean ± std. dev. of 7 runs, 1 loop each)

它给我的结果与您的第一个代码示例(用np.array_equal()检查)相同,并且速度快了几个数量级。


说明

窍门是通过向np.multiply()的结果添加一个额外的轴来使np.dot()调用正常工作,我对[:, None]之后的np.dot()切片进行了处理。基本上,在numpy切片中使用的None是添加轴的快捷方式,因此np.dot()的结果应为(2521909,)的形状,并在带有None的方括号之后,将具有形状(2521909, 1)np.multiply()(和temp)的结果将具有形状(2521909, 3),我们沿最后一个轴取范数以获取从直线到每个2521909的3维距离点。

答案 1 :(得分:1)

通常,当可以使用广播代替时,请不要使用类似tile之类的操作,尤其是在速度/内存问题时。

https://docs.scipy.org/doc/numpy-1.15.0/user/basics.broadcasting.html

使用一点代数,您可以将其写为单个矩阵矢量乘积,后跟范数。这可以帮助您避免使用临时变量并节省内存。

这是一个可行的示例。注意,在此示例中,所有3D向量都是列向量,因此p是3x1000而不是1000x3。您必须转置p才能将其插入此示例。

import numpy as np
# define an example line with unit n
a = np.array([1,2,3])
n = np.array([4,5,6])
norm2n = np.sum(n**2)
n = n/np.sqrt(norm2n)
# get some point data p
p = np.random.randn(3,1000)
# form the projection matrix (see use of None in broadcasting at link above)
P = np.eye(3) - n[:,None]*n[None,:]
# perform the projection using matrix multiplication    
projected = P.dot(a[:,None]-p)
# get the distance
dist = np.sqrt(np.sum(projected**2, axis=0))