使用groupby+apply结合np.select函数时输出异常,期望得到可合并回原DataFrame的单列标记结果
groupby+apply结合np.select函数时输出异常,期望得到可合并回原DataFrame的单列标记结果
嘿,我来帮你解决这个问题~
你现在遇到的核心问题是:用groupby.apply之后得到的是按星期几分组的结果集合,每个分组对应一个异常值标记的列表,没法直接和原DataFrame的行一一对应。这是因为apply默认会把每个分组的处理结果打包成单个元素,最终返回的是以分组键为索引的Series,而不是和原数据对齐的逐行结果。
解决方案
我们只需要调整处理逻辑,让分组后的结果能自动匹配原DataFrame的索引,这里有两种简单好用的方法:
方法1:修改函数返回带原索引的Series
你的is_outlier函数里,参数x其实是每个分组对应的、带有原DataFrame索引的Series,所以不用把结果转成列表,直接返回标记后的Series即可:
import pandas as pd import numpy as np # 修正原数据的笔误:randint不是randin,2024年是闰年,日期范围共366天 df = pd.DataFrame({'datecol' : pd.date_range('2024-1-1', '2024-12-31'), 'val' : np.random.randint(low = 100, high = 5000, size = 366)}) def is_outlier(x): iqr = x.quantile(.75) - x.quantile(.25) lower_bound = x.quantile(.25) - 1.5*iqr upper_bound = x.quantile(.75) + 1.5*iqr # 判断异常值,直接返回带原索引的Series,转成int类型(1=异常,0=正常) outlier_mask = (x <= lower_bound) | (x >= upper_bound) return outlier_mask.astype(int) # apply后自动拼接成和原df对齐的Series,直接添加为新列 df['outlier_flag'] = df.groupby(df['datecol'].dt.weekday)['val'].apply(is_outlier)
方法2:使用transform代替apply(更推荐)
transform是Pandas专门为这种场景设计的工具:它会自动把分组处理后的结果映射回原数据的对应行,代码更简洁直观:
import pandas as pd import numpy as np df = pd.DataFrame({'datecol' : pd.date_range('2024-1-1', '2024-12-31'), 'val' : np.random.randint(low = 100, high = 5000, size = 366)}) def is_outlier(x): iqr = x.quantile(.75) - x.quantile(.25) lower = x.quantile(.25) - 1.5*iqr upper = x.quantile(.75) + 1.5*iqr return ((x <= lower) | (x >= upper)).astype(int) # 用transform直接得到和原df行数完全一致的结果列 df['outlier_flag'] = df.groupby(df['datecol'].dt.weekday)['val'].transform(is_outlier)
这样处理后,outlier_flag列就会和原DataFrame的每一行一一对应,你可以直接用它做后续的数据分析啦~
备注:内容来源于stack exchange,提问作者Karthik S
相关产品推荐
相关产品推荐

