簡體   English   中英

"有效地從 Python 中的集合中找到最近的坐標對"

[英]Efficiently finding the closest coordinate pair from a set in Python

問題

想象一下我站在機場。 給定一個地理坐標對,如何有效地確定我站在哪個機場?

輸入

  • 代表我所在位置的坐標對(x,y)
  • 一組坐標對[(a1,b1), (a2,b2)...] ,其中每個坐標對代表一個機場。

期望的輸出

來自機場坐標對集合的坐標對(a,b) ,表示離點(x,y)最近的機場。

低效解決方案

這是我解決這個問題的低效嘗試。 它在機場集合的長度上顯然是線性的。

shortest_distance = None
shortest_distance_coordinates = None

point = (50.776435, -0.146834)

for airport in airports:
    distance = compute_distance(point, airport)
    if distance < shortest_distance or shortest_distance is None:
        shortest_distance = distance
        shortest_distance_coordinates = airport

問題

如何改進此解決方案? 這可能涉及基於我們當前所在位置的坐標預先過濾機場列表的某種方式,或者預先按特定順序對它們進行排序。

使用k維樹:

>>> from scipy import spatial
>>> airports = [(10,10),(20,20),(30,30),(40,40)]
>>> tree = spatial.KDTree(airports)
>>> tree.query([(21,21)])
(array([ 1.41421356]), array([1]))

其中1.41421356是查詢點與最近鄰居之間的距離,1是鄰居的索引。

請參閱: http//docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.KDTree.query.html#scipy.spatial.KDTree.query

從這個SO問題

import numpy as np
def closest_node(node, nodes):
    nodes = np.asarray(nodes)
    deltas = nodes - node
    dist_2 = np.einsum('ij,ij->i', deltas, deltas)
    return np.argmin(dist_2)

其中node是具有兩個值(x,y)的元組,而nodes是具有兩個值的元組數組( [(x_1, y_1), (x_2, y_2),]

如果您的坐標未分類,則只能通過首先在緯度上進行過濾來確定(latitude,longitude) ,您的搜索只會稍微改善一下

球體的1度緯度為111.2 km或69英里

但這不會帶來巨大的加速。

如果您airport_lat >= point_lat-tolerance緯度對機場進行排序,那么您可以使用二分搜索來找到可以匹配的第一個機場( airport_lat >= point_lat-tolerance ),然后只比較最后一個可以匹配的airport_lat <= point_lat+toleranceairport_lat <= point_lat+tolerance ) - 但要注意0度等於360.雖然你不能直接使用該庫,但是bisect的來源是實現二進制搜索的良好開端。

雖然從技術上講這種方式搜索仍然是O(n),但實際距離計算(取決於公差)和緯度比較很少。 所以你將獲得巨大的加速。

@Juddling 的答案很好,但是 KDTree 不支持半正弦距離,它更適合經緯度坐標。 對於半正弦距離,您可以使用 BallTree。 請注意,您需要先將坐標轉換為弧度。

from math import radians
from sklearn.neighbors import BallTree
import numpy as np

airports = [(10,10),(20,20),(30,30),(40,40)]
airports_rad = np.array([[radians(x[0]), radians(x[1])] for x in airports ])
tree = BallTree(airports_rad , metric = 'haversine')
result = tree.query([(radians(21),radians(21))])
print(result)

(array([[0.02391369]]), array([[1]], dtype=int64))

要將距離轉換為米,您需要乘以地球半徑(以米為單位)。

earth_radius = 6371000 # meters in earth
print(result[0][0] * earth_radius)
[152354.11114795]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM