如何获取两个不同数据框中两个地理坐标之间的距离?

3

我正在为大学的一个项目工作,其中有两个pandas数据框:

      # Libraries
      import pandas as pd
      from geopy import distance

      # Dataframes

      df1 = pd.DataFrame({'id': [1,2,3],                   
                          'lat':[-23.48, -22.94, -23.22],
                          'long':[-46.36, -45.40, -45.80]})

       df2 = pd.DataFrame({'id': [100,200,300],                   
                           'lat':[-28.48, -22.94, -23.22],
                           'long':[-46.36, -46.40, -45.80]})

我需要计算数据框中地理纬度和经度坐标之间的距离。因此我使用了geopy。如果坐标组合之间的距离小于100米的阈值,则必须在“nearby”列中赋值为1。我编写了以下代码:
      threshold = 100  # meters

      df1['nearby'] = 0

      for i in range(0, len(df1)):
          for j in range(0, len(df2)):

              coord_geo_1 = (df1['lat'].iloc[i], df1['long'].iloc[i])
              coord_geo_2 = (df2['lat'].iloc[j], df2['long'].iloc[j])

              var_distance = (distance.distance(coord_geo_1, coord_geo_2).km) * 1000 

              if(var_distance < threshold):
                   df1['nearby'].iloc[i] = 1

虽然有警告出现,但代码仍在运行。不过我想找到一种方法来覆盖 for() 循环。这是可能的吗?

       # Output:

       id   lat       long  nearby
        1   -23.48  -46.36    0
        2   -22.94  -45.40    0
        3   -23.22  -45.80    1
2个回答

2
如果您可以使用scikit-learn库,方法haversine_distances可计算两组坐标之间的距离。因此,您将获得:
from sklearn.metrics.pairwise import haversine_distances

# variable in meter you can change
threshold = 100 # meters

# another parameter
earth_radius = 6371000  # meters

df1['nearby'] = (
    # get the distance between all points of each DF
    haversine_distances(
        # note that you need to convert to radiant with *np.pi/180
        X=df1[['lat','long']].to_numpy()*np.pi/180, 
        Y=df2[['lat','long']].to_numpy()*np.pi/180)
    # get the distance in meter
    *earth_radius
    # compare to your threshold
    < threshold
    # you want to check if any point from df2 is near df1
    ).any(axis=1).astype(int)

print(df1)

#    id    lat   long  nearby
# 0   1 -23.48 -46.36       0
# 1   2 -22.94 -45.40       0
# 2   3 -23.22 -45.80       1

编辑:OP要求使用geopy计算距离的版本,下面是一种方法。

df1['nearby'] = (np.array(
    [[(distance.distance(coord1, coord2).km)
      for coord2 in df2[['lat','long']].to_numpy()] 
     for coord1 in df1[['lat','long']].to_numpy()]
     ) * 1000 < threshold
).any(1).astype(int)

有没有办法只使用geopy库来使用这个逻辑呢?使用这个库是学科要求。 - valentim.kodak
我按照你建议的修改了代码,现在它完美地运行了。谢谢! - valentim.kodak
@valentim.kodak 很高兴它能正常工作,看一下编辑后的帖子,我将 *1000<threshold 移到了数组创建之外,这样这两个操作就可以以向量化的方式在整个数组上执行,而不是在每个单独的距离上执行。 - Ben.T
您在回复中包含的代码出现了错误。正确的应该是 'cood1 和 cood2',而不是 'coord_geo_1 和 coord_geo_2',对吗? - valentim.kodak
@valentim.kodak 是的,你说得对,复制粘贴错误了。 - Ben.T
显示剩余2条评论

1
您可以将两个数据框进行交叉合并,以获取df1中每个id与df2之间的距离:
dfm = pd.merge(df1, df2, how = 'cross', suffixes = ['','_2'])
dfm['dist'] = dfm.apply(lambda r: distance.distance((r['lat'],r['long']),(r['lat_2'],r['long_2'])).km * 1000 , axis=1)

dfm的样子如下:

      id     lat    long    id_2    lat_2    long_2      dist
--  ----  ------  ------  ------  -------  --------  --------
 0     1  -23.48  -46.36     100   -28.48    -46.36  553941
 1     1  -23.48  -46.36     200   -22.94    -46.4    59943.4
 2     1  -23.48  -46.36     300   -23.22    -45.8    64095.5
 3     2  -22.94  -45.4      100   -28.48    -46.36  621251
 4     2  -22.94  -45.4      200   -22.94    -46.4   102568
 5     2  -22.94  -45.4      300   -23.22    -45.8    51393.4
 6     3  -23.22  -45.8      100   -28.48    -46.36  585430
 7     3  -23.22  -45.8      200   -22.94    -46.4    68854.7
 8     3  -23.22  -45.8      300   -23.22    -45.8        0

你可以测试“dist”列是否低于阈值,但如果要按照df1中的id进行聚合,则可以执行以下操作:
res = df1.merge(dfm.groupby('id').apply(lambda g:any(g['dist'] < threshold)*1).rename('nearby'), on = 'id')

res现在看起来像这样:

      id     lat    long    nearby
--  ----  ------  ------  --------
 0     1  -23.48  -46.36         0
 1     2  -22.94  -45.4          0
 2     3  -23.22  -45.8          1

代码完美运行!存储坐标组合之间距离的想法很棒。我想知道是否有可能在 dfm 中仅保留具有最小距离的 'ids' 组合? - valentim.kodak
@valentim.kodak 最小的一对是 dfm.nsmallest(1,'dist')。如果按id最小,则为dfm.groupby('id').apply(lambda g: g.nsmallest(1,'dist')) - piterbarg
当我在完整数据集上运行时,出现以下错误:KeyError: 'cross' on line: dfm = pd.merge(df1, df2, how = 'cross', suffixes = ['','_2']) - valentim.kodak
在示例数据框中它可以完美地工作,但在原始数据框中出现错误:KeyError: 'cross'。 - valentim.kodak
是的,我将使用原始数据框的一部分进行测试。谢谢! - valentim.kodak
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接