Jak przypisać kolory do zmiennych kategorialnych w ggplot2, które mają stabilne mapowanie?

178

Zacząłem nabierać prędkości z R w ostatnim miesiącu.

Oto moje pytanie:

Jaki jest dobry sposób przypisywania kolorów do zmiennych kategorialnych w ggplot2, które mają stabilne mapowanie? Potrzebuję spójnych kolorów w zestawie wykresów, które mają różne podzbiory i różną liczbę zmiennych kategorialnych.

Na przykład,

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

gdzie categoricalDatama 5 poziomów.

I wtedy

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

gdzie categoricalData.subsetma 3 poziomy.

Jednak określony poziom w obu zestawach będzie miał inny kolor, co utrudnia wspólne odczytywanie wykresów.

Czy muszę utworzyć wektor kolorów w ramce danych? A może jest inny sposób przypisania określonych kolorów do kategorii?

zimowy
źródło

Odpowiedzi:

187

W prostych sytuacjach, takich jak dokładny przykład w PO, zgadzam się, że odpowiedź Thierry'ego jest najlepsza. Uważam jednak, że warto wskazać inne podejście, które staje się łatwiejsze, gdy próbujesz zachować spójne schematy kolorów w wielu ramkach danych, z których nie wszystkie są uzyskiwane przez podzbiór jednej dużej ramki danych. Zarządzanie poziomami czynników w wielu ramkach danych może stać się uciążliwe, jeśli są one pobierane z oddzielnych plików, a nie wszystkie poziomy czynników pojawiają się w każdym pliku.

Jednym ze sposobów rozwiązania tego problemu jest utworzenie niestandardowej ręcznej skali kolorów w następujący sposób:

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

a następnie w razie potrzeby dodaj skalę kolorów do wykresu:

#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

Pierwsza fabuła wygląda następująco:

wprowadź opis obrazu tutaj

a druga fabuła wygląda tak:

wprowadź opis obrazu tutaj

W ten sposób nie musisz pamiętać ani sprawdzać każdej ramki danych, aby zobaczyć, czy ma ona odpowiedni poziom.

joran
źródło
1
To zadziała, ale prawdopodobnie jest zbyt skomplikowane. Myślę, że nie musisz tworzyć do tego ręcznej skali. Wszystko, czego potrzebujesz, to to, factorco jest wspólne dla wszystkich działek.
Andrie
14
@Andrie - tak, dla pojedynczego podzbioru. Ale jeśli żonglujesz wieloma zestawami danych, z których nie wszystkie zostały utworzone przez podzbiór jednej oryginalnej ramki danych, uważam tę strategię za znacznie prostszą.
joran
2
@joran Thanks Joran. To zadziałało dla mnie! Tworzy legendę z odpowiednią liczbą czynników. Podoba mi się to podejście, a uzyskanie mapowania kolorów w różnych zestawach danych jest warte trzech linii.
wintour
3
Potrzebowałem: biblioteki („RColorBrewer”)
PatrickT
4
działało idealnie! Dodałem, fillScale <- scale_fill_manual(name = "grp",values = myColors)aby użyć tego z wykresami słupkowymi.
Pentandrous
42

Jestem w takiej samej sytuacji wskazał malcook w swoim komentarzu : niestety odpowiedź przez Thierry nie działa z wersją 0.9.3.1 ggplot2.

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

Oto pierwsza cyfra:

ggplot AE, kolory mieszane

a druga cyfra:

ggplot ADE, kolory mieszane

Jak widać, kolory nie pozostają niezmienne, na przykład E przełącza się z magenty na niebieski.

Jak zasugerował malcook w swoim komentarzu i hadley w swoim komentarzu, kod, który używa, limitsdziała poprawnie:

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

podaje następujący rysunek, który jest poprawny:

poprawny ggplot

Oto wynik 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
źródło
3
Powinieneś opublikować to jako nowe pytanie, odwołując się do tego pytania i pokazując, dlaczego przedstawione tutaj rozwiązania nie zadziałały.
Brian Diggs,
Zadano tutaj podobne pytanie , ale chciałbym zaznaczyć, że zaakceptowana odpowiedź działa dobrze.
tonytonov
1
Wiem, że to jest stare, ale zastanawiam się, czy można to zrobić bez dodatkowych kolorów w legendzie.
goryh
20

