基于经纬度与迭代在Pandas中匹配最近行的问题排查
解决方案:修复Haversine距离函数并实现站点-气象数据匹配
错误原因分析
你遇到的TypeError是因为自定义的haversine函数参数格式不符合scipy.cdist的要求。cdist会将两个坐标数组的每一对点作为两个一维数组传入距离函数(比如u代表第一个点的[lat1, lon1],v代表第二个点的[lat2, lon2]),但你之前的函数可能期望接收四个单独的lat1, lon1, lat2, lon2参数,导致参数缺失报错。
步骤1:修正Haversine距离函数
重新定义适配cdist的距离函数,参数为两个一维坐标数组:
import numpy as np from scipy.spatial.distance import cdist def haversine(u, v): # u: 第一个点的[纬度, 经度], v: 第二个点的[纬度, 经度] lat1, lon1 = u lat2, lon2 = v # 转换为弧度(Haversine公式要求) lat1, lon1, lat2, lon2 = map(np.radians, [lat1, lon1, lat2, lon2]) # 计算Haversine距离 dlat = lat2 - lat1 dlon = lon2 - lon1 a = np.sin(dlat/2)**2 + np.cos(lat1) * np.cos(lat2) * np.sin(dlon/2)**2 c = 2 * np.arcsin(np.sqrt(a)) earth_radius_km = 6371 # 地球平均半径(公里) return c * earth_radius_km
步骤2:实现分组匹配逻辑
针对df1的每个num和valid_time分组,为df2的每个站点找到组内最近的气象数据行:
基础实现(基于scipy.cdist)
import pandas as pd # 提取df2的站点经纬度数组 stations_coords = df2[['station_lat', 'station_lon']].values def match_nearest(group): # 提取当前组的气象数据经纬度数组 group_coords = group[['lat', 'lon']].values # 计算组内所有点与所有站点的距离矩阵 dist_matrix = cdist(group_coords, stations_coords, metric=haversine) # 对每个站点,找到组内距离最小的行索引 nearest_idx = dist_matrix.argmin(axis=0) # 获取最近行并关联站点信息 nearest_rows = group.iloc[nearest_idx].reset_index(drop=True) return pd.concat([df2.reset_index(drop=True), nearest_rows], axis=1) # 按num和valid_time分组处理,合并结果得到df3 df3 = df1.groupby(['num', 'valid_time'], group_keys=False).apply(match_nearest)
优化实现(大数据量场景,基于sklearn BallTree)
如果df1数据量较大,cdist的全量距离计算效率较低,可使用BallTree做空间索引加速最近邻查找:
from sklearn.neighbors import BallTree def match_nearest_optimized(group): # 转换经纬度为弧度(BallTree的Haversine metric要求) group_coords_rad = np.radians(group[['lat', 'lon']].values) stations_coords_rad = np.radians(stations_coords) # 创建BallTree索引 tree = BallTree(group_coords_rad, metric='haversine') # 查询每个站点的最近邻(k=1表示只取最近的一个) distances_rad, nearest_idx = tree.query(stations_coords_rad, k=1) # 获取最近行并关联站点信息 nearest_rows = group.iloc[nearest_idx.flatten()].reset_index(drop=True) result = pd.concat([df2.reset_index(drop=True), nearest_rows], axis=1) # 将弧度距离转换为公里 result['distance_km'] = distances_rad.flatten() * 6371 return result df3 = df1.groupby(['num', 'valid_time'], group_keys=False).apply(match_nearest_optimized)
验证结果
生成的df3会包含df2的所有站点信息,以及对应每个num和valid_time下距离该站点最近的df1气象数据行,同时可保留距离字段用于验证。
内容的提问来源于stack exchange,提问作者user2100039
相关产品推荐
相关产品推荐

