34 votes

ggplot2 : Échelles de couleurs multiples ou décalage systématique des couleurs sur différentes couches ?

Quand je fais des diagrammes en boîte, j'aime aussi montrer les données brutes en arrière-plan, comme ceci :

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

Cependant, je n'aime pas la façon dont mes diagrammes en boîte disparaissent complètement lorsque les points deviennent trop denses. Je sais que je peux ajuster alpha ce qui est correct dans certains cas, mais pas lorsque mes groupes ont des densités variables (par exemple, lorsque le groupe le plus léger disparaîtrait complètement si je diminuais la valeur de alpha suffisamment pour que le groupe le plus sombre ne masque pas le box plot). Ce que j'essaie de faire, c'est de décaler systématiquement les couleurs des box plots - un peu plus foncées, peut-être - afin qu'elles apparaissent même lorsque les points d'arrière-plan maximisent l'alpha. Par exemple :

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

Jusqu'à présent, je n'ai pas trouvé le moyen de simuler une autre scale_color pour le geom_boxplot (ce qui semble être la solution la plus simple, s'il y a un moyen de le faire). Je n'ai pas non plus été capable de trouver une syntaxe simple pour ajuster systématiquement les couleurs de la même manière que vous pouvez facilement compenser une esthétique continue comme aes(x=x+1) .

La chose la plus proche que j'ai été capable d'obtenir est de dupliquer complètement les niveaux du facteur...

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

mais ensuite je dois faire face à cette horrible légende. Une meilleure idée ?

2 votes

Pourquoi ne pas rendre les boîtes noires ?

0 votes

J'ai d'abord pensé à ce que @kohske a dit, mais je pense que cela va à l'encontre de la valeur alpha qui infecte la légende et rend les couleurs illisibles (au moins jusqu'à la sortie de la 0.9.0 dans quelques semaines, je pense).

0 votes

Oui, le noir/gris est tout à fait acceptable (cf. i.imgur.com/7KKg2.png ), mais je n'aime pas la façon dont il peut dominer/distraire du codage des facteurs que j'essaie généralement de mettre en évidence. Je suppose que je pense que ce serait juste encore plus beau si je pouvais garder le même schéma de couleurs mais en le décalant un peu.

21voto

MattBagg Points 3278

Réponse tardive ajoutée en novembre 2012 :

Puisque certaines de ces excellentes réponses nécessitent des versions plus anciennes de ggplot2 et que les gens se réfèrent toujours à cette page, je vais la mettre à jour avec la solution ridiculement simple que j'ai utilisée avec ggplot2 0.9.0+.

Nous ajoutons simplement une deuxième couche geom_boxplot qui est identique à la première, sauf que nous assignons une couleur constante en utilisant scales::alpha() pour que le premier boxplot apparaisse.

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))

éditer : TobiO fait remarquer que fill=0 a cessé de fonctionner. Au lieu de cela, fill=NA o alpha=0 peuvent être substitués. Cela semble être dû à un changement dans col2rgb() à partir de R 3.0.0.

jittered points under darker boxplot

14voto

Josh O'Brien Points 68397

Pour l'instant, vous pourriez définir votre propre version de GeomBoxplot (en l'appelant, disons, GeomPlotDark ), qui ne diffère de l'original que par le fait qu'il "assombrit" d'abord les couleurs avant de les tracer.

Avec proto, vous pouvez le faire en créant un objet proto, GeomBoxplotDark qui hérite de GeomBoxplot et ne diffère que par son draw fonction. La plupart des draw La définition de la fonction est tirée du GeomBoxplot sources ; j'ai annoté les lignes que j'ai modifiées avec des commentaires comme celui-ci # ** ... ** :

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), ...)
    )))
  }
)

Créez ensuite un geom_boxplot_dark() à appeler par l'utilisateur, et qui enveloppe de manière appropriée l'appel à 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, ...)

Enfin, essayez-le avec un code presque identique à votre appel original, en substituant simplement un appel à geom_boxplot_dark() pour l'appel à 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)

Je pense que le tracé résultant est plutôt chouette. Avec un peu d'ajustement, et visualisé directement (pas comme un fichier téléchargé), il aura l'air génial :

enter image description here

4 votes

Je pense que vous pourriez simplifier le code en héritant de GeomBoxplot au lieu de Geom ; en particulier, vous pourriez éviter d'écrire le double de .$examples etc. Vous avez juste besoin du .$draw méthode

1 votes

@baptiste -- Merci beaucoup pour votre suggestion ! J'ai modifié ma question pour l'intégrer. Je suis en train d'apprendre à connaître proto et vous avez presque doublé ma compréhension de ce dont ils sont capables, alors merci pour cela aussi ;)

0 votes

Je pense que je vais accepter celui-ci. Même s'il y a plus de code, je peux simplement le mettre dans un fichier de fonctions communes que je source, et ensuite être capable de l'utiliser de manière flexible dans différents graphiques, avec plus ou moins de niveaux de facteurs, etc. La solution parfaite serait une nouvelle échelle comme celle mentionnée par Baptiste, mais ceci est la meilleure solution suivante. C'est formidable. Merci à tous !

8voto

baptiste Points 19677

Vous pouvez pirater la légende grob, mais il semble difficile de la placer.

 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

0 votes

Courte et douce - excellente ! C'est aussi une excellente contribution car elle aborde le problème du côté opposé à celui de Josh.

3 votes

Ne pouvez-vous pas simplement spécifier les pauses ?

0 votes

@hadley Ah je ne savais pas non plus que ça pouvait marcher. J'ai pensé que cela clipperait la balance d'une manière ou d'une autre. Joli !

3voto

TobiO Points 25

La syntaxe de ggplot2 semble avoir changé, et comme il m'a fallu un peu de temps pour la comprendre :

le site fill=0 n'a (pour moi) aucun effet (plus ?)

mais il doit être modifié en alpha=0 afin de rendre la boîte transparente :

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))

Je viens de découvrir que le changement fill=0 a fill=NA fait aussi l'affaire...

0 votes

Merci de nous le signaler. ?col2rgb indique qu'il s'agit d'un changement à partir de R 3.0.0 où fill=0 n'est plus valide. J'ai mis à jour ma réponse avec une note.

1voto

YAK Points 185

Cela a été mis en œuvre dans ggplot2 3.3.0 (sortie en 2020-03) : Le nouveau stage vous permet de contrôler l'esthétique après le mappage des données par une statistique ou une échelle :

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)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X