手动计算与ggplot箱线图统计量差异解析及批量去异常值方法需求
关于箱线图异常值计算差异与批量去异常值的问题
我将多个数据集整合到一个dataframe中,需要去除其中的异常值。在尝试不同方法计算上下阈值时,发现ggplot箱线图的结果与手动计算存在差异。我需要解决两个问题:
- 理解该差异的原因;
- 找到一种借助dplyr对多个相似数据集批量去除异常值的便捷方法。
示例数据(取自boxplot.stats帮助文档)
library(tidyverse) require(stats) x <- c(1:100, 1000) (b1 <- boxplot.stats(x))
这段代码返回ymin=1、max=100,1000被识别为明显异常值。
原始数据集(讨论基于简化数据集展开)
# 小型数据集 values_A30 <- c(0.2079762,0.2605029,0.3054334,0.304067,0.8696487,0.3470931,0.2560001,0.3096838,0.2887556,0.3741472,0.2178375,0.2234682,0.2628923,0.2745458,0.5438208,0.7068278,0.6492924,1.100175,0.2740491,0.2849299,0.7562737,0.3009749,0.2598575,0.3460925,0.3265929,0.4208336,0.4353992,1.132036,0.3856708,0.1978752,0.3676808,0.4196799,0.4486595,0.3282394,0.3725664,0.385373,0.3680049,0.7875058,0.8098903,0.741165,1.260887,0.3521471,0.3883195,1.17124,0.3225514,0.3492051) bpdata <- data.frame(Var1 = "A", SSW = 30, Value = values_A30)
我的操作过程
我通常先通过ggplot绘制箱线图获得直观印象:
# 测试绘图 ggplot(as.data.frame(x), aes(x = NA, y = x)) + geom_boxplot() # 提取箱线图统计值 gg_stats <- ggplot_build( ggplot(as.data.frame(x), aes(x = NA, y = x)) + geom_boxplot() )$data[[1]] |> select(1:9) # 手动计算统计值 man_stats <- as.data.frame(x) |> summarise( Qu1 = quantile(x, 0.25), Qu3 = quantile(x, 0.75), IQR = IQR(x) ) |> mutate(ymin = Qu1 - (1.5 * IQR), ymax = Qu3 + (1.5 * IQR))
boxplot.stats与ggplot_build的结果一致,但手动计算得到的结果不同:ymin=-49、ymax=151。四分位数和IQR的数值是正确的,计算过程也不复杂,请问我哪里出错了?
解析与解决方案
差异原因总结
- 阈值定义不同:手动计算的
Q1 - 1.5*IQR和Q3 + 1.5*IQR是异常值的理论判定阈值(基于异常值去除前的数据计算),而boxplot.stats及ggplot箱线图输出的ymin/ymax是剔除异常值后数据集中实际存在的最小/最大数值,这是核心差异点。 - 算法细节差异:
quantile和IQR函数的底层算法(通过type参数控制)不同,也可能导致数值上的细微偏差。
借助dplyr批量去除异常值的方法
方法1:基于理论阈值过滤
按分组计算每组的异常值判定阈值,再过滤掉超出范围的数值:
# 按Var1和SSW分组批量去除异常值 cleaned_data <- bpdata |> group_by(Var1, SSW) |> mutate( q1 = quantile(Value, 0.25), q3 = quantile(Value, 0.75), iqr_val = IQR(Value), lower_threshold = q1 - 1.5 * iqr_val, upper_threshold = q3 + 1.5 * iqr_val ) |> filter(Value >= lower_threshold & Value <= upper_threshold) |> ungroup() |> select(-q1, -q3, -iqr_val, -lower_threshold, -upper_threshold) # 移除临时计算列
方法2:匹配boxplot.stats结果
如果需要和boxplot.stats的异常值判定完全一致,可直接调用该函数筛选数据:
# 按分组匹配boxplot.stats的异常值判定结果 cleaned_data_boxplot <- bpdata |> group_by(Var1, SSW) |> filter(Value %in% boxplot.stats(Value)$stats) |> ungroup()
内容的提问来源于Stack Exchange,提问作者Sulfatide
相关产品推荐
相关产品推荐

