Como atribuir cores a variáveis ​​categóricas no ggplot2 que possuem mapeamento estável?

177

Eu tenho me acostumado com R no último mês.

Aqui está a minha pergunta:

Qual é uma boa maneira de atribuir cores a variáveis ​​categóricas no ggplot2 que possuem mapeamento estável? Preciso de cores consistentes em um conjunto de gráficos que tenham subconjuntos diferentes e número diferente de variáveis ​​categóricas.

Por exemplo,

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

onde categoricalDatatem 5 níveis.

E depois

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

onde categoricalData.subsettem 3 níveis.

No entanto, um nível específico que está em ambos os conjuntos terá uma cor diferente, o que dificulta a leitura dos gráficos juntos.

Preciso criar um vetor de cores no quadro de dados? Ou existe outra maneira de atribuir cores específicas a categorias?

wintour
fonte

Respostas:

186

Para situações simples como o exemplo exato no OP, concordo que a resposta de Thierry é a melhor. No entanto, acho útil ressaltar outra abordagem que se torna mais fácil quando você tenta manter esquemas de cores consistentes em vários quadros de dados que nem todos são obtidos com o subconjunto de um único quadro de dados grande. O gerenciamento dos níveis de fatores em vários quadros de dados pode se tornar tedioso se eles forem extraídos de arquivos separados e nem todos os níveis de fator aparecerem em cada arquivo.

Uma maneira de resolver isso é criar uma escala de cores manual personalizada da seguinte maneira:

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

e adicione a escala de cores à plotagem, conforme necessário:

#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

O primeiro gráfico é assim:

insira a descrição da imagem aqui

e o segundo gráfico é assim:

insira a descrição da imagem aqui

Dessa forma, você não precisa se lembrar ou verificar cada quadro de dados para ver se eles têm os níveis apropriados.

joran
fonte
1
Isso vai funcionar, mas provavelmente é muito complicado. Eu não acho que você precise criar uma escala manual para isso. Tudo o que você precisa factoré comum entre todas as parcelas.
Andrie
14
@ Andrie - Para um único subconjunto, sim. Mas se você estiver manipulando muitos conjuntos de dados que não foram todos criados com o subconjunto de um quadro de dados original, acho essa estratégia muito mais simples.
joran
2
@joran Obrigado Joran. Isso funcionou para mim! Cria uma legenda com o número certo de fatores. Gosto da abordagem e obter mapeamentos de cores em diferentes conjuntos de dados vale bem as três linhas.
Wintour 5/08
3
Eu precisava: library ("RColorBrewer")
PatrickT 25/04
4
funcionou perfeitamente! Eu adicionei fillScale <- scale_fill_manual(name = "grp",values = myColors)para usar isso com gráficos de barras.
pentandrous
42

Estou na mesma situação apontada por malcook em seu comentário : infelizmente a resposta de Thierry não funciona com o ggplot2 versão 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()

Aqui está a primeira figura:

ggplot AE, cores misturadas

e a segunda figura:

ggplot ADE, cores misturadas

Como podemos ver, as cores não permanecem fixas, por exemplo, E muda de magenta para azul.

Conforme sugerido por malcook em seu comentário e por hadley em seu comentário, o código que usa limitsfunciona corretamente:

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

fornece a seguinte figura, que está correta:

ggplot correto

Esta é a saída 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
fonte
3
Você deve postar isso como uma nova pergunta, referenciando-a e mostrando por que as soluções aqui não funcionaram.
Brian Diggs
Uma pergunta semelhante foi feita aqui , mas eu gostaria de salientar que a resposta aceita funciona bem.
tonytonov
1
Então eu sei que isso é antigo, mas me pergunto se existe uma maneira de fazer isso sem ter as cores extras na legenda.
goryh 17/01
20

A solução mais fácil é converter sua variável categórica em um fator anterior ao subconjunto. Bottomline é que você precisa de uma variável fator com exatamente os mesmos níveis em todos os seus 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"))

Com uma variável de caractere

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

Com uma variável fator

ggplot(dataset, aes(x = x, y = y, colour = fCategory)) + geom_point()
ggplot(subdata, aes(x = x, y = y, colour = fCategory)) + geom_point()
Thierry
fonte
11
A maneira mais fácil é a limites de uso
Hadley
1
Poderia fornecer um exemplo neste contexto Hadley? Não sei como usar limites com um fator.
Thierry
@ Thierry Obrigado. Fiquei feliz em receber respostas no meu primeiro post. E obrigado Thierry ou adicionando código reproduzível como eu deveria ter no meu post ... Minhas variáveis ​​categóricas eram o tipo certo de fatores. A outra questão é que eu quero que a legenda não mostre fatores não utilizados. R ignora variáveis ​​de caracteres não utilizadas ao criar a legenda. No entanto, fatores não utilizados persistem. Se eu descartá-los usando: subdata $ categoria <- fator (subdata $ categoria) [drop = TRUE], a legenda tem o número certo de fatores, MAS perde o mapeamento.
Wintour 5/08
11
@Thierry - nas minhas mãos, usando ggplot2_0.9.3.1, esse método não funciona (mais?); as cores atribuídas à categoria f são diferentes entre os dois gráficos. No entanto, felizmente, @wintour, imaginei que @hadley sugerisse que + scale_colour_discrete(drop=TRUE,limits = levels(dataset$fCategory))preservasse a associação do fator de cor |, mas que funciona, exceto, nas minhas mãos, que a queda = TRUE NÃO está sendo respeitada (espero que remova o nível de a lenda). Drat ... ou sou eu?
malcook
1
@malcook, em vez de queda = TRUE, você precisa especificar quais níveis você quer manter por "quebra": github.com/hadley/ggplot2/issues/1433
Eric
17

Este é um post antigo, mas eu estava procurando resposta para essa mesma pergunta,

Por que não tentar algo como:

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

Se você tiver valores categóricos, não vejo uma razão para que isso não funcione.

Pavlos Panteliadis
fonte
3
Na verdade, é isso que a resposta de Joran faz, mas usando myColors <- brewer.pal(5,"Set1"); names(myColors) <- levels(dat$grp)para evitar a codificação manual dos níveis.
Axeman
No entanto, a resposta de Joran não codifica os valores das cores. Há casos em que você precisa de um valor de cor específico para um determinado fator.
René Nyffenegger 27/05/19
Embora eu tenha a desvantagem da "codificação embutida" em certos casos, acho que com muita freqüência as camadas de desenvolvedores / codificadores de abstração adicionam tornam seu trabalho menos acessível, e não mais. A intenção é 100% clara neste caso. Além disso, é fácil pensar em como criar uma função de utilitário que se expanda neste exemplo que retorna um vetor nomeado de cores específicas.
Matt Barstead em 25/01
16

Com base na resposta muito útil de joran, consegui encontrar esta solução para uma escala de cores estável para um fator 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

Como o ColorBrewer não é muito útil em escalas de cores binárias, as duas cores necessárias são definidas manualmente.

Aqui mybooleanestá o nome da coluna que myDataFramecontém o fator TRUE / FALSE. datee durationsão os nomes das colunas a serem mapeadas para os eixos xey da plotagem neste exemplo.

Marian
fonte
Outra abordagem é aplicar "as.character ()" à coluna. Isso tornará uma coluna de string que funciona bem com a escala _ * _ manual
Sahir Moosvi