Python最近邻居 - 坐标

时间:2016-02-05 15:20:20

标签: python scipy spatial kdtree map-projections

我想检查一下我是否正确使用了scipy的KD树,因为它看起来比简单的暴力更慢。

我有三个问题:

Q1。

如果我创建以下测试数据:

nplen = 1000000
# WGS84 lat/long
point = [51.349,-0.19]
# This contains WGS84 lat/long
points = np.ndarray.tolist(np.column_stack(
        [np.round(np.random.randn(nplen)+51,5),
         np.round(np.random.randn(nplen),5)]))

创建三个功能:

def kd_test(points,point):
    """ KD Tree"""
    return points[spatial.KDTree(points).query(point)[1]]

def ckd_test(points,point):
    """ C implementation of KD Tree"""
    return points[spatial.cKDTree(points).query(point)[1]]

def closest_math(points,point):
    """ Simple angle"""
    return (min((hypot(x2-point[1],y2-point[0]),y2,x2) for y2,x2 in points))[1:3]   

我希望cKD树是最快的 - 然而 - 运行它:

print("Co-ordinate: ", f(points,point))
print("Index: ", points.index(list(f(points,point))))
%timeit f(points,point)

结果时间 - 简单的暴力方法更快:

closest_math: 1 loops, best of 3: 3.59 s per loop
ckd_test: 1 loops, best of 3: 13.5 s per loop
kd_test: 1 loops, best of 3: 30.9 s per loop

这是因为我使用它错了 - 不知何故?

Q2。

我认为即使得到最近点的排名(而不是距离),仍需要投射数据。然而,似乎投射和未投射的点给了我相同的最近邻居:

def proj_list(points,
              inproj = Proj(init='epsg:4326'),
              outproj = Proj(init='epsg:27700')):
    """ Projected geo coordinates"""
    return [list(transform(inproj,outproj,x,y)) for y,x in points]
proj_points = proj_list(points)
proj_point = proj_list([point])[0]

这只是因为我的点数扩散不足以引入失真吗?我重新跑了几次,仍然从返回的预计和未投影列表中得到相同的索引。

Q3。

与计算(非投影)纬度/经度上的半径或vincenty距离相比,投射点(如上所述)和计算斜边距离通常更快吗?哪个选项会更准确?我做了一个小测试:

from math import *
def haversine(origin,
              destination):
    """
    Find distance between a pair of lat/lng coordinates
    """
    lat1, lon1, lat2, lon2 = map(radians, [origin[0],origin[1],destination[0],destination[1]])
    dlon = lon2 - lon1
    dlat = lat2 - lat1
    a = sin(dlat / 2) ** 2 + cos(lat1) * cos(lat2) * sin(dlon / 2) ** 2
    c = 2 * asin(sqrt(a))
    r = 6371000  # Metres
    return (c * r)

def closest_math_unproj(points,point):
    """ Haversine on unprojected """
    return (min((haversine(point,pt),pt[0],pt[1]) for pt in points))

def closest_math_proj(points,point):
    """ Simple angle since projected"""
    return (min((hypot(x2-point[1],y2-point[0]),y2,x2) for y2,x2 in points)) 

结果:

enter image description here

所以这似乎表明投射然后做距离比不快 - 但是,我不确定哪种方法会带来更准确的结果。

online vincenty calculation上对此进行测试似乎是预计的坐标:

enter image description here

1 个答案:

答案 0 :(得分:1)

Q1。

k-d树明显效率低下的原因很简单:你要同时测量k-d树的构造和查询。这不是你应该或应该如何使用k-d树:你应该只构造一次。如果仅测量查询,则所花费的时间减少到几十毫秒(使用蛮力方法的秒数)。

Q2。

这取决于所使用的实际数据的空间分布以及所使用的投影。根据k-d树的实现在平衡构造树的效率方面可能存在细微差别。如果您只查询一个点,那么结果将是确定性的,并且不受点分布的影响。

使用您正在使用的样本数据(具有强中心对称性)和地图投影(Transverese Mercator),差异应该可以忽略不计。

Q3。

从技术上讲,你的问题的答案是微不足道的:使用Haversine公式进行地理距离测量既准确又慢。是否需要在准确性和速度之间进行权衡取决于您的使用案例和数据的空间分布(显然主要取决于空间范围)。

如果你的点的空间范围在小的区域方面,那么使用合适的投影和简单的欧几里德距离测量对于你的用例可能是准确的,并且比使用Haversine公式更快。<​​/ p>