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

Pandas/Pyspark中对比两个DataFrame并高效打标拆分行的方案

高效实现方案:基于矢量化区间匹配替代嵌套循环

针对百万级数据的性能瓶颈,核心思路是利用pandas的矢量化操作、分组累积量计算和近似匹配,彻底避免嵌套循环。以下是分步实现方案:

1. 预处理与排序

先统一两个DataFrame的结构,计算组内累积数量(用于区间匹配),同时标记数据来源:

import pandas as pd
import numpy as np

# 对expected_orders预处理
expected_orders = expected_orders.sort_values(['country', 'product', 'date'])
expected_orders['start_cum'] = expected_orders.groupby(['country', 'product'])['qty'].cumsum().shift(fill_value=0)
expected_orders['end_cum'] = expected_orders.groupby(['country', 'product'])['qty'].cumsum()
expected_orders['source'] = 'expected'

# 对actual_orders预处理
actual_orders = actual_orders.sort_values(['country', 'product', 'date'])
actual_orders['start_cum'] = actual_orders.groupby(['country', 'product'])['qty'].cumsum().shift(fill_value=0)
actual_orders['end_cum'] = actual_orders.groupby(['country', 'product'])['qty'].cumsum()
actual_orders['source'] = 'actual'
  • start_cum/end_cum:标记当前行的数量在组内的累积区间(比如前3行总数量是100,当前行数量是50,则start_cum=100,end_cum=150)
  • 排序确保后续分组匹配的顺序正确

2. 分组区间匹配与打标

定义分组处理函数,用merge_asof实现矢量化的区间匹配,替代循环拆分:

def process_group(group):
    # 拆分当前组的expected和actual数据
    exp = group[group['source'] == 'expected']
    act = group[group['source'] == 'actual']
    
    # 提取所有累积量端点,生成匹配用的数量片段
    all_cum_points = np.sort(np.unique(np.concatenate([exp[['start_cum', 'end_cum']].values.flatten(),
                                                      act[['start_cum', 'end_cum']].values.flatten()])))
    segments = pd.DataFrame({
        'start': all_cum_points[:-1],
        'end': all_cum_points[1:]
    })
    segments['qty'] = segments['end'] - segments['start']
    segments['country'] = group['country'].iloc[0]
    segments['product'] = group['product'].iloc[0]
    
    # 匹配片段对应的expected日期
    segments = pd.merge_asof(segments, exp[['start_cum', 'date']],
                             left_on='start', right_on='start_cum', direction='backward')
    segments.rename(columns={'date': 'expected_date'}, inplace=True)
    
    # 匹配片段对应的actual日期
    segments = pd.merge_asof(segments, act[['start_cum', 'date']],
                             left_on='start', right_on='start_cum', direction='backward')
    segments.rename(columns={'date': 'actual_date'}, inplace=True)
    
    # 按规则打标签
    segments['label'] = np.select(
        [
            (segments['expected_date'].isna()) | (segments['actual_date'].isna()),
            segments['expected_date'] == segments['actual_date'],
            segments['expected_date'] < segments['actual_date'],
            segments['expected_date'] > segments['actual_date']
        ],
        [
            '无对应日期',
            '相同日期',
            '早于对应日期',
            '晚于对应日期'
        ]
    )
    
    # 整理输出列
    return segments[['country', 'product', 'expected_date', 'actual_date', 'qty', 'label']]

# 合并数据后分组处理
combined = pd.concat([expected_orders, actual_orders], ignore_index=True)
final_result = combined.groupby(['country', 'product'], group_keys=False).apply(process_group).reset_index(drop=True)

3. 性能优化说明

  • 矢量化替代循环:用merge_asof(排序后的近似匹配)和np.select(矢量化条件判断)替代嵌套循环,时间复杂度从O(n*m)降至O(n log n + m log m),百万级数据可在数分钟内完成处理
  • 内存控制:如果数据量过大导致内存不足,可以按country分块处理,每次仅加载部分数据到内存
  • 避免冗余计算:预处理阶段一次性计算累积量,避免分组内重复计算

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 06:50:24