Ggplot2: Многочисленные цветовые шкалы или цвета сдвига систематически на разных слоях?

Когда я создаю диаграммы, мне нравится также показывать необработанные данные в фоновом режиме, например:

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)

enter image description here

Однако мне не нравится, как мои полевые сюжеты полностью исчезают, когда очки становятся слишком плотными. Я знаю, что могу настроить alpha, что в некоторых случаях хорошо, но не тогда, когда мои группы имеют разную плотность (например, когда самая легкая группа полностью исчезнет, ​​если я уменьшу alpha настолько, чтобы самая темная группа не " t затмить окно коробки). То, что я пытаюсь сделать, - это систематически смещать цвета для графиков окна - немного темнее, возможно - так, чтобы они отображались даже тогда, когда фоновые точки превышают альфу. Например:

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)

enter image description here

До сих пор я не нашел способ подделать в другом scale_color для слоя geom_boxplot (который казался бы самым простым путем, если есть способ сделать это). Я также не смог найти простой синтаксис для систематической настройки цветов так же, как вы можете легко компенсировать непрерывную эстетику, например 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)

enter image description here

но тогда мне приходится иметь дело с этой уродливой легендой. Любые лучшие идеи?

Ответ 1

Теперь вы можете определить свою собственную версию GeomBoxplot (называя ее, скажем, GeomPlotDark), отличающуюся от оригинала только тем, что она сначала "затемняет" цвета перед их графикой.

С помощью proto вы можете сделать это, создав прото-объект GeomBoxplotDark, который наследует от GeomBoxplot и отличается только функцией 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_dark() на вызов geom_boxplot():

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)

Я думаю, что получившийся сюжет выглядит довольно изящным. С небольшим количеством настроек и просмотра напрямую (не как загруженный файл) это будет выглядеть потрясающе:

enter image description here

Ответ 2

Поздний ответ добавлен Ноябрь 2012:

Поскольку некоторые из этих потрясающих ответов требуют более старых версий 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))

edit: TobiO указывает, что fill=0 перестает работать. Вместо этого можно заменить fill=NA или alpha=0. Кажется, это связано с изменением col2rgb(), начиная с R 3.0.0.

jittered points under darker boxplot

Ответ 3

Вы можете взломать легендарный гроб, но его сложно разместить.

 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

Ответ 4

Синтаксис 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))

edit: Я только что узнал, что изменение fill=0 на fill=NA также делает трюк...