如何在ggplot2中给箱线图中的异常值点着色?

18

我如何在ggplot2中为异常点着色?我希望它们与箱线图本身颜色相同。使用colour=并不足以实现这一点。

示例:

p <- ggplot(mtcars, aes(factor(cyl), mpg))
p + geom_boxplot(aes(colour=factor(cyl)))

我希望能够根据 factor(cyl) 对离群值进行着色。但是这样做没有效果:

> p <- ggplot(mtcars, aes(factor(cyl), mpg))
> p + geom_boxplot(aes(colour=factor(cyl), outlier.colour=factor(cyl)))

你能否发布一份代码示例供大家参考?这将鼓励更多有用的回答。 - Dinre
@Dinre:好观点,添加了示例。 - user248237
6个回答

31

更新(2015年3月31日):请参见@tarch的ggplot2 >= 1.0.0的解决方案

ggplot2 <= 0.9.3的解决方案如下。


正如@koshke所说,现在可以通过设置outlier.colour = NULL来将异常值的颜色设置为盒线的线条颜色(而不是填充颜色):

p <- ggplot(mtcars, aes(x=factor(cyl), y=mpg, col=factor(cyl)))
p + geom_boxplot(outlier.colour = NULL)

带有彩色异常值的箱线图

  • 必须使用“ou”拼写outlier.colour
  • outlier.colour 必须放在 aes() 的外面。

我发表这篇晚回答是因为我发现自己一遍又一遍地查找它,并且还为相关问题Boxplot, how to match outliers' color to fill aesthetics? 发布了这个答案。


2
你使用的ggplot2版本是什么?在1.0.0版本中,这不会产生有颜色的异常值。 - Hamy
1
@Hamy:我刚刚从0.9.3升级到1.0.0,可以确认这个问题。快速查看帮助文档并没有找到解决方案。也许你可以问一下ggplot2的开发者该怎么做? - cbeleites unhappy with SX
2
@cbeleites 请查看下面tarch提供的最新答案,这是正确的解决方案。 NULL 是异常值颜色的默认值,并且它继承自默认点颜色,因此您必须设置默认点颜色。很抱歉我不得不给这篇帖子投反对票,以便现在正确的答案有更好的机会浮现到顶部。我知道您的答案曾经是正确的,但不幸的是现在已经不再正确了。 - slhck

15

我找到了一个解决方案,可以解决在最新版本的R(@hamy提到的ggplot2 1.0.0版本)中设置geom_boxplot(outlier.colour = NULL)不再起作用的问题。

为了复制@cbeleites提出的行为,您只需要使用以下代码:

update_geom_defaults("point", list(colour = NULL))
m <- ggplot(movies, aes(y = votes, x = factor(round(rating)),
            colour = factor(Animation)))
m + geom_boxplot() + scale_y_log10()

如预期所示,这将生成与线条颜色相匹配的点的图。

当然,如果需要绘制多个图,则应记得恢复默认设置:

update_geom_defaults("point", list(colour = "black"))

通过阅读 Github 上的 ggplot2 更新日志,找到了解决方案:

