如何在Pandas分组DataFrame中添加异常值占比列?
解决分组计算成本异常值占比的问题
嘿,我来帮你搞定这个异常值占比的计算问题!首先咱们先理清楚你遇到的核心问题,然后一步步修正代码,让它正常工作。
先拆解问题
你原来的思路是对的:按code分组后,聚合出sum和mean,再新增异常值占比列。但问题出在自定义函数的调用方式,以及一些不必要的冗余代码上。
第一步:修正自定义异常值函数
首先,咱们把is_outlier函数调整得更清晰,确保它能正确接收每个组的cost序列,返回一个标量(异常值占比)。这里要注意:
- 要导入
scipy.stats的iqr和numpy - 明确异常值的判断逻辑(上下界都算,还是只算上界,根据你的需求调整)
- 处理观测数不足的情况(返回
NaN)
修正后的函数:
import pandas as pd import numpy as np from scipy.stats import iqr def is_outlier(s): # 生产环境中改成 >=100,示例中暂时放宽条件 if len(s) >= 1: q1 = s.quantile(0.25) q3 = s.quantile(0.75) iqr_val = iqr(s) # 计算异常值上下界(你可以只保留上界或下界,根据需求调整) lower_bound = q1 - 1.5 * iqr_val upper_bound = q3 + 1.5 * iqr_val # 标记异常值并计算占比 outlier_mask = (s < lower_bound) | (s > upper_bound) return outlier_mask.mean() else: return np.nan
第二步:正确分组聚合(一步到位)
你原来的groupby代码里.apply(pd.Series)是多余的,因为agg(['sum', 'mean'])已经直接返回DataFrame了。咱们可以直接在agg里把自定义函数加进去,一步得到所有需要的列:
# 示例数据 data = {'code': ['a', 'b', 'a', 'c', 'c', 'c', 'c'], 'cost': [10, 20, 100, 10, 10, 500, 10]} df = pd.DataFrame(data) # 一步完成分组聚合 grouped = df.groupby('code')['cost'].agg( sum='sum', mean='mean', is_outlier=is_outlier ).reset_index() print(grouped)
运行后,如果你把函数里的判断条件改成len(s)>=1,就能得到符合你预期格式的结果(根据异常值定义不同,数值会对应变化)。
备选方案:给已有分组结果加列
如果你已经有了包含sum和mean的grouped DataFrame,也可以单独计算异常值占比再合并进去:
# 先计算sum和mean grouped = df.groupby('code')['cost'].agg(['sum', 'mean']).reset_index() # 计算每个组的异常值占比 outlier_ratios = df.groupby('code')['cost'].apply(is_outlier) # 合并到grouped中 grouped['is_outlier'] = grouped['code'].map(outlier_ratios)
为什么原来的写法不行?
- 你原来的
.apply(pd.Series)是冗余操作,agg(['sum','mean'])已经返回结构化的DataFrame了。 - 如果直接把
is_outlier放进agg的列表里(比如agg(['sum','mean', is_outlier])),虽然也能工作,但用字典形式指定列名会更清晰,避免自动生成奇怪的列名。 - 确保函数返回的是标量(比如
mean()的结果是单个数值),而不是Series,这样agg才能正确处理。
内容的提问来源于stack exchange,提问作者nvergos
相关产品推荐
相关产品推荐

