Как назначить цвета категориальным переменным в ggplot2, которые имеют стабильное отображение?

178

Я набирал скорость с R в прошлом месяце.

Вот мой вопрос:

Как можно назначить цвета категориальным переменным в ggplot2, которые имеют стабильное отображение? Мне нужны согласованные цвета для набора графиков, которые имеют разные подмножества и разное количество категориальных переменных.

Например,

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

где categoricalDataимеет 5 уровней.

А потом

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

где categoricalData.subsetимеет 3 уровня.

Тем не менее, определенный уровень в обоих наборах будет иметь другой цвет, что затруднит совместное чтение графиков.

Нужно ли создавать вектор цветов во фрейме данных? Или есть другой способ назначить определенные цвета категориям?

Винтур
источник

Ответы:

187

Для простых ситуаций, таких как точный пример в ОП, я согласен, что ответ Тьерри - лучший. Тем не менее, я думаю, что полезно указать на другой подход, который становится легче, когда вы пытаетесь поддерживать согласованные цветовые схемы для нескольких фреймов данных, которые не все получены путем поднабора одного большого фрейма данных. Управление уровнями факторов в нескольких фреймах данных может быть утомительным, если они извлекаются из отдельных файлов, и не все уровни факторов отображаются в каждом файле.

Одним из способов решения этой проблемы является создание настраиваемой ручной цветовой шкалы следующим образом:

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

и затем добавьте цветовую шкалу на график по мере необходимости:

#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

Первый сюжет выглядит так:

введите описание изображения здесь

и второй сюжет выглядит так:

введите описание изображения здесь

Таким образом, вам не нужно запоминать или проверять каждый фрейм данных, чтобы убедиться, что они имеют соответствующие уровни.

Joran
источник
1
Это будет работать, но, вероятно, слишком сложно. Я не думаю, что вам нужно создавать ручные весы для этого. Все, что вам нужно factor, это общий для всех участков.
Андри
14
@Andrie - Для одного подмножества, да. Но если вы манипулируете множеством наборов данных, которые не все были созданы путем поднабора одного исходного фрейма данных, я нахожу эту стратегию намного проще.
Джоран
2
@joran Спасибо, Джоран. Это сработало для меня! Это создает легенду с правильным количеством факторов. Мне нравится этот подход, и для того, чтобы получить сопоставление цветов между различными наборами данных, стоит три строки.
Винтур
3
Мне нужно: библиотека ("RColorBrewer")
PatrickT
4
работал отлично! Я добавил, fillScale <- scale_fill_manual(name = "grp",values = myColors)чтобы использовать это с гистограммами.
пятидесятый
42

Я нахожусь в такой же ситуации указывал malcook в своем комментарии : к сожалению, ответ на Тьерри не работает с ggplot2 версии 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()

Вот это первая цифра:

ggplot AE, смешанные цвета

и вторая цифра:

ggplot ADE, смешанные цвета

Как мы видим, цвета не остаются неизменными, например, E переключается с пурпурного на синий.

Как предложил malcook в своем комментарии и hadley в своем комментарии, код, который использует limitsправильно, работает:

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

дает следующий рисунок, который является правильным:

правильный ggplot

Это вывод из 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
Вы должны опубликовать это как новый вопрос, ссылаясь на этот вопрос и показывая, почему решения здесь не сработали.
Брайан Диггс
Подобный вопрос был задан здесь , но я хотел бы отметить, что принятый ответ работает нормально.
Тонитонов
1
Так что я знаю, что это старо, но мне интересно, есть ли способ сделать это без дополнительных цветов в легенде.
goryh
20

Самым простым решением является преобразование вашей категориальной переменной в коэффициент до поднабора. Суть в том, что вам нужна факторная переменная с одинаковыми уровнями во всех ваших подмножествах.

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 = category)) + geom_point()
ggplot(subdata, aes(x = x, y = y, colour = category)) + geom_point()

С переменной фактора

ggplot(dataset, aes(x = x, y = y, colour = fCategory)) + geom_point()
ggplot(subdata, aes(x = x, y = y, colour = fCategory)) + geom_point()
Thierry
источник
11
Самый простой способ - использовать лимиты
хэдли
1
Не могли бы привести пример в этом контексте Хэдли? Я не уверен, как использовать ограничения с фактором.
Тьерри
@ Тьерри Спасибо. Я был рад получить ответы на мой первый пост. И спасибо Тьерри или добавлению воспроизводимого кода, как я должен был в своем посте ... Мои категориальные переменные были правильными типами - факторами. Другой вопрос - я хочу, чтобы легенда не показала неиспользованные факторы. R игнорирует неиспользуемые символьные переменные при построении легенды. Однако неиспользованные факторы сохраняются. Если я отбрасываю их, используя: subdata $ category <- factor (subdata $ category) [drop = TRUE], тогда легенда имеет правильное количество факторов, НО теряет отображение.
Винтур
11
@Thierry - в моих руках, используя ggplot2_0.9.3.1, этот метод (больше не работает); цвета, назначенные для категории fCategory, различны для двух графиков. Однако, к счастью, @wintour, я понял , что @hadley предполагает , что + scale_colour_discrete(drop=TRUE,limits = levels(dataset$fCategory))для сохранения цвета | коэффициент ассоциации , но, который работает, за исключением того , в моих руках, на падение = TRUE является НЕ соблюдается (я ожидаю , что это удалить уровень из легенда). Драт ... или это я?
Malcook
1
@malcook, вместо drop = TRUE, вам нужно указать, какие уровни вы хотите сохранить с помощью «пауз»: github.com/hadley/ggplot2/issues/1433
Эрик
17

Это старый пост, но я искал ответ на этот же вопрос,

Почему бы не попробовать что-то вроде:

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

Если у вас есть категориальные значения, я не вижу причины, почему это не должно работать.

Павлос Пантелиадис
источник
3
Это именно то, что делает ответ Джорана, но использует, myColors <- brewer.pal(5,"Set1"); names(myColors) <- levels(dat$grp)чтобы избежать необходимости вручную кодировать уровни.
Axeman
Тем не менее, ответ Джорана не жестко закодировать значения цветов. Есть случаи, когда вам нужно конкретное значение цвета для данного фактора.
Рене Ниффенеггер
Хотя в некоторых случаях я получаю обратную сторону «жесткого кодирования», я думаю, что слишком часто добавляемые разработчиками / программистами уровни абстракции делают их работу менее доступной, а не большей. Намерение на 100% ясно в этом случае. Кроме того, достаточно легко подумать о том, как создать вспомогательную функцию, которая расширяет этот пример и возвращает именованный вектор определенных цветов.
Мэтт Барстед
16

Основываясь на очень полезном ответе Джорана, я смог придумать это решение для стабильной цветовой шкалы для логического фактора ( 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

Поскольку ColorBrewer не очень полезен для бинарных цветовых шкал, два необходимых цвета определяются вручную.

Вот mybooleanимя столбца, myDataFrameсодержащего фактор ИСТИНА / ЛОЖЬ. dateи durationимена столбцов, которые должны быть сопоставлены с осями x и y графика в этом примере.

Мэриан
источник
Другой подход заключается в применении «as.character ()» к столбцу. Это сделает его строковым столбцом, который хорошо работает с руководством по масштабированию _ * _
Сахир Моосви