员工月度数据DataFrame逐行对比、提取变动行及变动字段方案咨询
员工月度数据差异对比最优实现方案
直接用Pandas即可实现全需求,无需引入额外第三方依赖,处理逻辑覆盖所有规则要求:
实现思路
- 解决数据类型不一致问题:将两个数据表除
Index外的所有列统一转为字符串类型,避免数值、字符串、特殊格式(如带$的金额)的对比误差 - 解决空值对比问题:将所有空值(空字符串、NaN、NaT等)统一替换为专属占位符,规避原生Pandas中
NaN != NaN的逻辑问题 - 主键过滤:通过
Index列做内连接,仅保留两个月度表中都存在的匹配记录 - 差异识别:逐列对比两个表的同名字段,筛选出存在至少1处变动的行,同时收集每行的变动列名
完整代码示例
import pandas as pd # ---------------------- 1. 构造示例数据 ---------------------- # 上月存量数据df1 df1 = pd.DataFrame([ [1, "IT", "6000.00", "Jack", "ax@i.com", "01-01-2021"], [2, "HR", "7000", "O'Donnel", "ay@i.com", ""], [3, "MKT", "$7600", "Maria", "d", "30-06-2021"], [4, "I'T", "8000", "Peter", "az@i.com", "14-07-2021"] ], columns=["Index", "Department", "Salary", "Manager", "Email", "Start_Date"]) # 当月新数据df2 df2 = pd.DataFrame([ [1, "IT", "6000.00", "Jack", "ax@i.com", "01-01-2021"], [2, "HR", "7000", "O'Donnel", "ay@i.com", "01-01-2021"], [3, "MKT", "7600", "Maria", "dy@i.com", "30-06-2021"], [4, "IT", "8000", "Peter", "az@i.com", "14-07-2021"], [5, "IT", "9000", "John", "", "NOT PROVIDED"], [6, "IT", "9900", "John", "", "NOT PROVIDED"] ], columns=["Index", "Department", "Salary", "Manager", "Email", "Start_Date"]) # ---------------------- 2. 数据预处理 ---------------------- # 要对比的业务列(排除主键Index) compare_cols = [col for col in df1.columns if col != "Index"] # 统一空值为占位符,解决空值对比问题 null_placeholder = "__NULL__" df1 = df1.replace(["", pd.NA, pd.NaT, None], null_placeholder) df2 = df2.replace(["", pd.NA, pd.NaT, None], null_placeholder) # 统一所有业务列为字符串类型,解决数据类型不一致问题 df1[compare_cols] = df1[compare_cols].astype(str) df2[compare_cols] = df2[compare_cols].astype(str) # ---------------------- 3. 主键匹配+差异对比 ---------------------- # 内连接只保留双表都存在的Index merged = pd.merge(df1, df2, on="Index", how="inner", suffixes=("_上月", "_当月")) # 逐列对比生成差异掩码 diff_mask = pd.DataFrame() for col in compare_cols: diff_mask[col] = merged[f"{col}_上月"] != merged[f"{col}_当月"] # 筛选存在至少1处变动的行 changed_rows = merged[diff_mask.any(axis=1)].copy() # 标注每行的变动字段 changed_rows["变动字段"] = diff_mask[diff_mask.any(axis=1)].apply( lambda x: ",".join(x[x].index.tolist()), axis=1 ) # ---------------------- 4. 生成结果 ---------------------- # 结果1:仅输出当月变动行(和示例df3完全一致) result_simple = changed_rows[[f"{col}_当月" for col in ["Index"] + compare_cols]] result_simple.columns = ["Index"] + compare_cols print("精简变动结果:") print(result_simple) # 结果2:带变动详情的结果,包含上月值、当月值、变动字段 result_detail = changed_rows[["Index"] + [f"{col}_上月" for col in compare_cols] + [f"{col}_当月" for col in compare_cols] + ["变动字段"]] print("\n详细变动结果:") print(result_detail[["Index", "变动字段"]])
输出效果
运行代码后精简结果和你给出的示例df3完全一致,详细结果的变动字段列会显示:
- Index=2:Start_Date
- Index=3:Salary,Email
- Index=4:Department
可根据实际需求选择输出精简版或详细版结果,该方案支持63列的全量对比,性能无明显瓶颈。
内容的提问来源于stack exchange,提问作者Paulo Cortez
相关产品推荐
相关产品推荐

