在 Python 中矢量化平方欧氏距离的掩码
Vectorize mask of squared euclidean distance in Python
我正在 运行ning 代码生成一个掩码,其中 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列表,此代码需要一段时间。我很确定有一种方法可以对其进行矢量化,但可以使其 运行 快得多。谢谢。
使用二维数组索引更容易可视化此代码:
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,j
、outter
广播问题
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
我没有测试过这段代码,所以可能有错误,但我认为基本思想是存在的。并且可能足以让您为其他案例制定详细信息的思考过程。
您可以使用 scipy's cdist
,据称它对于此类距离计算非常有效,例如 -
from scipy.spatial.distance import cdist
N = (cdist(A,B,'sqeuclidean') <= dSquared).astype(int)
如中所建议,可以使用也可以使用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
我支持上面使用 Numpy 的建议。循环代码也对 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 中获得像样的性能是相当困难的。我还要指出,多维数组解决方案将需要...很多...内存。
只要你的矩阵 N 可能是稀疏的,scipy.spatial.cKDTree 将提供比基于计算所有距离蛮力的任何方法更好的时间复杂度:
cKDTree(A).sparse_distance_matrix(cKDTree(B), max_distance=D)
我正在 运行ning 代码生成一个掩码,其中 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列表,此代码需要一段时间。我很确定有一种方法可以对其进行矢量化,但可以使其 运行 快得多。谢谢。
使用二维数组索引更容易可视化此代码:
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,j
、outter
广播问题
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
我没有测试过这段代码,所以可能有错误,但我认为基本思想是存在的。并且可能足以让您为其他案例制定详细信息的思考过程。
您可以使用 scipy's cdist
,据称它对于此类距离计算非常有效,例如 -
from scipy.spatial.distance import cdist
N = (cdist(A,B,'sqeuclidean') <= dSquared).astype(int)
如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
我支持上面使用 Numpy 的建议。循环代码也对 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 中获得像样的性能是相当困难的。我还要指出,多维数组解决方案将需要...很多...内存。
只要你的矩阵 N 可能是稀疏的,scipy.spatial.cKDTree 将提供比基于计算所有距离蛮力的任何方法更好的时间复杂度:
cKDTree(A).sparse_distance_matrix(cKDTree(B), max_distance=D)