Рассмотрим следующий упрощенный фрейм данных:
x = c(.35, .35, .37, .5, .55, .56, .9, .91, .89)
y = c(.35, .36, .35, .22, .27, .25, .88, .9, .87)
clu3 = as.factor(c(31,31,31,32,32,32,33,33,33))
clu4 = as.factor(c(41,41,41,42,43,43,44,44,44))
df = data.frame (x,y,clu3,clu4)
В моем анализе три кластера сначала соответствуют набору данных (clu3, который имеет уровни фактора 31, 32 и 33). Затем к набору данных также подходят четыре кластера (clu4, который имеет уровни факторов 41, 42, 43, 44), а затем пять кластеров, шесть кластеров и так далее. Я включил только результаты подгонки трех и четырех кластеров для простоты.
Я могу построить результаты для каждого «прогона» (т. е. прогона с тремя кластерами и прогона с четырьмя кластерами), используя:
ggplot(df, aes(x=x, y=y, color=clu3)) +
geom_point(size=4)+
theme_bw()+
ggtitle(paste("Three-cluster scatterplot"))+
theme(plot.title = element_text(hjust = 0.5))
ggplot(df, aes(x=x, y=y, color=clu4)) +
geom_point(size=4)+
theme_bw()+
ggtitle(paste("Four-cluster scatterplot"))+
theme(plot.title = element_text(hjust = 0.5))
Прямо сейчас я указываю цвет кластера в ggplot. Но в моем примере кластеры 31 и 41 идентичны (но из разных "прогонов") и кластеры 33 и 44 тоже идентичны. В более поздних прогонах также есть дополнительные идентичные кластеры (при подгонке пяти кластеров к данным, шести и т. д.). Что я хотел бы сделать, так это кратко указать цвет уровней факторов различных переменных (в данном случае clu3 и clu4). В восприятии это будет примерно так:
"31" | "41" = "purple"
"33" | "44" = "green"
"32" = "blue"
"42" = "orange"
"43" = "yellow"
Я предполагаю, что решение включает в себя scale_fill_manual, и я читал о способах согласования цветов уровня факторов на графиках (даже если некоторые уровни факторов не используются). Но во всех этих примерах уровни факторов относятся к одной и той же переменной, тогда как я хочу сделать разные уровни факторов из разных переменных согласованными цветами. Любой совет очень ценится!
В этом игрушечном примере это менее ясно, но в моем реальном наборе данных я вычисляю евклидово расстояние между центроидами кластера, чтобы определить, какие из них являются дубликатами. Я итеративно подгоняю кластеры 2:9 к набору данных, поэтому неизбежно есть почти идентичные кластеры. Теперь я хотел бы отслеживать эти кластеры при каждом запуске, поэтому я надеялся установить цвет кластера чистым способом.
То есть вы имеете в виду, что будете вручную идентифицировать такие кластеры и захотите изменить используемые для них цвета?
Привет, Ронак, да. Я вручную идентифицирую повторяющиеся кластеры. На данный момент я должен указать цвет для каждого отдельного кластера, чтобы те, которые были идентифицированы как дубликаты, были одного цвета на графиках.
ПРИМЕЧАНИЕ. Это решение работает для случая с двумя кластерами в данных вашего примера. Вам нужно будет использовать другую логику (кроме первого/последнего) для создания new_clu
для> 2 кластеров.
Одним из решений является преобразование данных в «длинную» форму, группировка по x
и y
, а затем создание новой переменной new_clu
на основе значений clu3
и clu4
.
Примечания: я использую gather
, но вы можете использовать более новый pivot_longer
. О неидентичных атрибутах будет сгенерировано предупреждение, его можно игнорировать.
df %>%
gather(Cluster, Val, 3:4) %>%
group_by(x, y) %>%
mutate(new_clu = case_when(
first(Val) == 31 & last(Val) == 41 ~ paste0(first(Val), "/", last(Val)),
first(Val) == 33 & last(Val) == 44 ~ paste0(first(Val), "/", last(Val)),
TRUE ~ Val
)
)
Результат:
# A tibble: 18 x 5
# Groups: x, y [9]
x y Cluster Val new_clu
<dbl> <dbl> <chr> <chr> <chr>
1 0.35 0.35 clu3 31 31/41
2 0.35 0.36 clu3 31 31/41
3 0.37 0.35 clu3 31 31/41
4 0.5 0.22 clu3 32 32
5 0.55 0.27 clu3 32 32
6 0.56 0.25 clu3 32 32
7 0.9 0.88 clu3 33 33/44
8 0.91 0.9 clu3 33 33/44
9 0.89 0.87 clu3 33 33/44
10 0.35 0.35 clu4 41 31/41
11 0.35 0.36 clu4 41 31/41
12 0.37 0.35 clu4 41 31/41
13 0.5 0.22 clu4 42 42
14 0.55 0.27 clu4 43 43
15 0.56 0.25 clu4 43 43
16 0.9 0.88 clu4 44 33/44
17 0.91 0.9 clu4 44 33/44
18 0.89 0.87 clu4 44 33/44
Теперь вы можете передать это ggplot
и раскрасить new_clu
:
df %>%
gather(Cluster, Val, 3:4) %>%
group_by(x, y) %>%
mutate(new_clu = case_when(
first(Val) == 31 & last(Val) == 41 ~ paste0(first(Val), "/", last(Val)),
first(Val) == 33 & last(Val) == 44 ~ paste0(first(Val), "/", last(Val)),
TRUE ~ Val
)
) %>%
ggplot(aes(x, y)) +
geom_point(aes(color = new_clu))
Результат:
Как вы предложили, использование scale_fill_manual
или scale_color_manual
является допустимым вариантом.
Вы можете написать функцию, которая сопоставляет цвета между двумя кластеризациями (например, относительно кластеров первой или предыдущей кластеризации).
Вот один из способов, которым вы можете сопоставить цвета и последовательно применить их к нескольким кластерам:
library(ggplot2)
x <- c(.35, .35, .37, .5, .55, .56, .9, .91, .89)
y <- c(.35, .36, .35, .22, .27, .25, .88, .9, .87)
clu3 <- factor(c(31, 31, 31, 32, 32, 32, 33, 33, 33))
clu4 <- factor(c(41, 41, 41, 42, 43, 43, 44, 44, 44))
clu5 <- factor(c(51, 51, 52, 53, 54, 54, 55, 55, 55)) # added a few more clusters
clu6 <- factor(c(61, 61, 62, 63, 64, 64, 65, 66, 65))
df <- data.frame(x, y, clu3, clu4, clu5, clu6)
## assign specific colors to matching clusters; rest: use same colors
matchCol <- function(fac1, fac2, pal=c("#999999", "#E69F00", "#56B4E9",
"#009E73", "#F0E442", "#0072B2",
"#D55E00", "#CC79A7")){
maxl <- max(length(levels(fac1)), length(levels(fac2)))
if (length(pal) < maxl) { # make sure you have enough colors
warning("Not enough colors; using scales::hue_pal")
pal <- scales::hue_pal()(maxl)
}
tab <- as.matrix(table(fac1, fac2)) > 0
rs1 <- which(rowSums(tab) == 1)
rs2 <- apply(tab[rs1, , drop=FALSE], 1, which.max)
f1 <- setNames(pal[seq_along(levels(fac1))], levels(fac1))
f2 <- setNames(NA[seq_along(levels(fac2))], levels(fac2))
f2[levels(fac2)[rs2]] <- f1[levels(fac1)[rs1]] # add matching colors
f2n <- names(f2)
if (!identical(fac1, fac2)) f2n[rs2] <- paste0(levels(fac1)[rs1], " | ", levels(fac2)[rs2])
f2[is.na(f2)] <- setdiff(pal, f2)[seq_along(f2[is.na(f2)])] # fill in remaining colors
list(fac1=f1, fac2=f2, f2n=f2n ) # you only need f2 here, so could simplify
}
# then plot using matchCol function, e.g.:
ggplot(df, aes(x=x, y=y, color=clu4)) +
geom_point(size=4)+
theme_bw()+
ggtitle(paste("Four-cluster scatterplot"))+
theme(plot.title = element_text(hjust = 0.5)) +
scale_color_manual(values=matchCol(clu3, clu4)$fac2,
labels=matchCol(clu3, clu4)$f2)
# or generalized
clusts <- grep("clu", colnames(df), value=TRUE)
p1 <- lapply(clusts, function(z){
mc <- matchCol(get(clusts[1]), get(z))
ggplot(df, aes_string(x = "x", y = "y", color=z)) +
geom_point(size=4)+
theme_bw()+
ggtitle(paste0(gsub("clu", "", z),"-cluster scatterplot"))+
theme(plot.title = element_text(hjust = 0.5)) +
scale_color_manual(values=mc$fac2, labels=mc$f2)
}
)
cowplot::plot_grid(plotlist = p1)
# same, relative to previous clustering:
p2 <- lapply(seq_along(clusts), function(z){
mc <- matchCol(get(clusts[max(1, z-1)]), get(clusts[z]))
ggplot(df, aes_string(x = "x", y = "y", color=clusts[z])) +
geom_point(size=4)+
theme_bw()+
ggtitle(paste0(gsub("clu", "", clusts[z]),"-cluster scatterplot"))+
theme(plot.title = element_text(hjust = 0.5)) +
scale_color_manual(values=mc$fac2, labels=mc$f2)
}
)
cowplot::plot_grid(plotlist = p2)
Created on 2020-12-17 by the reprex package (v0.3.0)
Откуда вы знаете, что кластеры 31 и 41 идентичны или кластеры 33 и 44 идентичны?