ggplot2:在不同图层上应用多个颜色比例尺或系统地改变颜色?

34

当我制作箱线图时,我喜欢在背景中显示原始数据,就像这样:

library(ggplot2)
library(RColorBrewer)

cols = brewer.pal(9, 'Set1')

n=10000
dat = data.frame(value=rnorm(n, 1:4), group=factor(1:4))

ggplot(dat, aes(x=group, y=value, color=group, group=group)) +
  geom_point(position=position_jitter(width=0.3), alpha=0.1) +
  scale_color_manual(values=cols) +
  geom_boxplot(fill=0, outlier.size=0)

在此输入图像描述

然而,当点过于密集时,我的箱线图完全消失了,我并不喜欢这个情况。我知道我可以调整alpha,但在某些情况下这是可行的,但当我的分组具有不同的密度时就不行了(例如,如果我将alpha减小到足以使最暗的组不遮盖箱线图,则最轻的组将完全消失)。我试图做的是系统地改变箱线图的颜色 - 变得稍微深一些,以便它们即使在背景点达到最大alpha时也能显示出来。例如:

plot(1:9, rep(1, 9), pch=19, cex=2, col=cols)
cols_dk = rgb2hsv(col2rgb(brewer.pal(9, 'Set1'))) - c(0, 0, 0.2)
cols_dk = hsv(cols_dk[1,], cols_dk[2,], cols_dk[3,])
points(1:9, rep(1.2, 9), pch=19, cex=2, col=cols_dk)

在此输入图片描述

目前我还没有找到一种方法来为geom_boxplot层中的不同scale_color进行伪装(如果有这样一种方法,似乎这是最简单的路线)。也没有找到一个简单的语法来系统地调整颜色,就像你可以轻松地偏移连续的美学属性一样,如aes(x = x + 1)

我能够得到的最接近的方法是完全复制因子的级别...

ggplot(dat, aes(x=group, y=value, color=group, group=group)) +
  geom_point(position=position_jitter(width=0.3), alpha=0.1) +
  scale_color_manual(values=c(cols[1:4], cols_dk[1:4])) +
  geom_boxplot(aes(color=factor(as.numeric(group)+4)), fill=0, outlier.size=0)

在此输入图片描述

但是我现在必须处理那个丑陋的图例。有更好的想法吗?


