在 ggplot2 的箱线图中忽略异常值

193
我该如何在ggplot2的箱线图中忽略异常值?我不仅仅希望它们消失(即outlier.size=0),而是希望它们被忽略,使得Y轴刻度显示第1/3百分位数。我的异常值导致“箱子”缩小到几乎成为一条线。有没有一些技巧来处理这个问题? 编辑 这里有一个例子:
y = c(.01, .02, .03, .04, .05, .06, .07, .08, .09, .5, -.6)
qplot(1, y, geom="boxplot")

输入图像描述


一些样本数据和可重现的示例将使帮助您变得更加容易。 - Andrie
3
我的文件大小为200兆!请拿一份包含许多数据点在第一四分位数和第三四分位数之间,以及少量异常值(只需要1个)的数据集来说明。如果异常值远离第一/第三四分位数,那么盒子必然会缩小以容纳这个异常值。 - Suraj
是的,那就是我想要的。请构建这样一个数据集,并使用dput()将其连同您使用的ggplot()语句一起发布在此处。帮助我们来帮助您。 - Andrie
你不能只是改变y轴的限制来“缩放”到你感兴趣的y轴部分吗? - Gavin Simpson
@Gavin Simpson - 这与下面 @Richie Cotton 的解决方案是一样的吗? - Suraj
2
让我看看...哦,对不起。只需在数据上执行fivenum()以提取箱线图上使用的上下限,并将该输出用于@Ritchie所示的scale_y_continuous()调用中。使用R和ggplot提供的工具可以轻松自动化此过程。如果您还需要包括whiskers,请考虑使用boxplot.stats()获取whiskers的上限和下限,并在scale_y_continuous()中使用它们。 - Gavin Simpson
8个回答

295

使用geom_boxplot(outlier.shape = NA)来不显示异常值,并使用scale_y_continuous(limits = c(lower, upper))来更改轴限制。

一个例子。

n <- 1e4L
dfr <- data.frame(
  y = exp(rlnorm(n)),  #really right-skewed variable
  f = gl(2, n / 2)
)

p <- ggplot(dfr, aes(f, y)) + 
  geom_boxplot()
p   # big outlier causes quartiles to look too slim

p2 <- ggplot(dfr, aes(f, y)) + 
  geom_boxplot(outlier.shape = NA) +
  scale_y_continuous(limits = quantile(dfr$y, c(0.1, 0.9)))
p2  # no outliers plotted, range shifted

实际上,就像Ramnath在他的答案中所示(Andrie在评论中也是如此),更有意义的做法是在计算统计量后通过coord_cartesian裁剪刻度。

coord_cartesian(ylim = quantile(dfr$y, c(0.1, 0.9)))

(你可能仍然需要使用scale_y_continuous来修复坐标轴的断点.)


1
那么我需要计算下限/上限 - 也许通过计算第1个/第3个百分位数来实现?这意味着没有自动魔法可以告诉ggplot2忽略异常值并进行智能缩放吗? - Suraj
46
使用scale_y_continuous(limits=...)需要小心。这会删除超出限制范围的数据,然后进行统计计算。换句话说,平均值和其他摘要信息也会受到影响。如果这正是您想要的结果,那就太好了。另一种选择是使用coord_cartesian(limits=...),它可以“缩放”视图而不删除数据或影响摘要信息。 - Andrie
@Andrie - 谢谢!我不想影响均值和其他摘要。 - Suraj
1
coord_cartesian() 在我使用时与 coord_flip() 不兼容,因此我更喜欢 scale_y_continuous() - PatrickT
1
这是最好的解决方案。我想隐藏离群值的原因是因为我还使用geom_jitter绘制了抖动点。在这种情况下,离群值只会妨碍视线,使得看起来有比实际更多的点。 - williamsurles
这个修复是否可以与分面图一起使用,为每个分面计算不同的限制?我尝试过在我的数据上使用它,因为我希望我的x轴比例尺是自由的,但排除离群值 - 但coord_cartesian参数会全局设置坐标轴限制。 - Lucy Wheeler

161

这里有一个使用boxplot.stats的解决方案

# create a dummy data frame with outliers
df = data.frame(y = c(-100, rnorm(100), 100))

# create boxplot that includes outliers
p0 = ggplot(df, aes(y = y)) + geom_boxplot(aes(x = factor(1)))


# compute lower and upper whiskers
ylim1 = boxplot.stats(df$y)$stats[c(1, 5)]

# scale y limits based on ylim1
p1 = p0 + coord_cartesian(ylim = ylim1*1.05)

