1

我有一个包含对象及其坐标的 DataFrame:

      id        lat         lng
0   3816  18.384001  -66.114799
1   5922  20.766100 -156.434998
2   1527  21.291394 -157.843085
3   1419  21.291394 -157.843085
4   1651  21.291394 -157.843085

多个对象可以有相同的坐标。数据框很大(数百万条记录)。我有一个带坐标的目标点(target_lat, target_lng)。我的目标是尽可能高效地在数据框中找到距离目标点 X 英里以内的对象。

我正在使用haversine_np改编自这个问题的功能。它接受参数并有效地计算(两个系列)和(两个数字)(lat_series, lng_series, lat, lng)之间的所有距离。lat_series, lng_series(lat, lng)

现在我的问题是如何使用它来过滤距离并选择原始数据框中的对象。

这是我目前的解决方案:

grouper = df.groupby(['lat', 'lng'], sort=False).grouper
lat_series = grouper.result_index.get_level_values(0)  # lats of unique (lat, lng) pairs
lng_series = grouper.result_index.get_level_values(1)  # lngs of unique (lat, lng) pairs
df['location_index'] = grouper.group_info[0]  # assign index of group back to df
distances = haversine_np(lat_series, lng_series, target_lat, target_lng)
mask = distances <= 50  # let's say 50 miles; boolean mask of size = ngroups
loc_indexes = pd.Series(range(grouper.ngroups))[mask]  # select group indexes by mask
df[df.location_index.isin(loc_indexes)]  # select original records by group indexes

它似乎有效,虽然看起来不可靠,因为当我使用 选择相关的组索引时pd.Series(range(grouper.ngroups))[mask],我假设分组的级别值是自然索引的(从 0 到 ngroups-1)。换句话说,我依赖于i-th元素 ingrouper.result_index.get_level_values()对应于带有标签iin的组这一事实grouper.group_info[0]。我找不到更明确的方法来获取该映射。

问题:

  1. 我使用的方法可靠吗?
  2. 有没有更好(更安全/更简洁/更高效)的方法?
4

2 回答 2

1

更新: @DennisGolomazov 发现这种“预过滤”不适用于经度,这是一个很好的例子- 这是一个小演示:

In [115]: df
Out[115]:
     id   lat    lng
5  4444  40.0 -121.0
0  1111  40.0 -120.0

In [116]: %paste
threshold = 60
max_lng_factor = 69.17
max_lat_factor = 69.41
target_lat, target_lng = 40, -120
mask = df.lat.sub(target_lat).abs().le(threshold/max_lat_factor) \
       & \
       df.lng.sub(target_lng).abs().le(threshold/max_lng_factor)
x = df.loc[mask, ['lat','lng']].drop_duplicates()
## -- End pasted text --

In [117]: x
Out[117]:
    lat    lng
0  40.0 -120.0

这两个坐标之间的距离小于我们的阈值(60 英里):

In [119]: haversine_np(-120, 40, -121, 40)
Out[119]: 52.895043596886239

结论:我们可以预过滤纬度,但不能预过滤经度:

In [131]: df
Out[131]:
     id   lat    lng
5  4444  40.0 -121.0
0  1111  40.0 -120.0
1  2222  42.0 -121.0

正确的预过滤:

In [132]: mask = df.lat.sub(target_lat).abs().le(threshold/max_lat_factor)
     ...: x = df.loc[mask, ['lat','lng']].drop_duplicates()
     ...:

In [133]: x
Out[133]:
    lat    lng
5  40.0 -121.0
0  40.0 -120.0

查看:

In [135]: df.reset_index() \
     ...:   .merge(x.assign(distance=haversine_np(x.lng, x.lat, target_lng, target_lat))
     ...:           .query("distance <= @threshold"),
     ...:          on=['lat','lng'])
     ...:
Out[135]:
   index    id   lat    lng   distance
0      5  4444  40.0 -121.0  52.895044
1      0  1111  40.0 -120.0   0.000000

旧的,部分不正确的答案:

我会尝试进行预过滤以优化计算。例如,您可以轻松过滤掉绝对超出“感兴趣的矩形”的点。

演示:

threshold = 100

# http://gis.stackexchange.com/questions/142326/calculating-longitude-length-in-miles/142327#142327
max_lng_factor = 69.17
max_lat_factor = 69.41

target_lat, target_lng = 21.29, -157.84

mask = df.lat.sub(target_lat).abs().le(threshold/max_lat_factor) \
       & \
       df.lng.sub(target_lng).abs().le(threshold/max_lng_factor)

x = df.loc[mask, ['lat','lng']].drop_duplicates()

df.reset_index() \
  .merge(x.assign(distance=haversine_np(x.lng, x.lat, target_lng, target_lat))
          .query("distance <= @threshold"),
         on=['lat','lng']) \
  .drop('distance',1) \
  .set_index('index')

结果:

In [142]: df.reset_index() \
     ...:   .merge(x.assign(distance=haversine_np(x.lng, x.lat, target_lng, target_lat))
     ...:           .query("distance <= @threshold"),
     ...:          on=['lat','lng']) \
     ...:   .drop('distance',1) \
     ...:   .set_index('index')
     ...:
Out[142]:
         id        lat         lng
index
1      5922  20.766100 -156.434998
2      1527  21.291394 -157.843085
3      1419  21.291394 -157.843085
4      1651  21.291394 -157.843085
于 2017-04-08T12:37:46.903 回答
0

也许我在效率方面遗漏了一些东西,但我不明白你为什么使用 .grouper 方法。要获取 Lat 和 Long 系列只需引用它们,即 df['lat'] 或 df.lat,然后您可以直接计算距离

distances = haversine_np(df.lat, df.lng, target_lat, target_lng)

并创建一个面具

mask = distances <= 50

掩码现在已索引到数据帧。

df[mask]

将仅提供 True 元素。

于 2017-04-08T13:43:19.043 回答