能否用pd.merge_asof匹配近似经纬度坐标?求可行方案
如何在不使用geopandas的情况下基于经纬度3-5英里范围合并两个DataFrame
关于pd.merge_asof的适用性
pd.merge_asof不能用于基于经纬度的合并,它的设计逻辑是针对**单一有序连续键(如时间戳)**的向前/向后近邻匹配,而经纬度是二维空间坐标,无法作为单一有序键满足其匹配逻辑,所以无论怎么调整参数都无法实现需求。
替代方案:纯Pandas+Haversine公式实现空间匹配
由于无法安装geopandas,我们可以用球面距离公式(Haversine)计算两点间的英里距离,再筛选出3-5英里范围内的匹配对。
步骤1:定义Haversine距离计算函数
这个函数可以将经纬度转换为两点间的球面英里距离:
import pandas as pd import numpy as np def haversine(lat1, lon1, lat2, lon2): # 转换为弧度 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_miles = 3956 return c * earth_radius_miles
步骤2:两种实现方式
假设你的两个DataFrame结构如下(示例):
# 示例DataFrame 1 df1 = pd.DataFrame({ 'id': [1, 2, 3], 'lat': [40.7128, 34.0522, 41.8781], 'lon': [-74.0060, -118.2437, -87.6298] }) # 示例DataFrame 2 df2 = pd.DataFrame({ 'place_id': [101, 102, 103], 'place_lat': [40.7150, 34.0580, 41.8820], 'place_lon': [-74.0080, -118.2450, -87.6320] })
方式1:逐行筛选(适合中等数据量)
先通过经纬度范围初步过滤(减少计算量),再计算精确距离:
matches = [] # 遍历df1的每个点 for _, row in df1.iterrows(): # 初步筛选:保留df2中在当前点±0.1度范围内的点(≈6.9英里,覆盖3-5英里需求) lat_min, lat_max = row['lat'] - 0.1, row['lat'] + 0.1 lon_min, lon_max = row['lon'] - 0.1, row['lon'] + 0.1 filtered_df2 = df2[ (df2['place_lat'].between(lat_min, lat_max)) & (df2['place_lon'].between(lon_min, lon_max)) ] if filtered_df2.empty: continue # 计算精确距离 filtered_df2['distance_miles'] = haversine( row['lat'], row['lon'], filtered_df2['place_lat'], filtered_df2['place_lon'] ) # 筛选3-5英里范围内的匹配 valid_matches = filtered_df2[(filtered_df2['distance_miles'] >= 3) & (filtered_df2['distance_miles'] <= 5)] # 关联df1的信息 valid_matches = valid_matches.assign(**row) matches.append(valid_matches) # 合并所有匹配结果 final_merge = pd.concat(matches, ignore_index=True) # 整理列顺序(按需调整) final_merge = final_merge[['id', 'lat', 'lon', 'place_id', 'place_lat', 'place_lon', 'distance_miles']]
方式2:向量化计算(适合小数据量)
直接生成所有点对的距离矩阵,再筛选有效匹配,效率更高但内存消耗大:
# 提取经纬度数组 lat1, lon1 = df1['lat'].values, df1['lon'].values lat2, lon2 = df2['place_lat'].values, df2['place_lon'].values # 生成所有点对的经纬度矩阵 lat1_mat, lat2_mat = np.meshgrid(lat1, lat2) lon1_mat, lon2_mat = np.meshgrid(lon1, lon2) # 计算所有点对的距离 distance_matrix = haversine(lat1_mat, lon1_mat, lat2_mat, lon2_mat) # 找到3-5英里范围内的点对索引 match_rows, match_cols = np.where((distance_matrix >= 3) & (distance_matrix <= 5)) # 构建最终匹配结果 final_merge = pd.DataFrame({ 'id': df1.iloc[match_cols]['id'].values, 'lat': df1.iloc[match_cols]['lat'].values, 'lon': df1.iloc[match_cols]['lon'].values, 'place_id': df2.iloc[match_rows]['place_id'].values, 'place_lat': df2.iloc[match_rows]['place_lat'].values, 'place_lon': df2.iloc[match_rows]['place_lon'].values, 'distance_miles': distance_matrix[match_rows, match_cols] })
优化建议
如果数据量极大,逐行遍历和向量化都会有性能问题,可以:
- 将经纬度按网格分块(比如每0.05度为一个网格),先匹配同一/相邻网格的点,再计算距离,大幅减少计算量。
- 对经纬度进行排序后,用二分查找缩小筛选范围,进一步优化效率。
内容的提问来源于stack exchange,提问作者ASH
相关产品推荐
相关产品推荐

