如何在查询半径-BallTree sklearn中引入半径,单位为弧度或公里?

Dat*_*DMS 4 python nearest-neighbor haversine

我正在处理纬度和经度数据。我使用了 BallTree,因为数据集中有很多行(32000 行)。如果我用半正矢距离构建树:

model_BTree = BallTree(np.array(points_sec_rad), metric='haversine')
Run Code Online (Sandbox Code Playgroud)

我将纬度和经度转换为弧度单位,如何将 query_radius (max_dist_rad) 应用于我想要定位的点?我使用 0.150 米作为半径,但我不确定是否应该使用弧度近似值。

ind_BTree, dist_BTree = model_BTree.query_radius(np.array(points_loc_rad), r=max_dist_rad, return_distance=True, sort_results=True)
Run Code Online (Sandbox Code Playgroud)

另外,如何限制半径内邻居的数量?谢谢

Wil*_*iks 8

编辑:带有工作代码和解释的示例

可视化应用半正矢距离所发生情况的最佳方法是可视化所有大圆距离都是在小乒乓球上测量的。

如果您想应用于query_radius()更大的球体,例如地球,您需要将地球公里/英里转换回单位乒乓球体。假设您想要 100 英里,您需要除以以英里为单位的地球半径。的输出query_radius()需要通过乘法再次转换回英里/公里。

假设我们在 Pandas 中有以下城镇和博物馆数据:

import pandas as pd
import numpy as np

from sklearn.neighbors import BallTree
Run Code Online (Sandbox Code Playgroud)
towns = pd.DataFrame({
    "name" : ["Merry Hill", "Spring Valley", "Nesconset"],
    "lat" : [36.01, 41.32, 40.84],
    "long" : [-76.7, -89.20, -73.15]
})

museum = pd.DataFrame({
    "name" : ["Motte Historical Car Museum, Menifee", "Crocker Art Museum, Sacramento", "World Chess Hall Of Fame, St.Louis", "National Atomic Testing Museum, Las", "National Air and Space Museum, Washington", "The Metropolitan Museum of Art", "Museum of the American Military Family & Learning Center"],
    "lat" : [33.743511, 38.576942, 38.644302, 36.114269, 38.887806, 40.778965, 35.083359],
    "long" : [-117.165161, -121.504997, -90.261154, -115.148315, -77.019844, -73.962311, -106.381531]
})
Run Code Online (Sandbox Code Playgroud)

我们需要将纬度/经度对提取为numpy数组

places_gps = towns[["lat", "long"]].values
museum_gps = museum[["lat", "long"]].values
Run Code Online (Sandbox Code Playgroud)

现在我们可以创建球树

places_radians =  np.radians(places_gps)
museum_radians = np.radians(museum_gps)

tree = BallTree(museum_radians, leaf_size=15, metric='haversine')
Run Code Online (Sandbox Code Playgroud)

再次想象这个小球只有乒乓球大小。要将它们用于更大/更小的球体,我们需要乘法/除法。

假设我想要 100 英里内的所有博物馆;

distance_in_miles = 100
earth_radius_in_miles = 3958.8
    
radius = distance_in_miles / earth_radius_in_miles
Run Code Online (Sandbox Code Playgroud)

现在我可以申请query_radius(),并记住返回的距离需要转换回英里。这distances是单位球体(我们的乒乓球)上的大圆距离。

is_within, distances = tree.query_radius(places_radians, r=radius, count_only=False, return_distance=True) 
Run Code Online (Sandbox Code Playgroud)

所以我们

distances_in_miles = distances * earth_radius_in_miles
Run Code Online (Sandbox Code Playgroud)

让我们检查输出,我们看到distances_in_miles

array([array([], dtype=float64), array([], dtype=float64),
       array([42.68960475])], dtype=object)
Run Code Online (Sandbox Code Playgroud)

这意味着“Nesconset”距离“大都会艺术博物馆”应该 < 100 英里,并且这个距离约为 42.689 英里。请注意,最后一个数组 (Nesconset) 确实只返回了一个距离,在它的帮助下,is_within我们找到了 5 以内的博物馆索引,即museum.name[5]“大都会艺术博物馆”。

根据检查方法的不同,它不会是精确的 42.689 英里,但通过 Google 地图快速检查后确认它大约在该范围内。地球并不是一个完美的球体,因此会有误差。

就像我原来的帖子一样,很容易犯错误,因为忘记应用校正因子、交换纬度/经度值或公里/米。