Pandas:如何用两个DataFrame实现类Excel COUNTIF的带通配符统计?
问题
我正在编写脚本自动化Excel重复数据的转换/清洗工作,目前进展顺利,但遇到以下问题:
已导入相关DataFrame并完成过滤等清洗操作,创建了两个DataFrame——df2是基于df1生成的唯一ProductID列表,df1包含ProductID及对应的ProcCode。需要统计每个ProductID对应的ProcCode以P、A、X开头的次数(分别列为单独字段),但不清楚如何跨两个DataFrame实现。
示例数据:
import pandas as pd df1 = pd.DataFrame({'ProductID': ["12441","44123","77880","12345","33445","77565","34354","77880","33445", "12345", "12441", "12441","12441","44123"], "ProcCode":["P34","P35","P67","P67","X77","P34","P35","P34","X77","P35","A55","P34","P35","A55"]})
df1内容:
ProductID ProcCode 0 12441 P34 1 44123 P35 2 77880 P67 3 12345 P67 4 33445 X77 5 77565 P34 6 34354 P35 7 77880 P34 8 33445 X77 9 12345 P35 10 12441 A55 11 12441 P34 12 12441 P35 13 44123 A55
df2 = pd.DataFrame({"ProductID": ["12441","44123","77880","12345","33445","77565"]})
df2内容:
ProductID 0 12441 1 44123 2 77880 3 12345 4 33445 5 77565
期望生成的df3:
df3 = pd.DataFrame({"ProductID":["12441","44123","77880","12345","33445","77565"], "CountofPCode":[3,1,2,3,0,1],"CountofXCode":[0,0,0,0,2,0]})
df3内容:
ProductID CountofPCode CountofXCode 0 12441 3 0 1 44123 1 0 2 77880 2 0 3 12345 3 0 4 33445 0 2 5 77565 1 0
解决方案
可以通过分组统计+左连接合并实现需求,下面提供两种写法:
写法一:分步实现(易理解)
- 给df1新增标记列,识别每个ProcCode的开头类型
# 标记是否以P/A/X开头,转为整数(True=1,False=0) df1['is_P'] = df1['ProcCode'].str.startswith('P').astype(int) df1['is_A'] = df1['ProcCode'].str.startswith('A').astype(int) df1['is_X'] = df1['ProcCode'].str.startswith('X').astype(int)
- 按ProductID分组求和,得到每个ID的各开头次数
stats_df = df1.groupby('ProductID')[['is_P', 'is_A', 'is_X']].sum().reset_index()
- 与df2左连接,确保保留df2中所有唯一ID,缺失统计项填充0并调整列名
df3 = df2.merge(stats_df, on='ProductID', how='left').fillna(0) # 重命名列名匹配需求 df3 = df3.rename(columns={ 'is_P': 'CountofPCode', 'is_A': 'CountofACode', 'is_X': 'CountofXCode' }) # 转换为整数类型 df3[['CountofPCode', 'CountofACode', 'CountofXCode']] = df3[['CountofPCode', 'CountofACode', 'CountofXCode']].astype(int)
写法二:链式写法(更简洁高效)
无需新增临时列,直接在聚合阶段完成统计:
df3 = df2.merge( df1.groupby('ProductID').agg( CountofPCode=('ProcCode', lambda x: x.str.startswith('P').sum()), CountofACode=('ProcCode', lambda x: x.str.startswith('A').sum()), CountofXCode=('ProcCode', lambda x: x.str.startswith('X').sum()) ).reset_index(), on='ProductID', how='left' ).fillna(0).astype({ 'CountofPCode': int, 'CountofACode': int, 'CountofXCode': int })
两种写法最终得到的df3均与期望结果一致,若不需要A开头的统计,只需删除对应列的代码即可。
内容的提问来源于stack exchange,提问作者conrito345
相关产品推荐
相关产品推荐

