[英]Finding closest three x,y points in three arrays
在Python中,我有三个包含x和y坐标的列表。 每个列表包含128点。 如何有效地找到最接近的三个点?
这是我工作的python代码,但效率不够:
def findclosest(c1, c2, c3):
mina = 999999999
for i in c1:
for j in c2:
for k in c3:
# calculate sum of distances between points
d = xy3dist(i,j,k)
if d < mina:
mina = d
def xy3dist(a, b, c):
l1 = math.sqrt((a[0]-b[0]) ** 2 + (a[1]-b[1]) ** 2 )
l2 = math.sqrt((b[0]-c[0]) ** 2 + (b[1]-c[1]) ** 2 )
l3 = math.sqrt((a[0]-c[0]) ** 2 + (a[1]-c[1]) ** 2 )
return l1+l2+l3
知道如何使用numpy做到吗?
您可以使用Numpy的广播功能对两个内部循环进行矢量化处理:
import numpy as np
def findclosest(c1, c2, c3):
c1 = np.asarray(c1)
c2 = np.asarray(c2)
c3 = np.asarray(c3)
for arr in (c1, c2, c3):
if not (arr.ndim == 2 and arr.shape[1] == 2):
raise ValueError("expected arrays of 2D coordinates")
min_val = np.inf
min_pos = None
for a, i in enumerate(c1):
d = xy3dist(i, c2.T[:,:,np.newaxis], c3.T[:,np.newaxis,:])
k = np.argmin(d)
if d.flat[k] < min_val:
min_val = d.flat[k]
b, c = np.unravel_index(k, d.shape)
min_pos = (a, b, c)
print a, min_val, d.min()
return min_val, min_pos
def xy3dist(a, b, c):
l1 = np.sqrt((a[0]-b[0]) ** 2 + (a[1]-b[1]) ** 2 )
l2 = np.sqrt((b[0]-c[0]) ** 2 + (b[1]-c[1]) ** 2 )
l3 = np.sqrt((a[0]-c[0]) ** 2 + (a[1]-c[1]) ** 2 )
return l1+l2+l3
np.random.seed(1234)
c1 = np.random.rand(5, 2)
c2 = np.random.rand(9, 2)
c3 = np.random.rand(7, 2)
val, pos = findclosest(c1, c2, c3)
a, b, c = pos
print val, xy3dist(c1[a], c2[b], c3[c])
也可以向量化所有3个循环
def findclosest2(c1, c2, c3):
c1 = np.asarray(c1)
c2 = np.asarray(c2)
c3 = np.asarray(c3)
d = xy3dist(c1.T[:,:,np.newaxis,np.newaxis], c2.T[:,np.newaxis,:,np.newaxis], c3.T[:,np.newaxis,np.newaxis,:])
k = np.argmin(d)
min_val = d.flat[k]
a, b, c = np.unravel_index(k, d.shape)
min_pos = (a, b, c)
return min_val, min_pos
如果数组很大,则findclosest
可能比findclosest2
更好,因为它使用的内存更少。 (如果数组很大,则仅矢量化最里面的一个循环。)
您可以通过Google搜索“ numpy广播”来了解np.newaxis的更多功能
让我们尝试确定一些不同的解决方案的时间。
我将使用numpy的随机函数初始化三个数组。 如果现有变量是元组列表或列表列表,则只需在它们上调用np.array
即可。
import numpy as np
c1 = np.random.normal(size=(128, 2))
c2 = np.random.normal(size=(128, 2))
c3 = np.random.normal(size=(128, 2))
首先,让我们为您的代码计时,以便我们有一个起点。
def findclosest(c1, c2, c3):
mina = 999999999
for i in c1:
for j in c2:
for k in c3:
# calculate sum of distances between points
d = xy3dist(i,j,k)
if d < mina:
mina = d
return mina
def xy3dist(a, b, c):
l1 = math.sqrt((a[0]-b[0]) ** 2 + (a[1]-b[1]) ** 2 )
l2 = math.sqrt((b[0]-c[0]) ** 2 + (b[1]-c[1]) ** 2 )
l3 = math.sqrt((a[0]-c[0]) ** 2 + (a[1]-c[1]) ** 2 )
return l1+l2+l3
%timeit findclosest(c1, c2, c3)
# 1 loops, best of 3: 23.3 s per loop
可能有用的一个函数是scipy.spatial.distance.cdist
,它计算两个点阵列之间的所有成对距离。 因此,我们可以使用它来预先计算和存储所有距离,然后简单地从这些数组获取并添加距离。 我也将使用itertools.product
简化循环,尽管它不会做任何加速工作。
from scipy.spatial.distance import cdist
from itertools import product
def findclosest_usingcdist(c1, c2, c3):
dists_12 = cdist(c1, c2)
dists_23 = cdist(c2, c3)
dists_13 = cdist(c1, c3)
min_dist = np.inf
ind_gen = product(range(len(c1)), range(len(c2)), range(len(c3)))
for i1, i2, i3 in ind_gen:
dist = dists_12[i1, i2] + dists_23[i2, i3] + dists_13[i1, i3]
if dist < min_dist:
min_dist = dist
min_points = (c1[i1], c2[i2], c3[i3])
return min_dist, min_points
%timeit findclosest_usingcdist(c1, c2, c3)
# 1 loops, best of 3: 2.02 s per loop
因此,使用cdist
我们获得一个数量级的加速。
但是,这甚至无法与@pv的答案相提并论。 剥离了他的一个实现,其中包含一些东西,可以更好地与以前的解决方案进行比较(返回点的实现请参见@pv的答案)。
def findclosest2(c1, c2, c3):
d = xy3dist(c1.T[:,:,np.newaxis,np.newaxis],
c2.T[:,np.newaxis,:,np.newaxis],
c3.T[:,np.newaxis,np.newaxis,:])
k = np.argmin(d)
min_val = d.flat[k]
i1, i2, i3 = np.unravel_index(k, d.shape)
min_points = (c1[i1], c2[i2], c3[i3])
return min_val, min_points
def xy3dist(a, b, c):
l1 = np.sqrt((a[0]-b[0]) ** 2 + (a[1]-b[1]) ** 2 )
l2 = np.sqrt((b[0]-c[0]) ** 2 + (b[1]-c[1]) ** 2 )
l3 = np.sqrt((a[0]-c[0]) ** 2 + (a[1]-c[1]) ** 2 )
return l1+l2+l3
%timeit findclosest_usingbroadcasting(c1, c2, c3)
# 100 loops, best of 3: 19.1 ms per loop
因此,这是巨大的提速,并且绝对是正确的答案。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.