Python:基于另一DataFrame匹配行将指定列数据移至上一行
问题描述
我有两个DataFrame:
df1:
Name Month Amount Status 0 Bill Apr 0 1 Bill May 0 2 Bill Jun 100 member 3 Sally Apr 0 4 Sally May 0 5 Sally Jun 200 member 6 Tom Apr 0 7 Tom May 300 member 8 Tom Jun 0
df2:
Name Month 0 Bill Jun 1 Tom May
需求:更新df1,当df1的Name和Month与df2匹配时,将该行的Amount和Status值移至同一用户的上一行,原行的这两个字段置为0。已知每个匹配行都存在对应的上一行,且数据已按Name和Month正确排序。
预期结果:
Name Month Amount Status 0 Bill Apr 0 1 Bill May 100 member 2 Bill Jun 0 3 Sally Apr 0 4 Sally May 0 5 Sally Jun 200 member 6 Tom Apr 300 member 7 Tom May 0 8 Tom Jun 0
我知道可以用iterrows()实现,但有没有更直接高效的方法?
高效实现方案
用pandas的向量化操作结合布尔索引就能完成,完全避免循环,比iterrows()效率高得多,尤其适合大数据量场景:
步骤1:标记需要处理的匹配行
先把df2的Name和Month组合成元组集合,再用这个集合匹配df1的对应行,生成掩码:
# 生成需匹配的(Name, Month)元组集合 match_pairs = set(df2.apply(tuple, axis=1)) # 标记df1中符合匹配条件的行 mask = df1[['Name', 'Month']].apply(tuple, axis=1).isin(match_pairs)
步骤2:将匹配行的值移动到上一行
利用shift(1)获取匹配行的上一行位置,直接赋值对应字段:
# 把匹配行的Amount和Status值赋值给同一用户的上一行 df1.loc[mask.shift(1).fillna(False), ['Amount', 'Status']] = df1.loc[mask, ['Amount', 'Status']].values
步骤3:清空原匹配行的字段
将匹配行的Amount置0,Status清空:
df1.loc[mask, 'Amount'] = 0 df1.loc[mask, 'Status'] = ''
完整可运行代码
import pandas as pd # 构造示例数据 df1 = pd.DataFrame({ 'Name': ['Bill', 'Bill', 'Bill', 'Sally', 'Sally', 'Sally', 'Tom', 'Tom', 'Tom'], 'Month': ['Apr', 'May', 'Jun', 'Apr', 'May', 'Jun', 'Apr', 'May', 'Jun'], 'Amount': [0, 0, 100, 0, 0, 200, 0, 300, 0], 'Status': ['', '', 'member', '', '', 'member', '', 'member', ''] }) df2 = pd.DataFrame({ 'Name': ['Bill', 'Tom'], 'Month': ['Jun', 'May'] }) # 执行处理逻辑 match_pairs = set(df2.apply(tuple, axis=1)) mask = df1[['Name', 'Month']].apply(tuple, axis=1).isin(match_pairs) df1.loc[mask.shift(1).fillna(False), ['Amount', 'Status']] = df1.loc[mask, ['Amount', 'Status']].values df1.loc[mask, 'Amount'] = 0 df1.loc[mask, 'Status'] = '' print(df1)
运行后即可得到预期结果,全程基于pandas的向量化运算,性能远高于循环遍历的iterrows()。
内容的提问来源于stack exchange,提问作者Maxxmilo
相关产品推荐
相关产品推荐