23
自动计算加1分,使用coord_cartesian进行缩放而不是排除数据也加1分。 - Ben Bolker
4
@Ben - 你有两个账户?=)@Ramnath - 这是一个非常优雅的解决方案。 - Suraj
7
使用上述方法,限制值可能会因为一侧的小极端值和另一侧的大极端值而产生偏差,例如ylim <- c(-0.1, 1000) * 1.05得到 [1] 0.105 1050。为了获得均匀分布在平均线周围的限制值,您可以使用 ylim + c(-0.05, 0.05) * diff(ylim) / 2。在我看来更美观。 - Bram Visser
4
@Ramnath,$stats[c(1,5)]是什么意思? - lukeg
5
如果使用 facet_grid(),该图不起作用。那么你会得到多个箱线图而不是一个,因此你无法获得正确的限制范围。 - WitheShadow
显示剩余6条评论

17

我曾经遇到同样的问题,使用boxplot.stats预先计算了Q1、Q2、中位数、ymin和ymax:

# Load package and generate data
library(ggplot2)
data <- rnorm(100)

# Compute boxplot statistics
stats <- boxplot.stats(data)$stats
df <- data.frame(x="label1", ymin=stats[1], lower=stats[2], middle=stats[3], 
                 upper=stats[4], ymax=stats[5])

# Create plot
p <- ggplot(df, aes(x=x, lower=lower, upper=upper, middle=middle, ymin=ymin, 
                    ymax=ymax)) + 
    geom_boxplot(stat="identity")
p
结果是一个不包含异常值的箱线图。 enter image description here

12

一种方法是通过两次操作来进行数据Winsorizing:

  1. 首先进行第一遍操作,确定边界,可以在给定百分位数处截断,或者在均值上方N个标准差处截断等等。

  2. 第二遍操作,将超出给定边界的值设置为该边界的值。

需要强调的是,这是一种旧式方法,应该被更现代、更健壮的技术所替代,但你仍然会经常遇到它。


2
谁刚刚悄悄地点了踩:请留下评论解释为什么 - Dirk Eddelbuettel
不是我。只是想补充一下,在环境数据中,胡须通常停在百分位数(通常是第10和第90个百分位)似乎非常普遍。 - Richie Cotton
我曾经默默无闻地点了个赞,但希望我还能再点一个。Winsorizing 在经济学和金融学中几乎总是会用到。如果 SFun 有离群值破坏了数据可视化,那么它们对数据分析的影响又是什么呢? - Richard Herron
重新阅读这篇文章,你提到温莎化是一种较旧的技术...那么一些更现代的技术有哪些? - Suraj
1
一般来说,健壮的方法是过去30多年的发展。 - Dirk Eddelbuettel
值得一提的是,有许多方便的函数可用(DescTools::Winsorizestatar::winsorizerobustHD::winsorize - 我遇到了这些,但我认为还有更多)。 - Konrad

7

1
谢谢!已经用我的数据测试过了,完美运行!我会推荐这个解决方案,尽管我不确定 GitHub 的稳定性和长期支持。 - Gildas
嗨 @Gildas,这是一个长期支持的包,也是我每天都在使用的包,https://github.com/rpkgs/Ipaper。 - Dongdong Kong
2
这与geom_boxplot()有何不同,除了更改箱体和/或须的宽度选项? - jtr13
这里控制参数widthwidth.errorbar。您可以在 https://rpkgs.github.io/gg.layers/reference/geom_boxplot2.html 中找到示例。 - Dongdong Kong
我在那个页面上没有看到任何关于这两者之间区别的解释。 - Herman Toothrot

5

如果您想强制将胡须延伸到最大和最小值,您可以调整coef参数。默认情况下,coef的值为1.5(即胡须的默认长度是IQR的1.5倍)。

# Load package and create a dummy data frame with outliers 
#(using example from Ramnath's answer above)
library(ggplot2)
df = data.frame(y = c(-100, rnorm(100), 100))

# create boxplot that includes outliers
p0 = ggplot(df, aes(y = y)) + geom_boxplot(aes(x = factor(1)))

# create boxplot where whiskers extend to max and min values
p1 = ggplot(df, aes(y = y)) + geom_boxplot(aes(x = factor(1)), coef = 500)

image of p0

image of p1


1

简单、粗糙而有效。 geom_boxplot(outlier.alpha = 0)


3
你好,这并没有解决y轴扩展过多的问题。原帖作者说:“我不想让它们消失(即outlier.size=0),而是希望它们被忽略掉,以便y轴标尺显示第1/3个百分位数。” - Paul

-1
geom_boxplot函数的"coef"选项允许根据四分位数范围来改变异常值的截断点。这个选项在stat_boxplot函数的文档中有详细说明。如果要禁用异常值(换句话说,将它们视为常规数据),可以将截断点的默认值1.5替换为一个非常高的值。
library(ggplot2)
# generate data with outliers:
df = data.frame(x=1, y = c(-10, rnorm(100), 10)) 
# generate plot with increased cutoff for outliers:
ggplot(df, aes(x, y)) + geom_boxplot(coef=1e30)

4
它只是扩展了图表的须,没有改变任何比例尺度。 - moodymudskipper

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接