Comment attribuer des couleurs aux variables catégorielles dans ggplot2 qui ont un mappage stable?


178

Je me suis familiarisé avec R le mois dernier.

Voici ma question:

Quel est le bon moyen d'attribuer des couleurs aux variables catégorielles dans ggplot2 qui ont un mappage stable? J'ai besoin de couleurs cohérentes sur un ensemble de graphiques qui ont différents sous-ensembles et un nombre différent de variables catégorielles.

Par exemple,

plot1 <- ggplot(data, aes(xData, yData,color=categoricaldData)) + geom_line()

categoricalDataa 5 niveaux.

Puis

plot2 <- ggplot(data.subset, aes(xData.subset, yData.subset, 
                                 color=categoricaldData.subset)) + geom_line()

categoricalData.subseta 3 niveaux.

Cependant, un niveau particulier qui se trouve dans les deux ensembles se terminera par une couleur différente, ce qui rend plus difficile la lecture des graphiques ensemble.

Dois-je créer un vecteur de couleurs dans le bloc de données? Ou existe-t-il un autre moyen d'attribuer des couleurs spécifiques aux catégories?

Réponses:


187

Pour des situations simples comme l'exemple exact dans l'OP, je suis d'accord que la réponse de Thierry est la meilleure. Cependant, je pense qu'il est utile de souligner une autre approche qui devient plus facile lorsque vous essayez de maintenir des schémas de couleurs cohérents sur plusieurs trames de données qui ne sont pas toutes obtenues en sous-ensemble une seule grande trame de données. La gestion des niveaux de facteurs dans plusieurs blocs de données peut devenir fastidieuse s'ils sont extraits de fichiers séparés et que tous les niveaux de facteurs n'apparaissent pas dans chaque fichier.

Une façon de résoudre ce problème consiste à créer une échelle de couleurs manuelle personnalisée comme suit:

#Some test data
dat <- data.frame(x=runif(10),y=runif(10),
        grp = rep(LETTERS[1:5],each = 2),stringsAsFactors = TRUE)

#Create a custom color scale
library(RColorBrewer)
myColors <- brewer.pal(5,"Set1")
names(myColors) <- levels(dat$grp)
colScale <- scale_colour_manual(name = "grp",values = myColors)

puis ajoutez l'échelle de couleurs sur le tracé si nécessaire:

#One plot with all the data
p <- ggplot(dat,aes(x,y,colour = grp)) + geom_point()
p1 <- p + colScale

#A second plot with only four of the levels
p2 <- p %+% droplevels(subset(dat[4:10,])) + colScale

Le premier tracé ressemble à ceci:

entrez la description de l'image ici

et le deuxième tracé ressemble à ceci:

entrez la description de l'image ici

De cette façon, vous n'avez pas besoin de vous souvenir ou de vérifier chaque bloc de données pour voir qu'ils ont les niveaux appropriés.


1
Cela fonctionnera, mais est probablement trop compliqué. Je ne pense pas que vous ayez besoin de créer une échelle manuelle pour cela. Tout ce dont vous avez besoin est un factorélément commun à toutes les parcelles.
Andrie

14
@Andrie - Pour un seul sous-ensemble, ouais. Mais si vous jonglez avec de nombreux ensembles de données qui n'ont pas tous été créés en sous-ensemble une trame de données originale, je trouve cette stratégie beaucoup plus simple.
joran

2
@joran Merci Joran. Cela a fonctionné pour moi! Cela crée une légende avec le bon nombre de facteurs. J'aime l'approche et obtenir des mappages de couleurs sur différents ensembles de données vaut bien les trois lignes.
hiver

3
J'avais besoin de: bibliothèque ("RColorBrewer")
PatrickT

4
a parfaitement fonctionné! J'ai ajouté fillScale <- scale_fill_manual(name = "grp",values = myColors)pour l'utiliser avec des graphiques à barres.
pentandrous le

42

Je suis dans la même situation évoquée par malcook dans son commentaire : malheureusement la réponse de Thierry ne fonctionne pas avec ggplot2 version 0.9.3.1.

png("figure_%d.png")
set.seed(2014)
library(ggplot2)
dataset <- data.frame(category = rep(LETTERS[1:5], 100),
    x = rnorm(500, mean = rep(1:5, 100)),
    y = rnorm(500, mean = rep(1:5, 100)))
dataset$fCategory <- factor(dataset$category)
subdata <- subset(dataset, category %in% c("A", "D", "E"))

ggplot(dataset, aes(x = x, y = y, colour = fCategory)) + geom_point()
ggplot(subdata, aes(x = x, y = y, colour = fCategory)) + geom_point()

Voici le premier chiffre:

ggplot AE, couleurs mélangées

et le deuxième chiffre:

ggplot ADE, couleurs mélangées

Comme on peut le voir, les couleurs ne restent pas fixes, par exemple E passe du magenta au bleu.

Comme suggéré par malcook dans son commentaire et par hadley dans son commentaire le code qui utilise limitsfonctionne correctement:

ggplot(subdata, aes(x = x, y = y, colour = fCategory)) +       
    geom_point() + 
    scale_colour_discrete(drop=TRUE,
        limits = levels(dataset$fCategory))

donne la figure suivante, qui est correcte:

ggplot correct

C'est la sortie de sessionInfo():

R version 3.0.2 (2013-09-25)
Platform: x86_64-pc-linux-gnu (64-bit)

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] methods   stats     graphics  grDevices utils     datasets  base     

other attached packages:
[1] ggplot2_0.9.3.1

