如何在DataFrame分组中应用排除当前行的自定义z-score函数?
按分组计算排除当前行的专属Z-score
问题背景
现有如下包含日期、查询词、搜索次数字段的DataFrame数据:
| 日期 | 查询词 | 搜索次数 |
|---|---|---|
| 1 | abc | 2 |
| 1 | def | 3 |
| 2 | abc | 6 |
| 3 | abc | 5 |
| 4 | def | 1 |
| 4 | abc | 3 |
| 5 | abc | 7 |
| 6 | abc | 8 |
| 7 | abc | 10 |
| 8 | abc | 1 |
需求
按查询词分组,为每组中每个搜索次数值计算排除当前行值的Z-score:
- 以查询词
abc为例,每行计算时需移除当前行的搜索次数值,使用组内其余值计算均值和标准差得到Z-score - 示例:日期1的
abc行用[6,5,3,7,8,10,1]计算,Z-score=-1.32;日期7的abc行用[2,6,5,3,7,8,1]计算,Z-score=2.22
用户已编写自定义计算函数(已修正原函数中的变量笔误):
def zscore_exclusive(arr): newl = [] for index, val in enumerate(arr): l = list(arr) current_val = l.pop(index) arr_popped = np.array(l) avg = np.mean(arr_popped) stdev = np.std(arr_popped) newl.append((current_val - avg) / stdev) return np.array(newl)
解决方案
利用Pandas的groupby结合transform方法,即可将自定义函数应用到每个分组,同时保留原数据的行结构:
import pandas as pd import numpy as np # 构造示例DataFrame df = pd.DataFrame({ '日期': [1,1,2,3,4,4,5,6,7,8], '查询词': ['abc','def','abc','abc','def','abc','abc','abc','abc','abc'], '搜索次数': [2,3,6,5,1,3,7,8,10,1] }) def zscore_exclusive(arr): newl = [] n = len(arr) for index, val in enumerate(arr): l = list(arr) current_val = l.pop(index) arr_popped = np.array(l) avg = np.mean(arr_popped) stdev = np.std(arr_popped) # 处理分组仅2个元素时标准差为0的情况 if stdev == 0: newl.append(0.0) else: newl.append((current_val - avg) / stdev) return np.array(newl) # 应用函数到每个分组,生成新列 df['专属Z-score'] = df.groupby('查询词')['搜索次数'].transform(zscore_exclusive) # 查看格式化后的结果 print(df.round(2))
关键说明
groupby('查询词'):按查询词对数据分组,保证每个分组内的计算独立进行transform(zscore_exclusive):对每个分组的搜索次数列应用自定义函数,transform会自动将计算结果映射回原DataFrame的对应行,无需手动对齐索引- 新增的标准差判断:避免分组仅含2个元素时,移除当前行后剩余1个值导致标准差为0、触发除以0错误的情况
输出结果
运行后将得到包含专属Z-score列的完整DataFrame,其中abc组的结果与需求示例完全匹配。
内容的提问来源于stack exchange,提问作者saigafreak
相关产品推荐
相关产品推荐

