You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 13:25:33