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

如何高效实现Pandas中基于地理区域的DataFrame模糊匹配合并

高效实现带地理层级与日期约束的左连接方案

针对你遇到的地理区域匹配不一致、性能低下的问题,可通过区域标准化+分层级优先级匹配的方案替代apply,大幅提升效率,同时满足所有匹配规则:

一、预处理:统一区域命名与层级标记

首先解决区域命名不一致的问题,同时明确每条数据的区域层级:

  1. 区域标准化:
    • 对两张表的省、市、区列做统一清洗,去掉冗余后缀,用映射字典修正别名。示例代码:
      # 定义标准化映射
      province_map = {"北京市": "北京", "上海市": "上海", "京": "北京", "沪": "上海"}
      # 应用到DataFrame
      left_df['province_std'] = left_df['province'].replace(province_map).str.replace(r'省|自治区|特别行政区', '', regex=True)
      right_df['province_std'] = right_df['province'].replace(province_map).str.replace(r'省|自治区|特别行政区', '', regex=True)
      # 市、区列做同样处理
      left_df['city_std'] = left_df['city'].str.replace(r'市|地区|自治州', '', regex=True).fillna('')
      right_df['city_std'] = right_df['city'].str.replace(r'市|地区|自治州', '', regex=True).fillna('')
      left_df['district_std'] = left_df['district'].str.replace(r'区|县|旗', '', regex=True).fillna('')
      right_df['district_std'] = right_df['district'].str.replace(r'区|县|旗', '', regex=True).fillna('')
      
  2. 标记区域层级:
    • 给每条数据标记层级,方便后续匹配逻辑判断:
      def get_level(row):
          if row['district_std']:
              return '三级'
          elif row['city_std']:
              return '二级'
          else:
              return '一级'
      left_df['level'] = left_df.apply(get_level, axis=1)
      right_df['level'] = right_df.apply(get_level, axis=1)
      
  3. 统一日期格式:确保两张表的日期列转为datetime类型:
    left_df['left_date'] = pd.to_datetime(left_df['date'])
    right_df['right_date'] = pd.to_datetime(right_df['date'])
    

二、分层级优先级匹配(核心逻辑)

按精确层级优先的顺序匹配,每次匹配后标记已匹配的行,避免重复计算,同时严格满足right_df.date <= left_df.date的条件:

1. 初始化匹配标记与结果容器

left_df['matched'] = False
result = pd.DataFrame(columns=left_df.columns.tolist() + ['right_id', 'right_listed_date'])

2. 三级匹配(省+市+区完全对应)

优先匹配最精确的三级区域,仅保留日期符合条件的记录:

# 筛选未匹配的三级数据
left_unmatched = left_df[~left_df['matched'] & (left_df['level'] == '三级')]
# 匹配right_df中对应的三级数据
match_3 = pd.merge(
    left_unmatched,
    right_df[right_df['level'] == '三级'],
    left_on=['province_std', 'city_std', 'district_std'],
    right_on=['province_std', 'city_std', 'district_std'],
    how='left'
)
# 过滤日期条件
match_3 = match_3[match_3['right_date'] <= match_3['left_date']]
# 更新匹配标记
left_df.loc[match_3.index, 'matched'] = True
# 合并到结果
result = pd.concat([result, match_3[left_df.columns.tolist() + ['right_id', 'right_listed_date']]])

3. 二级匹配(省+市对应)

匹配次精确的二级区域,包括其中一方无区县信息的情况:

left_unmatched = left_df[~left_df['matched'] & (left_df['level'] == '二级')]
right_candidates = right_df[(right_df['level'] == '二级') | (right_df['district_std'].isna())]
match_2 = pd.merge(
    left_unmatched,
    right_candidates,
    left_on=['province_std', 'city_std'],
    right_on=['province_std', 'city_std'],
    how='left'
)
match_2 = match_2[match_2['right_date'] <= match_2['left_date']]
left_df.loc[match_2.index, 'matched'] = True
result = pd.concat([result, match_2[left_df.columns.tolist() + ['right_id', 'right_listed_date']]])

4. 一级匹配(省对应,排除中央)

匹配省级区域,包括其中一方无市、区县信息的情况:

left_unmatched = left_df[~left_df['matched'] & (left_df['level'] == '一级') & (left_df['province_std'] != '中央')]
right_candidates = right_df[(right_df['level'] == '一级') | (right_df['city_std'].isna())]
match_1 = pd.merge(
    left_unmatched,
    right_candidates,
    left_on=['province_std'],
    right_on=['province_std'],
    how='left'
)
match_1 = match_1[match_1['right_date'] <= match_1['left_date']]
left_df.loc[match_1.index, 'matched'] = True
result = pd.concat([result, match_1[left_df.columns.tolist() + ['right_id', 'right_listed_date']]])

5. 中央区域匹配(全国范围)

处理left_df中一级区域为“中央”的情况,匹配所有日期符合条件的right_df记录:

left_unmatched = left_df[~left_df['matched'] & (left_df['province_std'] == '中央')]
if not left_unmatched.empty:
    # 笛卡尔积后过滤日期
    match_center = pd.merge(left_unmatched, right_df, how='cross')
    match_center = match_center[match_center['right_date'] <= match_center['left_date']]
    result = pd.concat([result, match_center[left_df.columns.tolist() + ['right_id', 'right_listed_date']]])

三、最终整理结果

将结果按left_df原始索引排序,确保保留全量left_df数据(未匹配到的行对应right字段为NaN):

result = result.set_index(left_df.index.names).reindex(left_df.index)
# 清理临时列
result = result.drop(['matched', 'level'], axis=1, errors='ignore')

四、性能优化建议

  • 建立索引:对标准化后的区域列和日期列建立索引,大幅提升merge速度:
    left_df = left_df.set_index(['province_std', 'city_std', 'district_std'])
    right_df = right_df.set_index(['province_std', 'city_std', 'district_std'])
    
  • 批量处理:如果数据量超千万级,可使用dask.dataframe替代pandas,实现分布式计算
  • 避免冗余:预处理时过滤right_df中无效数据(如日期远大于left_df最大日期的记录),减少匹配数据量

内容的提问来源于stack exchange,提问作者Constantine Theodore

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 23:26:19