Tidyverse单管道实现基于IQR的分组异常值过滤
Tidyverse单管道实现IQR规则异常值过滤
核心结论
- 该需求完全可以在单条管道内实现,不需要拆分第二条管道操作
- 之前写法无法回传阈值的核心原因是误用了
summarise():该函数会按分组将原始数据折叠为每组1行的统计结果,丢失了原始观测行,自然无法完成过滤 - 除了手动计算上下限,也有封装好的函数可以直接调用,不需要手写分位数、IQR计算逻辑
原生dplyr单管道实现方法
把summarise()替换为mutate()即可:mutate()不会折叠原始行,会为分组内的每一行都添加上计算好的统计阈值,后续直接接filter()判断即可完成过滤,全程在同一条管道中运行。
示例代码:
iris %>% group_by(Species) %>% mutate( # 提前计算Q1/Q3/IQR,避免重复计算 Q1 = quantile(Sepal.Length, 0.25, na.rm = TRUE), Q3 = quantile(Sepal.Length, 0.75, na.rm = TRUE), IQR_val = Q3 - Q1, upper = Q3 + 1.5 * IQR_val, lower = Q1 - 1.5 * IQR_val ) %>% # 过滤保留非异常值 filter(Sepal.Length >= lower, Sepal.Length <= upper) %>% # 移除中间计算用的阈值列,解除分组 select(-c(Q1, Q3, IQR_val, upper, lower)) %>% ungroup()
注意:不要把列名命名为
IQR,会和IQR()函数重名,容易引发计算错误;如果数据存在缺失值,记得把na.rm参数设为TRUE。
更简便的封装实现
如果不想手动编写分位数、上下限计算逻辑,可以直接调用现成的异常值判断函数,适配dplyr管道和分组操作:
用rstatix包的is_outlier()函数,默认就采用1.5*IQR的异常值判定规则,代码可以简化为:
library(rstatix) iris %>% group_by(Species) %>% # is_outlier返回TRUE代表是异常值,取反保留正常观测 filter(!is_outlier(Sepal.Length)) %>% ungroup()
如果需要调整异常值判定的严苛程度,只需要修改coef参数即可,比如要识别3倍IQR之外的极端异常值,就写成is_outlier(Sepal.Length, coef = 3),不需要手动调整计算公式。
内容的提问来源于stack exchange,提问作者SysRIP
相关产品推荐
相关产品推荐