Najłatwiejszym rozwiązaniem jest przekonwertowanie zmiennej kategorialnej na czynnik przed podzestawieniem. Podsumowując, potrzebujesz zmiennej czynnikowej z dokładnie tymi samymi poziomami we wszystkich podzbiorach.

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

Ze zmienną znakową

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

Ze zmienną czynnikową

ggplot(dataset, aes(x = x, y = y, colour = fCategory)) + geom_point()
ggplot(subdata, aes(x = x, y = y, colour = fCategory)) + geom_point()
Thierry
źródło
11
Najłatwiej jest użyć limitów
Hadley
1
Czy mógłby podać przykład w tym kontekście Hadley? Nie jestem pewien, jak używać limitów z czynnikiem.
Thierry,
@Thierry Thanks. Byłem szczęśliwy, że otrzymałem odpowiedzi na mój pierwszy post. I dziękuję Thierry lub dodając odtwarzalny kod, tak jak powinienem był w moim poście ... Moje zmienne kategorialne były właściwego typu - czynniki. Inną kwestią jest to, że chcę, aby legenda nie pokazywała niewykorzystanych czynników. R ignoruje nieużywane zmienne znakowe podczas tworzenia legendy. Jednak nadal istnieją niewykorzystane czynniki. Jeśli upuszczę je za pomocą: subdata $ category <- factor (subdata $ category) [drop = TRUE] to legenda ma odpowiednią liczbę czynników, ALE utraci mapowanie.
wintour
11
@Thierry - w moich rękach, używając ggplot2_0.9.3.1, ta metoda (już?) Nie działa; kolory przypisane do kategorii fCategory są różne na obu wykresach. Jednak szczęśliwie, @wintour, pomyślałem, że @hadley sugeruje, że + scale_colour_discrete(drop=TRUE,limits = levels(dataset$fCategory))aby zachować kolor | stowarzyszenie czynnik, ale który działa, z wyjątkiem, w moich rękach, ten spadek = PRAWDA jest NIE przestrzegane (I oczekiwać, że aby usunąć z poziomu legenda). Drat ... czy to ja?
malcook
1
@malcook, zamiast drop = TRUE, musisz określić, które poziomy chcesz zachować za pomocą „przerw”: github.com/hadley/ggplot2/issues/1433
Eric
17

To jest stary post, ale szukałem odpowiedzi na to samo pytanie,

Dlaczego nie spróbować czegoś takiego:

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

Jeśli masz wartości kategoryczne, nie widzę powodu, dla którego to nie powinno działać.

Pavlos Panteliadis
źródło
3
To jest właściwie to, co robi odpowiedź Jorana, ale używa, myColors <- brewer.pal(5,"Set1"); names(myColors) <- levels(dat$grp)aby uniknąć konieczności ręcznego kodowania poziomów.
Axeman
Jednak odpowiedź Jorana nie zakoduje na stałe wartości kolorów. Są przypadki, w których potrzebujesz określonej wartości koloru dla danego czynnika.
René Nyffenegger
Chociaż w niektórych przypadkach dostaję wadę „twardego kodowania”, myślę, że zbyt często warstwy abstrakcji dodawane przez programistów / programistów sprawiają, że ich praca jest mniej dostępna, a nie większa. W tym przypadku zamiar jest w 100% jasny. Ponadto łatwo jest pomyśleć o tym, jak utworzyć funkcję narzędzia, która rozszerza ten przykład, zwracając nazwany wektor o określonych kolorach.
Matt Barstead
16

Na podstawie bardzo pomocnej odpowiedzi jorana udało mi się wymyślić to rozwiązanie dla stabilnej skali kolorów dla współczynnika boolowskiego ( 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

Ponieważ ColorBrewer nie jest zbyt pomocny w przypadku binarnych skal kolorów, dwa potrzebne kolory są definiowane ręcznie.

Oto mybooleannazwa kolumny myDataFramezawierającej współczynnik PRAWDA / FAŁSZ. datei durationsą nazwami kolumn, które mają być odwzorowane na osie X i Y wykresu w tym przykładzie.

Marian
źródło
Innym podejściem jest zastosowanie „as.character ()” do kolumny. To sprawi, że będzie to kolumna typu string, która dobrze współpracuje ze skalą _ * _ manual
Sahir Moosvi