基于Haversine公式计算两个DataFrame最近邻距离的结果偏差排查
核心错误排查与修正方案
原代码存在的3处关键问题
- Haversine函数传参顺序错位
原函数定义的入参顺序为(lon1, lat1, lon2, lat2),即第一参数是点1的经度、第二参数是点1的纬度,但后续调用时传入的是row[['LATITUDE', 'LENGHT']]的顺序——也就是先传纬度值、再传经度值,直接把纬度当经度、经度当纬度代入公式计算,从根本上导致距离计算偏差。 - 最近距离提取逻辑缺失
原代码设置todos=True时,Top_nearest函数会返回和所有候选点位的距离数组,但代码中仅通过distances[0]取数组的第一个元素,也就是固定计算和Coordinates_coords第一行点位的距离,全程没有提取距离最小值的逻辑,完全不符合“找最近邻”的需求。 - 循环计算效率极低
依靠iterrows逐行遍历点位、嵌套循环计算点对距离的方式,计算耗时会随两个DataFrame行数的乘积线性增长,数千行规模下会出现明显的卡顿。
修正后可直接运行的实现代码
import numpy as np import pandas as pd # 修正入参顺序,匹配业务字段的"纬度在前、经度在后"的排列规则 def haversine_np(lat1, lon1, lat2, lon2): # 经纬度角度转弧度 lon1, lat1, lon2, lat2 = map(np.radians, [lon1, lat1, lon2, lat2]) dlon = lon2 - lon1 dlat = lat2 - lat1 a = np.sin(dlat/2.0)**2 + np.cos(lat1) * np.cos(lat2) * np.sin(dlon/2.0)**2 c = 2 * np.arcsin(np.sqrt(a)) km = 6371 * c return km # 批量计算最近邻,适配数千行的数据规模 def get_nearest_point_info(source_df, candidate_df): # 提取坐标数组,降低运算开销 source_coords = source_df[['LATITUDE', 'LENGHT']].values candidate_coords = candidate_df[['LATITUDE', 'LENGHT']].values # 利用numpy广播机制,一次性生成所有点对的距离矩阵 # source坐标维度扩展为(n, 1),candidate坐标扩展为(1, m),实现批量运算 s_lat = source_coords[:, 0][:, np.newaxis] s_lon = source_coords[:, 1][:, np.newaxis] c_lat = candidate_coords[:, 0][np.newaxis, :] c_lon = candidate_coords[:, 1][np.newaxis, :] # 生成距离矩阵,形状为(n, m),n为place_locations行数,m为Coordinates_coords行数 dist_matrix = haversine_np(s_lat, s_lon, c_lat, c_lon) # 提取每个源点的最近邻信息 nearest_candidate_idx = np.argmin(dist_matrix, axis=1) nearest_distance = np.min(dist_matrix, axis=1) nearest_candidate_name = candidate_df['PLACE'].values[nearest_candidate_idx] return nearest_distance, nearest_candidate_name # 调用方法 place_locations['nearest_distance_km'], place_locations['nearest_place'] = get_nearest_point_info( source_df=place_locations, candidate_df=Coordinates_coords )
修正后效果说明
- 距离计算严格匹配经纬度顺序,输出单位为公里,结果和标准Haversine公式计算值一致
- 取消逐行遍历的嵌套循环,依靠numpy广播实现批量运算,两个数千行规模的DataFrame可在数秒内完成全量计算
- 输出结果同时包含最近邻的距离值、对应候选点位的名称,可直接支撑业务分析
内容的提问来源于stack exchange,提问作者DS01
相关产品推荐
相关产品推荐

