Pandas按Day、C_Code对比两个DataFrame并拆分正误结果
Pandas双DataFrame按联合键对比拆分实现方案
核心思路
你的判断没错,实现过程确实需要用到groupby做分组聚合,全程用pandas内置向量化方法即可,不需要写逐行循环,处理效率有保证。整体流程分为:左关联匹配两表、分组计算单组Qty总和、按规则拆分结果到正确/错误列表三个阶段。
测试样例造数
先构造和你描述字段完全对齐的测试数据,方便你直接跑通验证逻辑:
import pandas as pd # 构造DF1:5条商品数量明细 df1 = pd.DataFrame({ 'Code': ['C001','C002','C003','C004','C005'], 'Product': ['P1','P2','P3','P4','P5'], 'Day': ['2024-01-01','2024-01-01','2024-01-02','2024-01-02','2024-01-03'], 'C_Code': ['CC1','CC1','CC2','CC2','CC3'], 'Name': ['N1','N1','N2','N2','N3'], 'Qty': [3,2,4,3,5] }) # 构造DF2:3条基准记录,Day+C_Code为联合唯一键 df2 = pd.DataFrame({ 'Day': ['2024-01-01','2024-01-02','2024-01-04'], 'C_Code': ['CC1','CC2','CC4'], 'Name': ['N1','N2','N4'], 'Qty': [4,6,10] }) # 前置校验:确认DF2联合键无重复,避免关联时行数膨胀 assert df2.duplicated(subset=['Day','C_Code']).sum() == 0
完整实现代码
步骤1:左关联两表,匹配基准值
以DF1为左表做关联,把DF2对应的基准Qty拼接到每一行,匹配不到的行基准值为空:
df_merged = df1.merge( df2[['Day','C_Code','Qty']].rename(columns={'Qty':'Qty_ref'}), on=['Day','C_Code'], how='left' )
步骤2:分组计算同联合键下的Qty总和
用groupby+transform直接给每一行打上所属Day+C_Code分组的总Qty,不需要单独聚合后再回拼:
df_merged['Qty_group_sum'] = df_merged.groupby(['Day','C_Code'])['Qty'].transform('sum')
步骤3:拆分无匹配记录直接进错误列表
# 筛出DF2中无对应联合键的行,直接写入DF4 df4_part1 = df_merged[df_merged['Qty_ref'].isna()].drop(columns=['Qty_ref','Qty_group_sum']).copy() # 剩余为匹配成功的待处理记录 df_matched = df_merged[~df_merged['Qty_ref'].isna()].copy()
步骤4:按分组Qty和基准值的关系拆分匹配成功的记录
# 分组总Qty ≤ 基准值的行,直接写入DF3 mask_valid = df_matched['Qty_group_sum'] <= df_matched['Qty_ref'] df3_part1 = df_matched[mask_valid].drop(columns=['Qty_ref','Qty_group_sum']).copy() # 分组总Qty > 基准值的行,分两部分处理 df_over = df_matched[~mask_valid].copy() # ① 原行Qty替换为基准值,写入DF3 df_over_for_df3 = df_over.copy() df_over_for_df3['Qty'] = df_over_for_df3['Qty_ref'] df3_part2 = df_over_for_df3.drop(columns=['Qty_ref','Qty_group_sum']).copy() # ② 计算Qty差值,构造差值记录写入DF4 df_over_for_df4 = df_over.groupby(['Day','C_Code'], as_index=False).first().copy() df_over_for_df4['Qty'] = df_over_for_df4['Qty_group_sum'] - df_over_for_df4['Qty_ref'] df4_part2 = df_over_for_df4.drop(columns=['Qty_ref','Qty_group_sum']).copy()
步骤5:拼接得到最终结果
df3 = pd.concat([df3_part1, df3_part2], ignore_index=True) df4 = pd.concat([df4_part1, df4_part2], ignore_index=True)
关键注意点
- 全程用pandas向量化操作,没有逐行循环,十万级以内的数据处理速度很快
- 超量差值行默认取对应Day+C_Code分组下的第一条商品记录作为模板,如果你需要自定义差值行的字段(比如添加超量标记、指定专属编码),直接在
df_over_for_df4上赋值修改即可 - 前置的联合键唯一性校验不要省略,避免DF2存在重复键时关联出多余行,导致计算结果错误
内容的提问来源于stack exchange,提问作者Max Prokopenko
相关产品推荐
相关产品推荐

