¿Cómo asignar colores a variables categóricas en ggplot2 que tienen mapeo estable?

178

Me he estado poniendo al día con R en el último mes.

Aquí está mi pregunta:

¿Cuál es una buena manera de asignar colores a variables categóricas en ggplot2 que tienen un mapeo estable? Necesito colores consistentes en un conjunto de gráficos que tienen diferentes subconjuntos y diferentes números de variables categóricas.

Por ejemplo,

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

donde categoricalDatatiene 5 niveles.

Y entonces

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

donde categoricalData.subsettiene 3 niveles.

Sin embargo, un nivel particular que está en ambos conjuntos terminará con un color diferente, lo que hace que sea más difícil leer los gráficos juntos.

¿Necesito crear un vector de colores en el marco de datos? ¿O hay otra forma de asignar colores específicos a las categorías?

wintour
fuente

Respuestas:

187

Para situaciones simples como el ejemplo exacto en el OP, estoy de acuerdo en que la respuesta de Thierry es la mejor. Sin embargo, creo que es útil señalar otro enfoque que se vuelve más fácil cuando intentas mantener esquemas de color consistentes en múltiples marcos de datos que no son obtienen todos al subconjunto de un solo marco de datos grande. La gestión de los niveles de factores en múltiples marcos de datos puede volverse tediosa si se extraen de archivos separados y no todos los niveles de factores aparecen en cada archivo.

Una forma de abordar esto es crear una escala de color manual personalizada de la siguiente manera:

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

y luego agregue la escala de color en la trama según sea necesario:

#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

El primer argumento se ve así:

ingrese la descripción de la imagen aquí

y la segunda trama se ve así:

ingrese la descripción de la imagen aquí

De esta manera, no necesita recordar ni verificar cada marco de datos para ver que tienen los niveles apropiados.

joran
fuente
1
Esto funcionará, pero probablemente sea demasiado complicado. No creo que necesite crear una escala manual para esto. Todo lo que necesitas es un factorcomún entre todas las parcelas.
Andrie
14
@Andrie: para un solo subconjunto, sí. Pero si está haciendo malabarismos con muchos conjuntos de datos que no se crearon todos al subconjugar un marco de datos original, esta estrategia me parece mucho más simple.
joran
2
@joran Gracias Joran. ¡Esto funcionó para mí! Crea una leyenda con el número correcto de factores. Me gusta el enfoque y obtener mapeos de color en diferentes conjuntos de datos bien vale las tres líneas.
wintour
3
Necesitaba: biblioteca ("RColorBrewer")
PatrickT
44
funcionó a la perfección! Agregué fillScale <- scale_fill_manual(name = "grp",values = myColors)para usar esto con gráficos de barras.
pentandroso
42

Estoy en la misma situación señalada por malcook en su comentario : desafortunadamente, la respuesta de Thierry no funciona con ggplot2 versión 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()

Aquí está la primera figura:

ggplot AE, colores mezclados

y la segunda figura:

ggplot ADE, colores mezclados

Como podemos ver, los colores no permanecen fijos, por ejemplo, E cambia de magenta a blu.

Según lo sugerido por malcook en su comentario y por hadley en su comentario, el código que utiliza limitsfunciona correctamente:

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

da la siguiente figura, que es correcta:

ggplot correcto

Este es el resultado 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 
Alessandro Jacopson
fuente
3
Debe publicar esto como una nueva pregunta, haciendo referencia a esta pregunta y mostrando por qué las soluciones aquí no funcionaron.
Brian Diggs
Aquí se hizo una pregunta similar , pero me gustaría señalar que la respuesta aceptada funciona bien.
tonytonov
1
Entonces sé que esto es viejo, pero me pregunto si hay una manera de hacerlo sin tener los colores adicionales en la leyenda.
goryh
20

La solución más fácil es convertir su variable categórica en un factor antes del subconjunto. La conclusión es que necesita una variable de factor con exactamente los mismos niveles en todos sus subconjuntos.

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

Con una variable de caracteres

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

Con una variable de factor

ggplot(dataset, aes(x = x, y = y, colour = fCategory)) + geom_point()
ggplot(subdata, aes(x = x, y = y, colour = fCategory)) + geom_point()
Thierry
fuente
11
La forma más fácil es usar límites
hadley
1
¿Podría proporcionar un ejemplo en este contexto Hadley? No estoy seguro de cómo usar los límites con un factor.
Thierry
@Thierry Gracias. Me alegró recibir respuestas en mi primera publicación. Y gracias Thierry o agregar código reproducible como debería haberlo hecho en mi publicación ... Mis variables categóricas fueron el tipo correcto - factores. El otro problema es que quiero que la leyenda no muestre factores no utilizados. R ignora las variables de caracteres no utilizadas al construir la leyenda. Sin embargo, los factores no utilizados persisten. Si los elimino usando: subdata $ category <- factor (subdata $ category) [drop = TRUE], entonces la leyenda tiene el número correcto de factores PERO pierde el mapeo.
wintour
11
@Thierry: en mis manos, usando ggplot2_0.9.3.1, este método no funciona (¿ya?); Los colores asignados a la fCategory son diferentes entre las dos parcelas. Sin embargo, felizmente, @wintour, pensé que @hadley está sugiriendo que + scale_colour_discrete(drop=TRUE,limits = levels(dataset$fCategory))para preservar el color | asociación factor, pero que funciona, excepto, en mis manos, la caída = TRUE se NO ser respetados (espero que para eliminar el nivel de la leyenda). Drat ... o soy yo?
malcook
1
@malcook, en lugar de soltar = VERDADERO, debe especificar qué niveles desea mantener mediante "pausas": github.com/hadley/ggplot2/issues/1433
Eric
17

Esta es una publicación antigua, pero estaba buscando respuesta a esta misma pregunta,

¿Por qué no probar algo como:

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

Si tiene valores categóricos, no veo una razón por la cual esto no debería funcionar.

Pavlos Panteliadis
fuente
3
Esto es en realidad lo que hace la respuesta de Joran, pero usar myColors <- brewer.pal(5,"Set1"); names(myColors) <- levels(dat$grp)para evitar tener que codificar manualmente los niveles.
Axeman
Sin embargo, la respuesta de Joran no codifica los valores de los colores. Hay casos en los que necesita un valor de color específico para un factor determinado.
René Nyffenegger
Si bien tengo el inconveniente de la "codificación rígida" en ciertos casos, creo que con demasiada frecuencia las capas de abstracción que los desarrolladores / codificadores agregan hacen que su trabajo sea menos accesible, no más. La intención es 100% clara en este caso. Además, es bastante fácil pensar en cómo hacer una función de utilidad que se expanda en este ejemplo que devuelva un vector con nombre de colores específicos.
Matt Barstead
16

Basado en la muy útil respuesta de joran, pude encontrar esta solución para una escala de color estable para un factor booleano ( 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

Dado que ColorBrewer no es muy útil con escalas de color binarias, los dos colores necesarios se definen manualmente.

Aquí mybooleanestá el nombre de la columna que myDataFramecontiene el factor VERDADERO / FALSO. datey durationson los nombres de columna que se asignarán al eje xey del diagrama en este ejemplo.

Mariano
fuente
Otro enfoque es aplicar "as.character ()" a la columna. Esto lo convertirá en una columna de cadena que funciona bien con la escala _ * _ manual
Sahir Moosvi