Python 将函数应用于组,并过滤原始数据帧

Python 将函数应用于组,并过滤原始数据帧,python,pandas,dataframe,group-by,split-apply-combine,Python,Pandas,Dataframe,Group By,Split Apply Combine,我有一个包含对象及其坐标的数据框: 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 多个对象可以具有相同的坐标。数据帧很大(数百万条记录)。我有一个坐标为(tar

我有一个包含对象及其坐标的数据框:

      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\u lat,target\u lng)
的目标点。我的目标是尽可能高效地在数据帧中找到距离目标点X英里以内的对象

我正在使用改编自的
haversine\u np
函数。它采用参数
(lat\u系列,lng\u系列,lat,lng)
,并有效地计算
lat\u系列,lng\u系列
(两个系列)和
(lat,lng)
(两个数字)之间的所有距离

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

df[mask]
这是我当前的解决方案:

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
grouper=df.groupby(['lat','lng',sort=False)。grouper
lat_系列=grouper.result_index.get_level_值(0)#唯一(lat,lng)对的lat
lng_series=grouper.result_index.get_level_值(1)#唯一(lat,lng)对的lng
df['location_index']=grouper.group_info[0]#将组的索引分配回df
距离=haversine_np(lat_系列、lng_系列、target_lat、target_lng)
掩码=距离更新:-这里是一个小演示:

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 [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
[135]中的
:df.reset_index()\
…:.merge(x.assign(距离=haversine\u np(x.lng,x.lat,target\u lng,target\u lat))

…:.query(“distance也许我在效率方面遗漏了一些东西,但我不明白您为什么要使用.gropper方法。 要获得Lat和长序列,只需参考它们,即df['Lat']或df.Lat,然后您可以直接使用

distances = haversine_np(df.lat, df.lng, target_lat, target_lng)
并创建一个带有

mask = distances <= 50

将只提供真实的元素。

这里的问题是
lat/lng
列中有许多重复值,我只想计算唯一值的
haversine_np
。否则,是的,您的方法将是最简单的方法。其中一个问题是,1度经度仅在赤道,离两极越近,它越小,这将错误地排除距离足够近但经度足够远的点。如果我错了,请纠正我,但我认为这是错误的。最大因子是分母(
threshold/max\u lng\u factor
),所以因子最大的矩形实际上是最小的。但是靠近极点的矩形应该更大,所以因子应该更小(在极点处降到零)希望我的解释是有意义的。@ DennisGolomazov,我想你是对的。所以我们只能预过滤<代码>纬度<代码>,而不是<代码>经度< /代码>。这是一个例子。让<代码> TajixLAT,TajiTyLNG =(40,-120)。考虑<代码>点=(40,-121)。
threshold=60
。那么您的函数将不会选择此点,因为
threshold/max_lng_factor=0.867
df.lng.sub(target_lng).abs()=121-120=1
。但实际上,这些点之间的距离是
85km=52.8英里<60
,因此应该选择该点。@DennisGolomazov,是的,非常好的示例-谢谢!你完全正确!我将更正答案。。。
distances = haversine_np(df.lat, df.lng, target_lat, target_lng)
mask = distances <= 50
df[mask]