如何在Python中对比DataFrame,识别单元格变更、新增条目并记录日期?
对比两个DataFrame并标记差异、新增项及变更日期
要实现你的需求,核心是通过唯一标识列(比如示例中的ID)匹配两个DataFrame的行,再结合列值对比区分状态,同时添加变更日期。以下是基于pandas的完整实现方案:
步骤1:模拟示例数据
先还原示例中的两个DataFrame(你可以替换成自己的真实数据):
import pandas as pd from datetime import datetime # 原始数据df1 df1 = pd.DataFrame({ 'ID': [1, 2, 3], 'Name': ['Alice', 'Bob', 'Charlie'], 'Age': [25, 30, 35] }) # 更新后的数据df2 df2 = pd.DataFrame({ 'ID': [1, 2, 4], 'Name': ['Alice', 'Bob', 'Dave'], 'Age': [26, 30, 28] })
步骤2:合并数据并标记来源
用outer join合并两个DataFrame,通过_merge字段区分行的来源(仅在df1、仅在df2、两边都有):
merged = pd.merge(df2, df1, on='ID', how='outer', suffixes=('_new', '_old'), indicator=True)
步骤3:标记状态与变更日期
初始化状态列和变更日期列,根据行的来源和列值匹配情况标记状态:
# 初始化列 merged['Status'] = '' merged['Change Date'] = pd.NaT current_date = datetime.today().strftime('%Y-%m-%d') # 标记新增条目(仅在df2中存在) merged.loc[merged['_merge'] == 'left_only', ['Status', 'Change Date']] = ['New Entry', current_date] # 标记无变更条目(ID匹配且所有非ID列值一致) non_id_cols = [col for col in df1.columns if col != 'ID'] match_mask = merged.apply(lambda row: all(row[f'{col}_new'] == row[f'{col}_old'] for col in non_id_cols), axis=1) merged.loc[match_mask, 'Status'] = 'No Change' # 标记更新条目(ID匹配但存在列值变更) update_mask = (merged['_merge'] == 'both') & ~match_mask merged.loc[update_mask, ['Status', 'Change Date']] = ['Updated', current_date] # 可选:标记移除条目(仅在df1中存在) merged.loc[merged['_merge'] == 'right_only', ['Status', 'Change Date']] = ['Removed', current_date]
步骤4:整理输出结果
将合并后的数据整理成目标格式,保留需要的列:
# 填充移除条目的原始值 for col in non_id_cols: merged[col] = merged[f'{col}_new'].fillna(merged[f'{col}_old']) # 生成最终结果df3 output_cols = ['ID'] + non_id_cols + ['Status', 'Change Date'] df3 = merged[output_cols].sort_values('ID').reset_index(drop=True) print(df3)
运行后输出的df3如下:
ID Name Age Status Change Date 0 1 Alice 26 Updated 2024-05-20 1 2 Bob 30 No Change NaT 2 3 Charlie 35 Removed 2024-05-20 3 4 Dave 28 New Entry 2024-05-20
额外:标记具体变更的单元格
如果需要明确指出哪几列发生了变更,可以添加Changed Columns列:
def get_changed_columns(row): changed = [] for col in non_id_cols: if row[f'{col}_new'] != row[f'{col}_old']: changed.append(col) return ', '.join(changed) if changed else None merged['Changed Columns'] = merged.apply(get_changed_columns, axis=1) # 更新输出列 output_cols.append('Changed Columns') df3 = merged[output_cols].sort_values('ID').reset_index(drop=True)
此时df3会新增一列显示变更的列名,比如ID=1的行显示Age。
内容的提问来源于stack exchange,提问作者Michael Amos
相关产品推荐
相关产品推荐

