我在二维空间中有一组点,需要计算从每个点到另一个点的距离。
我的点数相对较少,可能最多只有100个。但是因为我需要经常快速地完成它以确定这些移动点之间的关系,并且因为我知道迭代通过点数可能与O(n ^ 2)复杂度一样糟糕,我正在寻找利用numpy矩阵魔法(或scipy)的方法。
正如我的代码所示,每个对象的坐标都存储在它的类中。但是,当我更新类坐标时,我也可以在numpy数组中更新它们。
class Cell(object):
"""Represents one object in the field."""
def __init__(self,id,x=0,y=0):
self.m_id = id
self.m_x = x
self.m_y = y
我想要创建一个欧几里德距离矩阵来防止重复,但也许你有一个更聪明的数据结构。
我也可以指向漂亮的算法。
此外,我注意到有类似的问题涉及欧几里德距离和numpy,但没有找到任何直接解决有效填充全距离矩阵的问题。
答案 0 :(得分:26)
您可以使用complex
类型:
# build a complex array of your cells
z = np.array([complex(c.m_x, c.m_y) for c in cells])
# mesh this array so that you will have all combinations
m, n = np.meshgrid(z, z)
# get the distance via the norm
out = abs(m-n)
网格化是主要的想法。但是numpy
很聪明,所以你不必生成m
& n
。只需使用z
的转置版本计算差异。网格自动完成:
out = abs(z[..., np.newaxis] - z)
如果将z
直接设置为二维数组,则可以使用z.T
而不是奇怪的z[..., np.newaxis]
。最后,您的代码将如下所示:
z = np.array([[complex(c.m_x, c.m_y) for c in cells]]) # notice the [[ ... ]]
out = abs(z.T-z)
>>> z = np.array([[0.+0.j, 2.+1.j, -1.+4.j]])
>>> abs(z.T-z)
array([[ 0. , 2.23606798, 4.12310563],
[ 2.23606798, 0. , 4.24264069],
[ 4.12310563, 4.24264069, 0. ]])
作为补充,你可能想要删除重复项,取上面的三角形:
>>> np.triu(out)
array([[ 0. , 2.23606798, 4.12310563],
[ 0. , 0. , 4.24264069],
[ 0. , 0. , 0. ]])
>>> timeit.timeit('abs(z.T-z)', setup='import numpy as np;z = np.array([[0.+0.j, 2.+1.j, -1.+4.j]])')
4.645645342274779
>>> timeit.timeit('abs(z[..., np.newaxis] - z)', setup='import numpy as np;z = np.array([0.+0.j, 2.+1.j, -1.+4.j])')
5.049334864854522
>>> timeit.timeit('m, n = np.meshgrid(z, z); abs(m-n)', setup='import numpy as np;z = np.array([0.+0.j, 2.+1.j, -1.+4.j])')
22.489568296184686
答案 1 :(得分:7)
以下是使用numpy的方法:
import numpy as np
x = np.array([0,1,2])
y = np.array([2,4,6])
# take advantage of broadcasting, to make a 2dim array of diffs
dx = x[..., np.newaxis] - x[np.newaxis, ...]
dy = y[..., np.newaxis] - y[np.newaxis, ...]
dx
=> array([[ 0, -1, -2],
[ 1, 0, -1],
[ 2, 1, 0]])
# stack in one array, to speed up calculations
d = np.array([dx,dy])
d.shape
=> (2, 3, 3)
现在剩下的就是沿着0轴计算L2范数(如here所述):
(d**2).sum(axis=0)**0.5
=> array([[ 0. , 2.23606798, 4.47213595],
[ 2.23606798, 0. , 2.23606798],
[ 4.47213595, 2.23606798, 0. ]])
答案 2 :(得分:4)
如果您不需要全距离矩阵,最好使用kd-tree。考虑scipy.spatial.cKDTree
或sklearn.neighbors.KDTree
。这是因为kd-tree kan在O(n log n)时间内找到k-近似邻居,因此你可以避免计算所有n乘n距离的O(n ** 2)复杂度。
答案 3 :(得分:3)
Jake Vanderplas使用 Python数据科学手册中的广播给出了此示例,该示例与@ shx2提出的非常相似。
import numpy as np
rand = random.RandomState(42)
X = rand.rand(3, 2)
dist_sq = np.sum((X[:, np.newaxis, :] - X[np.newaxis, :, :]) ** 2, axis = -1)
dist_sq
array([[0. , 0.18543317, 0.81602495],
[0.18543317, 0. , 0.22819282],
[0.81602495, 0.22819282, 0. ]])