2
让盒子变成黑色怎么样? - kohske
@kohske所说的是我的第一个想法,但我认为这会违反alpha值感染图例并使颜色无法读取(至少在0.9.0再次发布几周之前,我认为)。 - joran
黑色/灰色绝对是可以接受的(见http://i.imgur.com/7KKg2.png),但我不喜欢它会有点压倒/分散我通常试图突出的因子编码。我想,如果我能保持相同的颜色方案但稍微偏移一下,那就更好了。 - John Colby
2
曾经有过一次关于 hcl 颜色比例尺的讨论,您可以独立地映射三个参数。我认为这可能是一个不错的选择。 - baptiste
建议的替代方案:https://github.com/hadley/ggplot2/issues/723 - naught101
显示剩余3条评论
5个回答

23

2012年11月新增的晚回答:

由于一些非常好的答案需要较旧的ggplot2版本,并且人们仍在参考此页面,因此我将使用ggplot2 0.9.0+更新它,并提供一个极其简单的解决方案。

我们只需添加第二个geom_boxplot图层,它与第一个图层完全相同,只是我们使用scales::alpha()分配了一个固定的颜色,以便第一个箱线图可见。

library(scales) # for alpha function
ggplot(dat, aes(x=group, y=value, color=group, group=group)) +
  geom_point(position=position_jitter(width=0.3), alpha=0.2) +
  geom_boxplot(size=1.4,fill=0, outlier.size=0)+
  geom_boxplot(size=1.4,fill=0, outlier.size=0, color=alpha("black",0.3))

编辑: TobiO指出fill=0已经无效了。可以使用fill=NAalpha=0来代替。这似乎是由于在R 3.0.0中col2rgb()的更改造成的。

较暗箱形图下的抖动点


14

目前,你可以定义自己的版本 GeomBoxplot(称其为GeomPlotDark),与原始版本唯一不同的是它在绘制之前将颜色变暗。

使用proto,你可以通过创建一个继承自GeomBoxplot的proto对象GeomBoxplotDark来实现这一点,并仅在其draw函数中有所不同。大部分draw函数的定义源自于GeomBoxplot代码,我已经用类似于# ** ... **的注释标注了我更改的行:

require(ggplot2)

GeomBoxplotDark <- proto(ggplot2:::GeomBoxplot,
  draw <- function(., data, ..., outlier.colour = "black", outlier.shape = 16, outlier.size = 2) {
    defaults <- with(data, {                               # ** OPENING "{" ADDED **
    cols_dk <- rgb2hsv(col2rgb(colour)) - c(0, 0, 0.2)     # ** LINE ADDED        **
    cols_dk <- hsv(cols_dk[1,], cols_dk[2,], cols_dk[3,])  # ** LINE ADDED        **
    data.frame(x = x, xmin = xmin, xmax = xmax,
      colour = cols_dk,                                    # ** EDITED, PASSING IN cols_dk **
      size = size,
      linetype = 1, group = 1, alpha = 1,
      fill = alpha(fill, alpha),
      stringsAsFactors = FALSE
    )})                                                    # ** CLOSING "}" ADDED **
    defaults2 <- defaults[c(1,1), ]

    if (!is.null(data$outliers) && length(data$outliers[[1]] >= 1)) {
      outliers_grob <- with(data,
        GeomPoint$draw(data.frame(
          y = outliers[[1]], x = x[rep(1, length(outliers[[1]]))],
          colour=I(outlier.colour), shape = outlier.shape, alpha = 1,
          size = outlier.size, fill = NA), ...
        )
      )
    } else {
      outliers_grob <- NULL
    }

    with(data, ggname(.$my_name(), grobTree(
      outliers_grob,
      GeomPath$draw(data.frame(y=c(upper, ymax), defaults2), ...),
      GeomPath$draw(data.frame(y=c(lower, ymin), defaults2), ...),
      GeomRect$draw(data.frame(ymax = upper, ymin = lower, defaults), ...),
      GeomRect$draw(data.frame(ymax = middle, ymin = middle, defaults), ...)
    )))
  }
)

然后创建一个geom_boxplot_dark(),供用户调用,并适当地包装对GeomBoxplotDark$new()的调用:

geom_boxplot_dark <- function (mapping = NULL, data = NULL, stat = "boxplot", position = "dodge", 
    outlier.colour = "black", outlier.shape = 16, outlier.size = 2, 
    ...) 
GeomBoxplotDark$new(mapping = mapping, data = data, stat = stat, 
    position = position, outlier.colour = outlier.colour, outlier.shape = outlier.shape, 
    outlier.size = outlier.size, ...)

最后,使用与您原始调用几乎相同的代码尝试它,只需将对geom_boxplot()的调用替换为对geom_boxplot_dark()的调用:

library(ggplot2)
library(RColorBrewer)

cols = brewer.pal(9, 'Set1')

n=10000
dat = data.frame(value=rnorm(n, 1:4), group=factor(1:4))

ggplot(dat, aes(x=group, y=value, color=group, group=group)) +
  geom_point(position=position_jitter(width=0.3), alpha=0.1) +
  scale_color_manual(values=cols) +
  geom_boxplot_dark(fill=0, outlier.size=0)

我认为得到的图表看起来相当不错。经过一些微调,并直接查看(而不是作为上传文件),它将会非常棒:

在此输入图片描述


4
我认为你可以通过继承GeomBoxplot而不是Geom来简化代码;特别是你可以避免编写重复的.$examples等内容。只需使用.$draw方法即可。 - baptiste
1
@baptiste -- 非常感谢您的建议!我已经编辑了我的问题以纳入它。我现在才开始学习proto对象,而您几乎将我的理解能力翻了一倍,所以也感谢您;) - Josh O'Brien
我想我会接受这一个。尽管有更多的代码,但我可以把它扔进一个我引用的常见函数文件中,然后能够在不同的绘图中灵活使用它,具有更多/更少的因子水平等。完美的解决方案是像巴蒂斯特提到的那样创建一个新比例尺,但这是次佳选择。太棒了。谢谢大家! - John Colby
2
FYI,ggplot2正在逐渐远离proto,因此在未来的版本中(也许是ggplot2 1.0?)这将无法使用。 - hadley
@hadley -- 谢谢你提醒!出于好奇,你是因为性能原因、因为它强制你编写某种类型的代码还是出于其他原因而远离proto的呢? - Josh O'Brien
主要是因为没有人理解它,而且它使得分析变得困难。 - hadley

8
你可以破解这个图形的传奇,但是它似乎很难放置。
 g = ggplotGrob(p)
 grid.draw(g)
 legend = editGrob(getGrob(g, gPath("guide-box","guide"), grep=TRUE), vp=viewport())
 new = removeGrob(legend, gPath("-7|-8|-9|-10"), grep=TRUE, glob=T)
 ## grid.set(gPath("guide-box"), legend, grep=TRUE) # fails for some reason
 grid.remove(gPath("guide-box"), grep=TRUE, global=TRUE)
 grid.draw(editGrob(new, vp=viewport(x=unit(1.4,"npc"), y=unit(0.1,"npc"))))

enter image description here


简短而精练 - 太棒了!这也是一个很好的贡献,因为它从与Josh相反的角度来解决问题。 - John Colby
3
你能具体说明休息时间吗? - hadley
@hadley 啊,我也不知道那个会起作用。我以为它会剪切比例尺呢。太好了! - John Colby

3

ggplot2语法似乎发生了变化,我花了一点时间才弄清楚:

fill=0对我来说没有效果(不再有效吗?)

然而,必须改为alpha=0才能使方框透明:

library(scales) # for alpha function
ggplot(dat, aes(x=group, y=value, color=group, group=group)) +
geom_point(position=position_jitter(width=0.3), alpha=0.2) +
geom_boxplot(size=1.4,alpha=0, outlier.size=0)+
geom_boxplot(size=1.4,alpha=0, outlier.size=0, color=alpha("black",0.3))

编辑:我刚刚发现将fill=0改为fill=NA也可以解决问题...


感谢您指出这一点。?col2rgb 表明自 R 3.0.0 起,fill=0 不再有效。我已经在我的答案中更新了一个注释。 - MattBagg

1
这在 ggplot2 3.3.0 中已经实现(于2020年3月发布): 新的 stage 函数允许你在将数据映射到统计或比例尺后控制美学属性:
ggplot(dat, aes(x=group, y=value, color=group, group=group)) +
  geom_point(position=position_jitter(width=0.3), alpha=0.1) +
  scale_color_manual(values=cols) +
  geom_boxplot(aes(color=stage(start=group, after_scale = colorspace::darken(color, 0.1))), fill=NA, outlier.size=0)


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