geom_boxplot() 的离群值使用 geom_point() 的默认颜色、大小和形状。使用 update_geom_defaults() 更改 geom_point() 的默认设置将同样适用于 geom_boxplot() 的离群值。之前无法更改离群值的默认设置。(@ThierryO, #757)

同时发布在此处:ggplot2 boxplot, how do i match the outliers' color to fill aesthetics?


我可以确认这适用于较新的版本 - 这应该是最顶部的答案。 - slhck
对我也有效,同时使用AE拼写color="black" - mavam
可以用!谢谢! - not_a_number

15
为了使异常点的颜色与您的箱线图相同,您需要单独计算异常值并绘制它们。据我所知,内置选项将所有异常值都涂上相同的颜色。 帮助文件示例 使用与“geom_boxplot”帮助文件相同的数据:
ggplot(mtcars, aes(x=factor(cyl), y=mpg, col=factor(cyl))) +
    geom_boxplot()

帮助文件演示

着色异常点

现在可能有更简单的方法来完成这个任务,但我喜欢手动计算,这样我就不必猜测底层发生了什么。使用'plyr'包,我们可以快速获取使用默认(Tukey)方法确定异常值的上限和下限,即任何在范围[Q1 - 1.5 * IQR,Q3 + 1.5 * IQR]之外的点都是异常值。 Q1和Q3是数据的1/4和3/4分位数,IQR = Q3-Q1。我们可以将所有内容写成一个巨大的语句,但由于'plyr'包的'mutate'函数允许我们引用新创建的列,因此我们最好将其拆分为易于阅读/调试的部分:

library(plyr)
plot_Data <- ddply(mtcars, .(cyl), mutate, Q1=quantile(mpg, 1/4), Q3=quantile(mpg, 3/4), IQR=Q3-Q1, upper.limit=Q3+1.5*IQR, lower.limit=Q1-1.5*IQR)
我们使用“ddply”函数,因为我们输入的是数据框并希望得到数据框作为输出(即“d->d” ply)。上面“ddply”语句中的“mutate”函数保留了原始的数据框并添加了额外的列,并且指定的.(cyl)表示要对每个“cyl”值分组进行计算。

此时,我们现在可以绘制箱线图,然后用新的有颜色的点覆盖异常值。
ggplot() +
    geom_boxplot(data=plot_Data, aes(x=factor(cyl), y=mpg, col=factor(cyl))) + 
    geom_point(data=plot_Data[plot_Data$mpg > plot_Data$upper.limit | plot_Data$mpg < plot_Data$lower.limit,], aes(x=factor(cyl), y=mpg, col=factor(cyl)))

离群点的颜色

代码中我们所做的是确定一个空的“ggplot”层,然后使用独立的数据添加箱线图和散点图形。箱线图的几何形状可以使用原始数据框,但我使用我们的新“plot_Data”以保持一致性。然后,点的几何形状仅绘制离群点,使用我们的新“lower.limit”和“upper.limit”列来确定异常状态。由于我们在“x”和“col”美学参数上使用相同的规格说明,因此箱线图和相应的离群点的颜色是自动匹配的。

更新:OP要求对此代码中使用的“ddply”函数进行更详细的解释。这是它的解释:

'plyr'函数族基本上是对数据子集进行函数处理的一种方式。在这个特定的例子中,我们有如下语句:

ddply(mtcars, .(cyl), mutate, Q1=quantile(mpg, 1/4), Q3=quantile(mpg, 3/4), IQR=Q3-Q1, upper.limit=Q3+1.5*IQR, lower.limit=Q1-1.5*IQR)

让我们按照语句的顺序来分解。首先选择“ddply”函数。我们希望计算“mtcars”数据中“cyl”的每个值的下限和上限。我们可以编写一个“for”循环或其他语句来计算这些值,但是然后我们将不得不编写另一个逻辑块来评估异常值状态。相反,我们想使用“ddply”来计算下限和上限,并将这些值添加到每一行。我们选择“ddply”(而不是“dlply”,“d_ply”等),因为我们输入了一个数据框并希望输出一个数据框。这给了我们:

ddply(
我们想在'mtcars'数据框上执行该语句,因此我们添加它。

我们希望对'mtcars'数据框执行该语句,因此将其添加。

ddply(mtcars, 

现在,我们想要使用“cyl”值作为分组变量进行计算。我们使用“plyr”函数 .()来引用变量本身而不是变量的值,像这样:

ddply(mtcars, .(cyl),
下一个参数指定要应用于每个组的函数。我们希望我们的计算将新行添加到旧数据中,因此我们选择'mutate'函数。这将保留旧数据并将新计算作为新列添加。这与其他函数(如'summarize')不同,后者会删除除分组变量之外的所有旧列。
ddply(mtcars, .(cyl), mutate, 

最后一组参数都是我们想要创建的新数据列。我们通过指定名称(不加引号)和表达式来定义它们。首先,我们创建 'Q1' 列。

ddply(mtcars, .(cyl), mutate, Q1=quantile(mpg, 1/4), 

'Q3'列的计算方式类似。

ddply(mtcars, .(cyl), mutate, Q1=quantile(mpg, 1/4), Q3=quantile(mpg, 3/4), 

幸运的是,通过使用“mutate”函数,我们可以将新创建的列用作其他列定义的一部分。这样可以避免编写一个巨大的函数或运行多个函数。我们需要在计算'IQR'变量的四分位距时使用'Q1'和'Q3',而使用'mutate'函数很容易实现。

ddply(mtcars, .(cyl), mutate, Q1=quantile(mpg, 1/4), Q3=quantile(mpg, 3/4), IQR=Q3-Q1, 
我们现在终于到达了我们想要的地方。从技术上讲,我们不需要“Q1”、“Q3”和“IQR”列,但是它确实使我们的下限和上限方程式更易于阅读和调试。我们可以像理论公式一样编写表达式:limits=+/- 1.5 * IQR
ddply(mtcars, .(cyl), mutate, Q1=quantile(mpg, 1/4), Q3=quantile(mpg, 3/4), IQR=Q3-Q1, upper.limit=Q3+1.5*IQR, lower.limit=Q1-1.5*IQR)

为了提高可读性,删除中间列后,新数据框如下:

plot_Data[, c(-3:-11)]
#     mpg cyl    Q1    Q3  IQR upper.limit lower.limit
# 1  22.8   4 22.80 30.40 7.60      41.800      11.400
# 2  24.4   4 22.80 30.40 7.60      41.800      11.400
# 3  22.8   4 22.80 30.40 7.60      41.800      11.400
# 4  32.4   4 22.80 30.40 7.60      41.800      11.400
# 5  30.4   4 22.80 30.40 7.60      41.800      11.400
# 6  33.9   4 22.80 30.40 7.60      41.800      11.400
# 7  21.5   4 22.80 30.40 7.60      41.800      11.400
# 8  27.3   4 22.80 30.40 7.60      41.800      11.400
# 9  26.0   4 22.80 30.40 7.60      41.800      11.400
# 10 30.4   4 22.80 30.40 7.60      41.800      11.400
# 11 21.4   4 22.80 30.40 7.60      41.800      11.400
# 12 21.0   6 18.65 21.00 2.35      24.525      15.125
# 13 21.0   6 18.65 21.00 2.35      24.525      15.125
# 14 21.4   6 18.65 21.00 2.35      24.525      15.125
# 15 18.1   6 18.65 21.00 2.35      24.525      15.125
# 16 19.2   6 18.65 21.00 2.35      24.525      15.125
# 17 17.8   6 18.65 21.00 2.35      24.525      15.125
# 18 19.7   6 18.65 21.00 2.35      24.525      15.125
# 19 18.7   8 14.40 16.25 1.85      19.025      11.625
# 20 14.3   8 14.40 16.25 1.85      19.025      11.625
# 21 16.4   8 14.40 16.25 1.85      19.025      11.625
# 22 17.3   8 14.40 16.25 1.85      19.025      11.625
# 23 15.2   8 14.40 16.25 1.85      19.025      11.625
# 24 10.4   8 14.40 16.25 1.85      19.025      11.625
# 25 10.4   8 14.40 16.25 1.85      19.025      11.625
# 26 14.7   8 14.40 16.25 1.85      19.025      11.625
# 27 15.5   8 14.40 16.25 1.85      19.025      11.625
# 28 15.2   8 14.40 16.25 1.85      19.025      11.625
# 29 13.3   8 14.40 16.25 1.85      19.025      11.625
# 30 19.2   8 14.40 16.25 1.85      19.025      11.625
# 31 15.8   8 14.40 16.25 1.85      19.025      11.625
# 32 15.0   8 14.40 16.25 1.85      19.025      11.625

仅为对比,如果我们使用'summarize'函数执行相同的'ddply'语句,我们将会得到所有相同的答案,但是没有其他数据的列。

ddply(mtcars, .(cyl), summarize, Q1=quantile(mpg, 1/4), Q3=quantile(mpg, 3/4), IQR=Q3-Q1, upper.limit=Q3+1.5*IQR, lower.limit=Q1-1.5*IQR)
#   cyl    Q1    Q3  IQR upper.limit lower.limit
# 1   4 22.80 30.40 7.60      41.800      11.400
# 2   6 18.65 21.00 2.35      24.525      15.125
# 3   8 14.40 16.25 1.85      19.025      11.625

1
谢谢,这很有帮助但非常复杂…难道不可能只获取factor(cyl)的颜色并手动输入一个向量到outlier.colour中吗?也就是直接告诉ggplot每个异常值应该是什么颜色,而不是计算异常点? - user248237
2
据我所知,outlier.colour=参数不允许使用颜色向量。您试图做一些超出'geom_boxplot'函数预期行为的事情,因此无法从函数内部完成。此外,几行代码并不是我所谓的“非常复杂”。比单个参数复杂?当然。比编写新的箱线图函数复杂?远远不是。 - Dinre
@user248237dfsf 确实我们正在重新计算,但该方法直接来自“geom_boxplot”帮助文件。它应该是精确的。要确定,唯一的方法是手动将数字提供给两个几何图形,这也是您应该做的,如果您想要一个真正可重现的示例。任何依赖于函数默认值的内容都不是真正可重现的,因为默认值可能会在更新中更改。 - Dinre
谢谢。我仍然不太理解"plot_Data <- ddply(mtcars, .(cyl), mutate, Q1=quantile(mpg, 1/4), Q3=quantile(mpg, 3/4), IQR=Q3-Q1, upper.limit=Q3+1.5*IQR, lower.limit=Q1-1.5*IQR)" - 如果您能解释一下.(cyl)是什么以及mutate如何工作,我会很感激。我发现ddply非常难懂。 - user248237
@Dinre 这是我见过的最好的 plyr 简短教程之一! - Gregor Thomas
显示剩余2条评论

7
如果需要根据不同的因素(而不是用于制作箱线图组的相同因素)更改异常值点的形状或颜色,则可以采用@Dinre的答案。只有当颜色未用于箱线图本身时,才能更改点的颜色(您不能使用两个变量进行颜色)。使用数据plot_Data和@Dinre答案中的代码-异常值的颜色取决于因子carb。通过向geom_boxplot()添加参数outlier.shape = NA,可以删除原始异常值,以确保它们不会被geom_point()覆盖。
ggplot() +
  geom_boxplot(data=plot_Data, aes(x=factor(cyl), y=mpg),outlier.shape = NA) + 
  geom_point(data=plot_Data[plot_Data$mpg > plot_Data$upper.limit | 
                              plot_Data$mpg < plot_Data$lower.limit,], 
             aes(x=factor(cyl), y=mpg, color=factor(carb)))

enter image description here

更改点的形状:

ggplot() +
  geom_boxplot(data=plot_Data, aes(x=factor(cyl), y=mpg),outlier.shape = NA) + 
  geom_point(data=plot_Data[plot_Data$mpg > plot_Data$upper.limit | 
                              plot_Data$mpg < plot_Data$lower.limit,], 
             aes(x=factor(cyl), y=mpg, shape=factor(carb)))

enter image description here


谢谢,但我正在尝试根据变量条件对其进行着色。这似乎不起作用:`> p <- ggplot(mtcars, aes(factor(cyl), mpg))
p + geom_boxplot(aes(outlier.colour = factor(cyl)), outlier.size = 3)`
- user248237
当您更改形状时,是否已经将“geom_boxplot()”调用中的形状异常值与圆形异常值重叠?您应该/可以关闭“geom_boxplot()”调用中的异常值吗? - Epimetheus
1
@John - 你可以使用参数outlier.shape=NA来删除原始的异常值。我已经更新了我的答案。 - Didzis Elferts

3
离群值在ggplot2 1.0.1.9003中会自动继承盒图的颜色。详情请参见:https://github.com/hadley/ggplot2/issues/1400
```{r}
library(ggplot2)
point_size=10
ggplot(mtcars, aes(x=factor(cyl), y=mpg, col=factor(cyl))) +
geom_boxplot(outlier.size = point_size)
```

箱线图


2
如果您真的想保持方框为黑色:
另一个可能的解决方案是:
1. 存储一个变量来确定点是否为异常值, 2. 使用 outlier.color = NA 来抑制异常值绘制,并且 3. 仅绘制那些是异常值的点,并在此调用 geom_point() 时使用 col 美学。
步骤1:定义一个函数来确定一个点是否为异常值:
is.outlier <- function (x) {
  x < quantile(x, .25) - 1.5 * IQR(x) |
    x > quantile(x, .75) + 1.5 * IQR(x)
}

步骤二:使用此函数对数据进行分组并计算异常值。最初的回答中提到了该方法。
diamonds %>% group_by(cut) %>%
  mutate(outlier.p = is.outlier(price)) %>%
  ungroup() -> diamonds

第三步:创建图表

ggplot(diamonds, aes(x = cut, y = price, fill = cut)) +
  geom_boxplot(outlier.color = NA) +
  geom_point(data = diamonds[diamonds$outlier.p,], aes(col = cut))

请注意,在geom_point()调用(最后一行)中必须对数据集进行子集划分,否则将绘制所有点。

图表


注:本文涉及IT技术。

2
对于分组箱线图,这变得有些困难,但类似的解决方案是调用两次geom_boxplot。首先将填充和颜色映射到'cut',然后再次使用outlier.color = NA(因此,第二个不会绘制异常值,但会制作黑色轮廓和中位数线)。 - ldecicco
@idecicco,你能给一个实现这个功能的代码示例吗? - Dylan Dijk
@idecicco,你能给一个实现这个功能的代码示例吗? - Dylan Dijk

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