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

如何加速Python中数十万组合记录的DataFrame匹配嵌套循环

问题背景

我有两个DataFrame:GL_df和RE_df,需要执行以下匹配逻辑:

  1. 找出两表中ISMATCHED字段值为0的ID的所有可能组合,单组合内最多包含4个ID,且组合金额和不为0;
  2. 筛选规则:跳过已匹配(ISMATCHED=1)的记录;组合内所有记录的年月(格式%m/%Y)需唯一,且GL_df组合与RE_df组合的年月一致;当GL_df组合金额和与RE_df组合金额和的绝对值之和≤Variance(值为1)时,标记这些记录为匹配并跳过后续匹配。

原代码逻辑符合预期,但执行耗时长达数小时,需优化至秒级或分钟级。

原代码性能瓶颈分析
  • 组合爆炸:使用itertools.combinations生成所有1-4元组合,当未匹配ID数量较多时,组合数呈指数级增长(例如100个ID的4元组合可达392万+),直接导致后续循环量剧增。
  • 重复DataFrame查询:每次循环多次查询GL_df/RE_df获取ISMATCHED、日期、金额信息,这类O(n)操作重复执行会累积大量耗时。
  • 嵌套循环遍历:双重循环遍历所有GL和RE组合,时间复杂度为O(M*N),当组合数达百万级时完全无法高效执行。
优化方案

核心思路为缩小匹配范围+预计算+哈希快速查找,具体步骤如下:

  1. 按年月分组处理,避免跨年月的无效比对;
  2. 预生成分组内的有效组合并计算金额和,减少重复计算;
  3. 用哈希表存储RE侧组合的金额信息,实现GL组合的快速匹配;
  4. 批量标记已匹配记录,实时移除待匹配集合中的已匹配ID,避免重复处理。
优化后的代码
import pandas as pd
import numpy as np
from itertools import combinations

Max_per_com = 4
Variance = 1

# 预处理:添加年月列,过滤未匹配数据
GL_df['YEAR_MONTH'] = GL_df['GL_DATE'].dt.strftime('%m/%Y')
RE_df['YEAR_MONTH'] = RE_df['RE_DATE'].dt.strftime('%m/%Y')

gl_unmatched = GL_df[GL_df['ISMATCHED'] == 0].copy()
re_unmatched = RE_df[RE_df['ISMATCHED'] == 0].copy()

# 按年月分组处理
for ym in gl_unmatched['YEAR_MONTH'].unique():
    # 获取当前年月的未匹配数据
    gl_group = gl_unmatched[gl_unmatched['YEAR_MONTH'] == ym]
    re_group = re_unmatched[re_unmatched['YEAR_MONTH'] == ym]
    
    if len(gl_group) == 0 or len(re_group) == 0:
        continue
    
    # 生成GL侧有效组合:预存组合ID列表和金额和
    gl_combinations = []
    gl_id_amount = gl_group.set_index('GL_ID')['GL_AMOUNT'].to_dict()
    
    for k in range(1, Max_per_com+1):
        for combo in combinations(gl_id_amount.keys(), k):
            sum_amt = sum(gl_id_amount[id] for id in combo)
            if sum_amt != 0:
                gl_combinations.append((combo, sum_amt))
    
    # 生成RE侧组合并构建哈希映射:键为金额值,值为对应组合列表
    re_combo_map = {}
    re_id_amount = re_group.set_index('RE_ID')['RE_AMOUNT'].to_dict()
    
    for k in range(1, Max_per_com+1):
        for combo in combinations(re_id_amount.keys(), k):
            sum_amt = sum(re_id_amount[id] for id in combo)
            if sum_amt != 0:
                if sum_amt not in re_combo_map:
                    re_combo_map[sum_amt] = []
                re_combo_map[sum_amt].append(combo)
    
    # 遍历GL组合,查找匹配的RE组合
    matched_gl_ids = set()
    matched_re_ids = set()
    
    for gl_combo, gl_sum in gl_combinations:
        # 跳过包含已匹配ID的组合
        if any(id in matched_gl_ids for id in gl_combo):
            continue
        
        # 计算符合条件的RE金额范围:abs(gl_sum + re_sum) ≤1 → re_sum ∈ [-gl_sum-1, -gl_sum+1]
        target_min = -gl_sum - Variance
        target_max = -gl_sum + Variance
        
        # 在哈希映射中查找符合范围的RE组合
        for re_sum in re_combo_map:
            if target_min <= re_sum <= target_max:
                for re_combo in re_combo_map[re_sum]:
                    if any(id in matched_re_ids for id in re_combo):
                        continue
                    # 最终确认金额符合条件
                    if abs(gl_sum + re_sum) <= Variance:
                        # 批量标记匹配
                        GL_df.loc[GL_df['GL_ID'].isin(gl_combo), 'ISMATCHED'] = 1
                        RE_df.loc[RE_df['RE_ID'].isin(re_combo), 'ISMATCHED'] = 1
                        GL_df.loc[GL_df['GL_ID'].isin(gl_combo), 'MATCH_TABLE'] = 'tbl$matched$entry'
                        GL_df.loc[GL_df['GL_ID'].isin(gl_combo), 'MATCH_ID'] = str(re_combo)
                        
                        # 更新已匹配ID集合
                        matched_gl_ids.update(gl_combo)
                        matched_re_ids.update(re_combo)
                        break
                else:
                    continue
                break
    
    # 更新未匹配数据集,移除已匹配ID
    gl_unmatched = gl_unmatched[~gl_unmatched['GL_ID'].isin(matched_gl_ids)]
    re_unmatched = re_unmatched[~re_unmatched['RE_ID'].isin(matched_re_ids)]
额外优化建议
  • 金额整数化:若金额为小数,可先乘以100转为整数(避免浮点精度问题),匹配完成后再转回,提升哈希映射的精准度。
  • 优先小组合匹配:先处理1元组合(一对一匹配),再依次处理2元、3元、4元组合,快速减少待处理ID数量。
  • 并行分组处理:若年月分组较多,可使用multiprocessing对不同年月分组并行处理,进一步缩短耗时。

内容的提问来源于stack exchange,提问作者Sabarish R

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 19:45:50