如何在Pandas分组后按列统计不同阈值以上的数值数量?
Pandas分组后按独立阈值统计达标数量的解决方案
问题背景
需要对数据集按[s_num, ip, f_num, direction]分组,为每个algo_i_score列设置独立阈值,统计每组中超过对应阈值的数值数量,结果中algo_i列对应组内达标个数。
示例数据集
id s_num ip f_num direction algo_1_x algo_2_x algo_1_score algo_2_score 0 0.0 0.0 0.0 0.0 X -4.63 -4.45 0.624356 0.664009 15 19.0 0.0 2.0 0.0 X -5.44 -5.02 0.411217 0.515843 16 20.0 0.0 2.0 0.0 X -12.36 -5.09 0.397237 0.541112 20 24.0 0.0 2.0 1.0 X -4.94 -5.15 0.401744 0.526032 21 25.0 0.0 2.0 1.0 X -4.78 -4.98 0.386410 0.564934 22 26.0 0.0 2.0 1.0 X -4.89 -5.03 0.394326 0.513896 24 28.0 0.0 2.0 2.0 X -4.78 -5.00 0.420078 0.521993 25 29.0 0.0 2.0 2.0 X -4.91 -5.14 0.407355 0.485878 26 30.0 0.0 2.0 2.0 X 11.83 -4.97 0.392242 0.659122 27 31.0 0.0 2.0 2.0 X -4.73 -5.07 0.377011 0.524774
尝试代码及报错
尝试的代码:
def count_success(x,thresh): return ((x > thresh)*1).sum() thresholds=[0.1,0.2] df.groupby(attr_cols).agg({f'algo_{i+1}_score':count_success(thresh) for i, thresh in enumerate(thresholds)})
报错信息:
count_success() missing 1 required positional argument: 'thresh'
疑问:如何为agg()中的自定义函数传递额外参数?或是否有更简便的Pandas内置方法实现需求?
解决方案
方法1:为自定义函数传递额外参数
报错原因是直接调用count_success(thresh)会立即执行函数,而agg需要传入的是函数对象而非执行结果。可以通过以下两种方式传递参数:
方式A:使用functools.partial绑定参数
partial可以固定函数的部分参数,生成一个新的可调用对象,适合agg使用:
from functools import partial import pandas as pd def count_success(x, thresh): return (x > thresh).sum() # 布尔值直接求和等价于计数,无需乘1 thresholds = [0.1, 0.2] # 构建聚合函数字典 agg_funcs = { f'algo_{i+1}_score': partial(count_success, thresh=thresh) for i, thresh in enumerate(thresholds) } # 分组聚合并调整列名 result = df.groupby(['s_num', 'ip', 'f_num', 'direction']).agg(agg_funcs) result.columns = [col.replace('_score', '') for col in result.columns]
方式B:使用lambda函数包装
用lambda函数封装原函数和参数,注意要把thresh设为默认参数,避免循环变量引用问题:
import pandas as pd def count_success(x, thresh): return (x > thresh).sum() thresholds = [0.1, 0.2] agg_funcs = { f'algo_{i+1}_score': lambda x, thresh=thresh: count_success(x, thresh) for i, thresh in enumerate(thresholds) } result = df.groupby(['s_num', 'ip', 'f_num', 'direction']).agg(agg_funcs) result.columns = [col.replace('_score', '') for col in result.columns]
方法2:更简便的Pandas内置方法(无需自定义函数)
利用Pandas的广播特性,先对所有score列做阈值判断生成布尔矩阵,再合并分组键后分组求和,代码更简洁高效:
import pandas as pd thresholds = [0.1, 0.2] # 提取所有score列并生成阈值判断后的布尔DataFrame score_cols = [f'algo_{i+1}_score' for i in range(len(thresholds))] bool_df = df[score_cols] > thresholds # 合并分组键与布尔结果,分组后求和得到达标数量 result = pd.concat([df[['s_num', 'ip', 'f_num', 'direction']], bool_df], axis=1)\ .groupby(['s_num', 'ip', 'f_num', 'direction'])\ .sum() # 调整列名以符合需求 result.columns = [col.replace('_score', '') for col in result.columns]
内容的提问来源于stack exchange,提问作者Jenny
相关产品推荐
相关产品推荐

