如何识别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
相关产品推荐
相关产品推荐