loaded via a namespace (and not attached):
 [1] colorspace_1.2-4   dichromat_2.0-0    digest_0.6.4       grid_3.0.2        
 [5] gtable_0.1.2       labeling_0.2       MASS_7.3-29        munsell_0.4.2     
 [9] plyr_1.8           proto_0.3-10       RColorBrewer_1.0-5 reshape2_1.2.2    
[13] scales_0.2.3       stringr_0.6.2 

3
Vous devriez publier ceci comme une nouvelle question, en faisant référence à cette question et en montrant pourquoi les solutions ici n'ont pas fonctionné.
Brian Diggs

Une question similaire a été posée ici , mais j'aimerais souligner que la réponse acceptée fonctionne très bien.
tonytonov

1
Donc je sais que c'est vieux mais je me demande s'il y a un moyen de le faire sans avoir les couleurs supplémentaires dans la légende.
goryh le

20

La solution la plus simple consiste à convertir votre variable catégorielle en un facteur avant le sous-ensemble. En fin de compte, vous avez besoin d'une variable de facteur avec exactement les mêmes niveaux dans tous vos sous-ensembles.

library(ggplot2)
dataset <- data.frame(category = rep(LETTERS[1:5], 100), 
    x = rnorm(500, mean = rep(1:5, 100)), y = rnorm(500, mean = rep(1:5, 100)))
dataset$fCategory <- factor(dataset$category)
subdata <- subset(dataset, category %in% c("A", "D", "E"))

Avec une variable de caractère

ggplot(dataset, aes(x = x, y = y, colour = category)) + geom_point()
ggplot(subdata, aes(x = x, y = y, colour = category)) + geom_point()

Avec une variable factorielle

ggplot(dataset, aes(x = x, y = y, colour = fCategory)) + geom_point()
ggplot(subdata, aes(x = x, y = y, colour = fCategory)) + geom_point()

11
Le moyen le plus simple est d'utiliser des limites
hadley

1
Pourrait donner un exemple dans ce contexte Hadley? Je ne sais pas comment utiliser les limites avec un facteur.
Thierry

@Thierry Merci. J'étais heureux de recevoir des réponses sur mon premier message. Et merci Thierry ou l'ajout de code reproductible comme j'aurais dû dans mon post ... Mes variables catégorielles étaient du bon type - facteurs. L'autre problème est que je veux que la légende ne montre pas les facteurs inutilisés. R ignore les variables de caractère inutilisées lors de la création de la légende. Cependant, des facteurs inutilisés persistent. Si je les supprime en utilisant: subdata $ category <- factor (subdata $ category) [drop = TRUE] alors la légende a le bon nombre de facteurs MAIS perd le mappage.
hiver

11
@Thierry - entre mes mains, en utilisant ggplot2_0.9.3.1, cette méthode ne fonctionne (plus?); les couleurs affectées à la fCategory sont différentes entre les deux tracés. Cependant, heureusement, @wintour, j'ai pensé que @hadley suggérait que + scale_colour_discrete(drop=TRUE,limits = levels(dataset$fCategory))pour préserver l'association couleur | facteur mais, ce qui fonctionne, sauf que, entre mes mains, le drop = TRUE n'est PAS respecté (je m'attends à ce qu'il supprime le niveau de la légende). Drat ... ou est-ce moi?
malcook

1
@malcook, au lieu de drop = TRUE, vous devez spécifier les niveaux que vous souhaitez conserver via "breaks": github.com/hadley/ggplot2/issues/1433
Eric

17

Ceci est un ancien post, mais je cherchais une réponse à cette même question,

Pourquoi ne pas essayer quelque chose comme:

scale_color_manual(values = c("foo" = "#999999", "bar" = "#E69F00"))

Si vous avez des valeurs catégoriques, je ne vois pas pourquoi cela ne devrait pas fonctionner.


3
C'est en fait ce que fait la réponse de Joran, mais en utilisant myColors <- brewer.pal(5,"Set1"); names(myColors) <- levels(dat$grp)pour éviter d'avoir à coder manuellement les niveaux.
Axeman

Cependant, la réponse de Joran ne code pas en dur les valeurs des couleurs. Il y a des cas où vous avez besoin d'une valeur de couleur spécifique pour un facteur donné.
René Nyffenegger

Bien que j'aie l'inconvénient du "codage en dur" dans certains cas, je pense que trop souvent les couches d'abstraction que les développeurs / codeurs ajoutent rendent leur travail moins accessible, pas plus. L'intention est claire à 100% dans ce cas. De plus, il est assez facile de penser à la façon de créer une fonction utilitaire qui se développe sur cet exemple et qui renvoie un vecteur nommé de couleurs spécifiques.
Matt Barstead le

16

Sur la base de la réponse très utile de joran, j'ai pu trouver cette solution pour une échelle de couleurs stable pour un facteur booléen ( TRUE, FALSE).

boolColors <- as.character(c("TRUE"="#5aae61", "FALSE"="#7b3294"))
boolScale <- scale_colour_manual(name="myboolean", values=boolColors)

ggplot(myDataFrame, aes(date, duration)) + 
  geom_point(aes(colour = myboolean)) +
  boolScale

Puisque ColorBrewer n'est pas très utile avec les échelles de couleurs binaires, les deux couleurs nécessaires sont définies manuellement.

Voici mybooleanle nom de la colonne myDataFramecontenant le facteur TRUE / FALSE. dateet durationsont les noms de colonne à mapper sur les axes x et y du tracé dans cet exemple.


Une autre approche consiste à appliquer "as.character ()" à la colonne. Cela en fera une colonne de cordes qui fonctionne bien avec l'échelle _ * _ manuelle
Sahir Moosvi
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.