在Python中计算*多个*地理坐标之间的距离

10

我正在尝试计算多组纬度和经度坐标之间的距离。简单来说,我已经找到了许多使用数学或geopy的教程。当我只想找到一个坐标集(或两个唯一位置)之间的距离时,这些教程非常有用。然而,我的目标是扫描具有400k个起点和终点坐标组合的数据集。下面列出了我使用过的代码示例,但是在我的数组大于1个记录时似乎会出现错误。非常感谢任何有用的提示。谢谢。

# starting dataframe is df

lat1 = df.lat1.as_matrix()
long1 = df.long1.as_matrix()
lat2 = df.lat2.as_matrix()
long2 = df.df_long2.as_matrix()

from geopy.distance import vincenty
point1 = (lat1, long1)
point2 = (lat2, long2)
print(vincenty(point1, point2).miles)

请确认:您有一个坐标对的长列表,并且想要计算每一对之间的距离? - Scott Hunter
你的数据是如何存储的?可能有一种循环围绕着这段代码吗?如果没有,你希望它重复400k次的方式是什么? - Trev Davies
你可以使用KDTree算法,这样你就不必计算所有成对之间的距离。也许这个答案可以给你一些启示 - Saullo G. P. Castro
3个回答

8

编辑:这里有一个简单的笔记本示例

一般情况下,假设您有一个包含点的DataFrame列,并且您想要计算它们之间的距离(如果您有单独的列,请首先将它们合并为(lon,lat)元组)。将新列命名为coords

import pandas as pd
import numpy as np
from geopy.distance import vincenty


# assumes your DataFrame is named df, and its lon and lat columns are named lon and lat. Adjust as needed.
df['coords'] = zip(df.lat, df.lon)
# first, let's create a square DataFrame (think of it as a matrix if you like)
square = pd.DataFrame(
    np.zeros(len(df) ** 2).reshape(len(df), len(df)),
    index=df.index, columns=df.index)

这个函数会从 df 数据框中使用输入的列名称查找我们的“结束”坐标,然后对输入列中的每一行应用 geopy 的 vincenty() 函数,使用 square.coords 列作为第一个参数。之所以能够工作,是因为该函数在应用时按列从右向左处理

def get_distance(col):
    end = df.ix[col.name]['coords']
    return df['coords'].apply(vincenty, args=(end,), ellipsoid='WGS-84')

现在我们准备计算所有距离。
我们对DataFrame进行转置(.T),因为我们将使用loc[]方法来检索距离,该方法引用索引标签、行标签。然而,我们上面的内部应用函数会用检索到的值填充列。
distances = square.apply(get_distance, axis=1).T

你的 geopy 值(如果我没有记错)以公里为单位返回,因此你可能需要使用 .meters, .miles 等将其转换为所需的单位。
以下类似的代码应该可以解决问题:
def units(input_instance):
    return input_instance.meters

distances_meters = distances.applymap(units)

您现在可以使用例如loc[row_index, column_index]来索引您的距离矩阵。您应该能够相对容易地进行适应。您可能需要调整get_distance函数中apply调用以确保将正确的值传递给great_circle。Pandas的apply文档可能会有所帮助,特别是关于使用args传递位置参数的部分(这需要一个较新的pandas版本才能工作)。
此代码未经过性能测试,可能还有更快的方法来完成它,但对于400k个距离计算而言,它应该相当快。
另外,我记不清geopy是期望坐标为(lon, lat)还是(lat, lon)了。我打赌是后者(叹息)。
更新: 以下是截至2021年5月的可行脚本。
import geopy.distance
# geopy DOES use latlon configuration
df['latlon'] = list(zip(df['lat'], df['lon']))
square = pd.DataFrame(
    np.zeros((df.shape[0], df.shape[0])),
    index=df.index, columns=df.index
)

# replacing distance.vicenty with distance.distance
def get_distance(col):
    end = df.loc[col.name, 'latlon']
    return df['latlon'].apply(geopy.distance.distance,
                              args=(end,),
                              ellipsoid='WGS-84'
                             )

distances = square.apply(get_distance, axis=1).T

1
代码需要进行一些修复,因为geopy和pandas发生了变化。vincenty需要被替换为distance。而在pandas中分配值的语法现在是:df.loc[col.name, 'coords'] - BodoB

3

