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

如何在Pandas中基于指定列合并DataFrame并保留各自重复项

问题描述

有两个按日期排序的交易数据DataFrame:主DataFrame(df_master)包含额外列,更新DataFrame(df_update)包含主表的所有行及新行。需求如下:

  • 基于非额外列(date、notification、amount)将更新表的数据合并到主表;
  • 保留两个DataFrame各自内部的重复项;
  • 移除两表之间的重叠重复项;
  • 维持交易的日期顺序。

示例数据

主表(MASTER)

date       notification       amount     added_info
0    15/10/2021     string1          30.00          XX
1    15/10/2021     string1          30.00          XX
2    15/10/2021     string2          25.00          XX
3    11/10/2021     string3           3.00          YY
4    07/10/2021     string1          10.00
5    05/10/2021     string2          12.50
6    30/09/2021     string2          12.50          XX

更新表(UPDATE)

date       notification       amount
0    24/12/2021     string2          20.00 
1    20/12/2021     string1          12.00 
2    15/10/2021     string1          30.00 
3    15/10/2021     string1          30.00
4    15/10/2021     string1          30.00    
5    15/10/2021     string2          25.00    
6    11/10/2021     string3           3.00   
7    07/10/2021     string1          10.00
8    06/10/2021     string2          10.00

预期结果

EXPECTED RESULT (UPDATED MASTER)
       date       notification       amount     added_info
0    24/12/2021     string2          20.00 
1    20/12/2021     string1          12.00
2    15/10/2021     string1          30.00
3    15/10/2021     string1          30.00          XX
4    15/10/2021     string1          30.00          XX
5    15/10/2021     string2          25.00          XX
6    11/10/2021     string3           3.00          YY
7    07/10/2021     string1          10.00
8    06/10/2021     string2          10.00
9    05/10/2021     string2          12.50
10   30/09/2021     string2          12.50          XX

说明:合并时不使用added_info列匹配,保留主表内部重复行,添加更新表中未与主表重叠的重复行。使用concat+drop_duplicates会移除所有重复项,不符合需求。

现有朴素解法

import pandas as pd

df_master = pd.DataFrame({'date': {0: '15/10/2021', 1: '15/10/2021', 2: '15/10/2021', 3: '11/10/2021', 4: '07/10/2021', 5: '05/10/2021', 6: '30/09/2021'}, 'notification': {0: 'string1', 1: 'string1', 2: 'string2', 3: 'string3', 4: 'string1', 5: 'string2', 6: 'string2'}, 'amount': {0: 30.0, 1: 30.0, 2: 25.0, 3: 3.0, 4: 10.0, 5: 12.5, 6: 12.5}, 'added_info': {0: 'XX', 1: 'XX', 2: 'XX', 3: 'YY', 4: '', 5: '', 6: 'XX'}})
df_update = pd.DataFrame({'date': {0: '24/12/2021', 1: '20/12/2021', 2: '15/10/2021', 3: '15/10/2021', 4: '15/10/2021', 5: '15/10/2021', 6: '11/10/2021', 7: '07/10/2021', 8: '06/10/2021'}, 'notification': {0: 'string2', 1: 'string1', 2: 'string1', 3: 'string1', 4: 'string1', 5: 'string2', 6: 'string3', 7: 'string1', 8: 'string2'}, 'amount': {0: 20.0, 1: 12.0, 2: 30.0, 3: 30.0, 4: 30.0, 5: 25.0, 6: 3.0, 7: 10.0, 8: 10.0}})

df_update["Dupl"] = ""

df_master_concat = pd.DataFrame()
df_update_concat = pd.DataFrame()
df_update_concat["string"] = df_update[["date", "notification", "amount"]].agg(lambda x: "".join(x.astype(str)), axis=1)
df_master_concat["string"] = df_master[["date", "notification", "amount"]].agg(lambda x: "".join(x.astype(str)), axis=1)

for indx, row in df_update_concat.iterrows():
    if df_update_concat.iloc[indx]["string"] in df_master_concat["string"].values:
        df_update.at[indx , "Dupl"] = "X"
        df_master_concat.drop(df_master_concat.loc[df_master_concat['string'] == df_update_concat.iloc[indx]["string"]].iloc[0].name, inplace=True) # drop the first occurence

