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

如何获取Pandas对比DataFrame时标记为left_only/right_only的行的差异列信息(结合Issr列校验场景)

如何获取Pandas对比DataFrame时标记为left_only/right_only的行的差异列信息(结合Issr列校验场景)

要解决你的核心问题——精准定位DataFrame对比时的差异列,并结合Issr列的规则校验违规行,我们需要跳出仅依赖merge(indicator=True)的思路,转而通过唯一键匹配行+逐列对比差异的方式实现。以下是分步解决方案:


步骤1:修正数据结构,添加唯一标识列

你的表格中SNo是每行的唯一标识(区分更新行和新增行),先将其补充到数据源中,方便后续行匹配:

import pandas as pd

# 补充SNo列的base数据
base = {
    'SNo': [1, 2, 3, 4],
    'Rank': [10,14,15,50],
    'Ctry': ['A', 'B', 'C', 'D'],
    'Cat': ['Book', 'Laptop', 'Pen', 'Pen'],
    'Issr': ['Y', 'C', 'J', ''],
    'Ref': ['100', '101', '102', '103']
}

# 补充SNo列的current数据
current = {
    'SNo': [1, 2, 3, 4, 5, 6],
    'Rank': [10,14,15,50,24,24],
    'Ctry': ['A', 'B', 'C', 'D', 'K', 'RT'],
    'Cat': ['Book1', 'Laptop', 'Pen', 'Pen', 'Pencil', 'Pencil'],
    'Issr': ['Y', '', 'J', 'U', 'W', ''],
    'Ref': ['100', '101', '102', '103', '101', '201']
}

# 转换为DataFrame并设置SNo为索引(简化行匹配逻辑)
base_df = pd.DataFrame(base).set_index('SNo')
current_df = pd.DataFrame(current).set_index('SNo')

步骤2:分离新增行与更新行

将current中的行分为两类,分别处理:

  • 新增行:base中不存在的行
  • 更新行:base中已存在,但可能有列值变化的行
# 1. 筛选新增行(current有,base无)
new_rows = current_df[~current_df.index.isin(base_df.index)]

# 2. 筛选更新行的索引,并提取两边的对应数据
common_idx = current_df.index.intersection(base_df.index)
current_common = current_df.loc[common_idx]
base_common = base_df.loc[common_idx]

步骤3:定位更新行的差异列(含Issr)

对于更新行,我们可以逐列对比,找出所有变化的列,重点关注Issr列的变化是否违反规则:

# 生成布尔值DataFrame:True表示该列值在两行中不同
all_column_changes = current_common.ne(base_common)

# 为每个更新行生成差异列的列表
row_diff_details = all_column_changes.apply(
    lambda row: row.index[row].tolist(),
    axis=1
).rename('diff_columns')

# 单独提取Issr列的变化细节,用于规则校验
issr_change_details = pd.DataFrame({
    'base_Issr': base_common['Issr'],
    'current_Issr': current_common['Issr'],
    'diff_columns': row_diff_details
})

执行后,issr_change_details会清晰展示每个更新行的Issr原值、新值,以及所有差异列:

base_Issr current_Issr diff_columns
SNo                                     
1            Y            Y        [Cat]
2            C                     [Issr]
3            J            J           []
4                          U        [Issr]

步骤4:结合规则校验违规行

根据你提出的两个Issr规则,标记所有违规行:

规则1:新增行的Issr必须为空('')

# 新增行中Issr非空的即为违规
new_row_violations = new_rows[new_rows['Issr'] != ''].assign(
    violation_type='新增行违规:Issr非空'
)

规则2:更新行的Issr不能从非空改为非空(仅允许改为空或保持不变;原空值不能改为非空)

# 标记更新行中的Issr违规情况
issr_update_violations = issr_change_details[
    # 情况1:原Issr非空,新值既不是原值也不是空
    ((issr_change_details['base_Issr'] != '') &
     (issr_change_details['current_Issr'] != issr_change_details['base_Issr']) &
     (issr_change_details['current_Issr'] != '')) |
    # 情况2:原Issr为空,新值非空
    ((issr_change_details['base_Issr'] == '') &
     (issr_change_details['current_Issr'] != ''))
].join(current_common).assign(
    violation_type='更新行违规:Issr非法修改'
)

步骤5:汇总所有违规行

# 合并两类违规行
all_violations = pd.concat([new_row_violations, issr_update_violations])

print("所有违规行:")
print(all_violations[['Rank', 'Ctry', 'Cat', 'Issr', 'Ref', 'violation_type']])

输出结果完全匹配你提到的违规场景:

Rank Ctry     Cat Issr  Ref          violation_type
SNo                                                   
5     24    K  Pencil    W  101    新增行违规:Issr非空
4     50    D     Pen    U  103  更新行违规:Issr非法修改

关键说明

  1. 为什么不用merge(indicator=True)?
    它是按整行内容判断是否存在,行中任意一列变化都会被标记为left_only/right_only,无法定位具体差异列。而通过唯一键匹配+逐列对比,能精准找到变化的列。
  2. 唯一标识的选择:
    这里用SNo作为唯一键,你可以根据实际业务调整为其他组合键(比如Ref+Cat等),确保能准确匹配base和current中的同一行。

备注:内容来源于stack exchange,提问作者adarsh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.13 19:53:06