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

问题描述 投票:0回答:4

我正在努力计算多组纬度和经度坐标之间的距离。简而言之,我发现了许多使用数学或几何学的教程。当我只想找到一组坐标(或两个唯一位置)之间的距离时,这些教程非常有用。然而,我的目标是扫描具有 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)
python numpy pandas distance geopy
4个回答
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
DataFrame 查找“结束”坐标,然后将 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[]
 方法指的是 
索引标签、行标签。然而,我们的内部 apply 函数(见上文)用检索到的值填充列

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

geopy

 值 (IIRC) 以公里为单位返回,因此您可能需要使用 
.meters
.miles
 等将这些值转换为您想要使用的任何单位。

类似以下内容应该有效:

def units(input_instance): return input_instance.meters distances_meters = distances.applymap(units)
您现在可以使用例如索引到距离矩阵中

loc[row_index, column_index]

。
您应该能够相当轻松地适应上述内容。您可能需要调整 
apply
 函数中的 
get_distance
 调用,以确保将正确的值传递给 
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
    

3
投票
我最近不得不做类似的工作,我结束了编写一个解决方案,我认为很容易理解并根据您的需求进行调整,但可能不是最好/最快的:

解决方案

这与 urschrei 发布的内容非常相似:假设您想要 Pandas DataFrame 中每两个连续坐标之间的距离,我们可以编写一个函数来处理每对点作为

pathstartfinish ,计算距离,然后构造一个新的 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 个坐标的距离数据。


0
投票
这可以通过地图来实现

import geopy.distance df['dist_origin_dest'] = list(map(geopy.distance.geodesic, df.loc[:, ["lat1", "lon1"]], df.loc[:, ["lat2", "lon2"]]))
    
© www.soinside.com 2019 - 2024. All rights reserved.