df_update = df_update[df_update.Dupl != "X"]
df_update.drop('Dupl', axis=1, inplace=True)
df_master= pd.concat([df_master, df_update], ignore_index=True).fillna("")


df_master["date"] = pd.to_datetime(df_master["date"], format="%d/%m/%Y")
df_master.sort_values(by='date', inplace = True, ascending=False)
df_master["date"] = df_master["date"].dt.strftime("%d/%m/%Y")
df_master = df_master.reset_index(drop=True)

print(df_master)

该解法支持扩展主表的额外列,但效率较低。请问是否有Pandas内置的高效实现方式,以及该朴素解法的优劣如何?


解决方案

一、高效实现方式(基于Pandas内置方法)

核心思路是给两个表的重复组添加计数标识,只保留更新表中计数超过主表对应组的部分,最后合并并排序,全程用向量化操作替代循环。

代码实现

import pandas as pd

# 加载数据
df_master = pd.DataFrame({'date': {0: '15/10/2021', 1: '15/10/2021', 2: '15/10/2021', 3: '11/10/2021', 4: '07/10/2021', 5: '05/10/2021', 6: '30/09/2021'}, 'notification': {0: 'string1', 1: 'string1', 2: 'string2', 3: 'string3', 4: 'string1', 5: 'string2', 6: 'string2'}, 'amount': {0: 30.0, 1: 30.0, 2: 25.0, 3: 3.0, 4: 10.0, 5: 12.5, 6: 12.5}, 'added_info': {0: 'XX', 1: 'XX', 2: 'XX', 3: 'YY', 4: '', 5: '', 6: 'XX'}})
df_update = pd.DataFrame({'date': {0: '24/12/2021', 1: '20/12/2021', 2: '15/10/2021', 3: '15/10/2021', 4: '15/10/2021', 5: '15/10/2021', 6: '11/10/2021', 7: '07/10/2021', 8: '06/10/2021'}, 'notification': {0: 'string2', 1: 'string1', 2: 'string1', 3: 'string1', 4: 'string1', 5: 'string2', 6: 'string3', 7: 'string1', 8: 'string2'}, 'amount': {0: 20.0, 1: 12.0, 2: 30.0, 3: 30.0, 4: 30.0, 5: 25.0, 6: 3.0, 7: 10.0, 8: 10.0}})

# 1. 给两个表的每组重复行添加序号(从1开始)
match_cols = ['date', 'notification', 'amount']
df_master['seq'] = df_master.groupby(match_cols).cumcount() + 1
df_update['seq'] = df_update.groupby(match_cols).cumcount() + 1

# 2. 获取主表每组的最大序号
master_max_seq = df_master.groupby(match_cols)['seq'].max().reset_index()

# 3. 筛选更新表中需要新增的行:要么是主表没有的组,要么是组内序号超过主表最大值的行
df_update_new = df_update.merge(
    master_max_seq, 
    on=match_cols, 
    how='left', 
    suffixes=('', '_master_max')
)
df_update_new = df_update_new[
    (df_update_new['seq'] > df_update_new['seq_master_max']) | 
    df_update_new['seq_master_max'].isna()
].drop(columns=['seq_master_max', 'seq'])

# 4. 合并主表和新增行,按日期排序
df_combined = pd.concat([df_master.drop(columns='seq'), df_update_new], ignore_index=True).fillna('')
df_combined['date'] = pd.to_datetime(df_combined['date'], format='%d/%m/%Y')
df_combined = df_combined.sort_values(by='date', ascending=False).reset_index(drop=True)
df_combined['date'] = df_combined['date'].dt.strftime('%d/%m/%Y')

print(df_combined)

二、朴素解法的优劣分析

优点

  • 逻辑直观:通过拼接字符串生成唯一标识,逐行匹配移除重叠项,符合常规思维,新手容易理解。
  • 兼容性强:不依赖复杂分组操作,支持主表添加任意额外列,无需修改核心逻辑。

缺点

  • 效率极低:iterrows()逐行循环+in判断+drop操作,时间复杂度为O(n²),数据量较大时性能会急剧下降。
  • 稳定性差:字符串拼接可能出现冲突(比如不同数值拼接后字符串相同),导致错误匹配。
  • 代码冗余:需要手动处理标识列、循环判断,代码量多,维护成本高。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 09:45:43