我最近也做了类似的工作,我编写了一个我认为非常容易理解和调整以满足您需求的解决方案,但可能不是最佳/最快的:

解决方案

它与urschrei发布的内容非常相似:假设您想从Pandas DataFrame中获得每两个连续坐标之间的距离,我们可以编写一个函数来处理每对点作为一条路径的起点终点,计算距离,然后构建一个新的DataFrame来返回:

import pandas as pd
from geopy import Point, distance
   
def get_distances(coords: pd.DataFrame,
                  col_lat='lat',
                  col_lon='lon',
                  point_obj=Point) -> pd.DataFrame:
    traces = len(coords) -1
    distances = [None] * (traces)
    for i in range(traces):
        start = point_obj((coords.iloc[i][col_lat], coords.iloc[i][col_lon]))
        finish = point_obj((coords.iloc[i+1][col_lat], coords.iloc[i+1][col_lon]))
        distances[i] = {
            'start': start,
            'finish': finish,
            'path distance': distance.geodesic(start, finish),
        }

    return pd.DataFrame(distances)

使用示例

coords = pd.DataFrame({
    'lat': [-26.244333, -26.238000, -26.233880, -26.260000, -26.263730],
    'lon': [-48.640946, -48.644670, -48.648480, -48.669770, -48.660700],
})

print('-> coords DataFrame:\n', coords)
print('-'*79, end='\n\n')

distances = get_distances(coords)
distances['total distance'] = distances['path distance'].cumsum()
print('-> distances DataFrame:\n', distances)
print('-'*79, end='\n\n')

# Or if you want to use tuple for start/finish coordinates:
print('-> distances DataFrame using tuples:\n', get_distances(coords, point_obj=tuple))
print('-'*79, end='\n\n')

输出示例

-> coords DataFrame:
          lat        lon
0 -26.244333 -48.640946
1 -26.238000 -48.644670
2 -26.233880 -48.648480
3 -26.260000 -48.669770
4 -26.263730 -48.660700
------------------------------------------------------------------------------- 

-> distances DataFrame:
                                   start                             finish  \
0  26 14m 39.5988s S, 48 38m 27.4056s W   26 14m 16.8s S, 48 38m 40.812s W   
1      26 14m 16.8s S, 48 38m 40.812s W  26 14m 1.968s S, 48 38m 54.528s W   
2     26 14m 1.968s S, 48 38m 54.528s W     26 15m 36s S, 48 40m 11.172s W   
3        26 15m 36s S, 48 40m 11.172s W  26 15m 49.428s S, 48 39m 38.52s W   

           path distance         total distance  
0  0.7941932910049856 km  0.7941932910049856 km  
1  0.5943709651000332 km  1.3885642561050187 km  
2  3.5914909016938505 km   4.980055157798869 km  
3  0.9958396130609087 km   5.975894770859778 km  
------------------------------------------------------------------------------- 

-> distances DataFrame using tuples:
                       start                  finish         path distance
0  (-26.244333, -48.640946)    (-26.238, -48.64467)  0.7941932910049856 km
1      (-26.238, -48.64467)  (-26.23388, -48.64848)  0.5943709651000332 km
2    (-26.23388, -48.64848)     (-26.26, -48.66977)  3.5914909016938505 km
3       (-26.26, -48.66977)   (-26.26373, -48.6607)  0.9958396130609087 km
------------------------------------------------------------------------------- 


1
截至5月19日
对于任何使用多个地理位置数据的人,您可以调整上述代码,但稍微修改一下以读取您数据驱动器中的CSV文件。该代码将在标记文件夹中写入输出距离。
import pandas as pd
from geopy import Point, distance
def get_distances(coords: pd.DataFrame,
   col_lat='lat',
              col_lon='lon',
              point_obj=Point) -> pd.DataFrame:
traces = len(coords) -1
distances = [None] * (traces)
for i in range(traces):
    start = point_obj((coords.iloc[i][col_lat], coords.iloc[i][col_lon]))
    finish = point_obj((coords.iloc[i+1][col_lat], coords.iloc[i+1][col_lon]))
    distances[i] = {
        'start': start,
        'finish': finish,
        'path distance': distance.geodesic(start, finish),
    }
output = pd.DataFrame(distances)
output.to_csv('geopy_output.csv')
return output

我使用相同的代码为50,000多个坐标生成了距离数据。


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