Vectorize方形欧陆式距离的面具在Python中

时间:2016-04-14 16:18:17

标签: python numpy scipy vectorization euclidean-distance

我正在运行代码以生成B中位置的掩码,该掩码距离A的某个距离D更近。

N = [[0 for j in range(length_B)] for i in range(length_A)]    
dSquared = D*D

for i in range(length_A):
    for j in range(length_B):
        if ((A[j][0]-B[i][0])**2 + (A[j][1]-B[i][1])**2) <= dSquared:
            N[i][j] = 1

对于长达数万个位置的A和B列表,此代码需要一段时间。我非常确定有一种方法可以对其进行矢量化,以使其运行得更快。谢谢。

4 个答案:

答案 0 :(得分:3)

使用2d数组索引可视化此代码更容易:

for j in range(length_A):
    for i in range(length_B):
        dist = (A[j,0] - B[i,0])**2 + (A[j,1] - B[i,1])**2 
        if dist <= dSquared:
            N[i, j] = 1

dist表达式看起来像

((A[j,:] - B[i,:])**2).sum(axis=1)

使用2个元素,这个数组表达式可能不会更快,但它应该有助于我们重新思考这个问题。

我们可以执行广播的i,joutter问题

A[:,None,:] - B[None,:,:]  # 3d difference array

dist=((A[:,None,:] - B[None,:,:])**2).sum(axis=-1)  # (lengthA,lengthB) array

将此与dSquared进行比较,并使用生成的布尔数组作为掩码,将N的元素设置为1:

N = np.zeros((lengthA,lengthB))
N[dist <= dSquared] = 1

我没有测试过这段代码,所以可能存在错误,但我认为基本的想法是存在的。并且可能已经足够让您了解其他案例的细节。

答案 1 :(得分:2)

你可以使用scipy's cdist对于这样的距离计算非常有效,如此 -

from scipy.spatial.distance import cdist

N = (cdist(A,B,'sqeuclidean') <= dSquared).astype(int)

根据@hpaulj's solution的建议,也可以使用broadcasting。现在,根据问题中的已发布代码,我们看起来正在处理Nx2形状的数组。因此,我们基本上可以对第一列和第二列进行切片,并分别对它们进行广播减法。好处是我们不会去3D并因此保持内存效率,这也可能转化为性能提升。因此,平方的欧氏距离将如此计算 -

sq_eucl_dist = (A[:,None,0] - B[:,0])**2 + (A[:,None,1] - B[:,1])**2

让所有这三种方法用于平方欧氏距离计算。

运行时测试 -

In [75]: # Input arrays
    ...: A = np.random.rand(200,2)
    ...: B = np.random.rand(200,2)
    ...: 

In [76]: %timeit ((A[:,None,:] - B[None,:,:])**2).sum(axis=-1) # @hpaulj's solution
1000 loops, best of 3: 1.9 ms per loop

In [77]: %timeit (A[:,None,0] - B[:,0])**2 + (A[:,None,1] - B[:,1])**2
1000 loops, best of 3: 401 µs per loop

In [78]: %timeit cdist(A,B,'sqeuclidean')
1000 loops, best of 3: 249 µs per loop

答案 2 :(得分:0)

我接下来建议使用上面的Numpy。循环代码也比A需要更多地索引到A中。你可以使用类似的东西:

import numpy as np

dimension = 10000
A = np.random.rand(dimension, 2) + 0.0
B = np.random.rand(dimension, 2) + 1.0
N = []
d = 1.0

for i in range(len(A)):
    distances = np.linalg.norm(B - A[i,:], axis=1)
    for j in range(len(distances)):
        if distances[j] <= d:
            N.append((i,j))

print(len(N))

用纯粹的Python来获得这样的性能会非常困难。我还要指出,更维度的数组解决方案需要很多内存。

答案 3 :(得分:0)

如果您的矩阵N可能很稀疏,scipy.spatial.cKDTree将比基于计算所有距离暴力的任何方法提供更好的时间复杂度:

cKDTree(A).sparse_distance_matrix(cKDTree(B), max_distance=D)