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

如何识别Pandas DataFrame中已抵消行,避免误标记重录项

问题背景

我有一份财务系统导出的大型费用条目数据,包含matter(工作唯一标识)、date、amount三个字段。操作中会出现以下情况:

  • 错误录入后,用抵消费用(金额为原条目相反数)取消同一matter、同一date的条目
  • 偶尔会重新录入已被抵消的条目,形成「原条目→抵消条目→重录条目」三行数据

需求是准确标记:哪些行是抵消行,哪些行已被抵消。

原实现及问题

原代码通过遍历每行,检查同一matter+date下是否存在相反数金额来标记,但会误将重录条目(如下例第6行)标记为已抵消:

原代码

import pandas as pd
data = [
        [1,'1/2/2022',10],
        [1,'1/2/2022',15],
        [1,'1/2/2022',-10],
        [2,'1/4/2022',12],
        [2,'1/5/2022', 5],
        [2,'1/5/2022',-5],
        [2,'1/5/2022', 5]
    ]
df = pd.DataFrame(data, columns=['matter','date','amount'])

def rev_check(matter, date, WorkAmt, df):
    funcDF = df.loc[(df['matter'] == matter) & (df['date'] == date)] 
    listCheck = funcDF['amount'].tolist()
    if WorkAmt*-1 in listCheck:
        return 'yes'
    
df['reversal'] = df.apply(lambda row: rev_check(row.matter, row.date, row.amount, df), axis=1)

print(df)

原运行结果

matter      date  amount reversal
0       1  1/2/2022      10      yes
1       1  1/2/2022      15     None
2       1  1/2/2022     -10      yes
3       2  1/4/2022      12     None
4       2  1/5/2022       5      yes
5       2  1/5/2022      -5      yes
6       2  1/5/2022       5      yes

问题:第6行是重新录入的有效条目,不应被标记为yes,但原逻辑只检查是否存在相反数,忽略了「抵消关系是一对一配对」的规则——第4行已被第5行抵消,第6行没有对应的抵消条目。

优化方案

核心思路是按顺序配对抵消关系:同一matter+date分组内,按顺序寻找未被配对的相反数金额,完成配对后标记双方为抵消/被抵消,剩余未配对的条目不标记。

优化代码

import pandas as pd

data = [
        [1,'1/2/2022',10],
        [1,'1/2/2022',15],
        [1,'1/2/2022',-10],
        [2,'1/4/2022',12],
        [2,'1/5/2022', 5],
        [2,'1/5/2022',-5],
        [2,'1/5/2022', 5]
    ]
df = pd.DataFrame(data, columns=['matter','date','amount'])
df['reversal'] = None

# 按matter和date分组处理
for _, group in df.groupby(['matter', 'date']):
    # 复制分组数据,保留原始索引
    group_copy = group.copy().reset_index()
    # 记录已配对的索引
    paired_indices = set()
    
    for i, row in group_copy.iterrows():
        if i in paired_indices:
            continue
        target_amount = -row['amount']
        # 从当前行之后的位置寻找未配对的目标金额
        matches = group_copy[(group_copy.index > i) & 
                            (group_copy['amount'] == target_amount) & 
                            (~group_copy.index.isin(paired_indices))]
        if not matches.empty:
            # 取第一个匹配项
            match_idx = matches.index[0]
            # 标记原始DataFrame中的对应行
            df.at[group_copy.loc[i, 'index'], 'reversal'] = 'yes'
            df.at[group_copy.loc[match_idx, 'index'], 'reversal'] = 'yes'
            # 加入已配对集合
            paired_indices.add(i)
            paired_indices.add(match_idx)

print(df)

优化后运行结果

matter      date  amount reversal
0       1  1/2/2022      10      yes
1       1  1/2/2022      15     None
2       1  1/2/2022     -10      yes
3       2  1/4/2022      12     None
4       2  1/5/2022       5      yes
5       2  1/5/2022      -5      yes
6       2  1/5/2022       5     None

方案说明

  • 按matter+date分组处理,确保只在同一工作、同一日期内配对
  • 按条目顺序遍历,仅寻找当前条目之后的未配对相反数,模拟真实业务中「先录入原条目,再录入抵消条目」的流程
  • 用集合记录已配对的索引,避免重复配对,确保每个条目最多被配对一次
  • 最终未被配对的条目(如第6行)不会被标记,符合业务逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 05:50:22