Извлечь цвет кластера из вывода dendextend:: circlize_dendrogram()

Я пытаюсь извлечь цвета, используемые в кластеризации circlize_dendrogram. Вот пример кода:

library(magrittr)
library(dendextend)

cols <- c("#009000", "#FF033E", "#CB410B", "#3B444B", "#007FFF")
dend <- iris[1:40,-5] %>% dist %>% hclust %>% as.dendrogram    

dend <- color_branches(dend, k = 5, col = cols)
dend %<>% set("labels_col", value = cols, k= 5)
dend %<>% set("labels_cex", .8)
dend %<>% set("branches_lwd", 2)

circlize_dendrogram(dend)

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

Чтобы таблицы с таблицами были извлечены с помощью cutree(dend, k = 5). Есть ли способ извлечь цвета кластеров в дендрограмме на основе cols? Мне нужно это для вставки легенды в сюжет с использованием пакета grid.

Пример, Легенда: Кластер 1 - #009000; Кластер 2 - #FF033E; Кластер 3 - #CB410B; Кластер 4 - #3B444B; Кластер 5 - #007FFF. Проблема с circlize_dendrogram заключается в том, что порядок цветов, используемых для кластера, отличается.

Хотя я могу сделать это вручную, было бы эффективно, если бы я мог сделать это автоматически. И это возможно, если я смогу извлечь цвета кластеров.

Ответ 1

Хорошо, вот очень хакерское решение. Я убежден, что есть лучшие, но это первый удар, так что несите меня.

Идея состоит в том, чтобы искать объект dend (который является внутренним списком) для соответствующих имен элементов (в данном случае только числа) и извлекать соответствующий цвет, сохранять его в кадре данных и использовать его для легенда.

# First we'll extract the elements and corresponding categories...
categories <- cutree(dend, k = 5)

# ... and save them in a data frame
categories_df <- data.frame(elements = as.numeric(names(categories)),
       categories = categories, 
       color = NA)

# now here a little function that extracts the color for each element
# from the 'dend' object. It uses the list.search() function from the
# 'rlist' package

library(rlist)

extract_color <- function(element_no, dend_obj) {
  dend.search <- list.search(dend_obj, all(. == element_no))
  color <- attr(dend.search[[1]], "edgePar")$col
  return(color)
}

# I use 'dplyr' to manipulate the data
library(dplyr)

categories_df <- categories_df %>% 
  group_by(elements) %>% 
  mutate(color = extract_color(elements, dend))

Теперь это дает нам следующий фрейм данных:

> categories_df
Source: local data frame [40 x 3]
Groups: elements [40]

   elements categories   color
      (dbl)      (int)   (chr)
1         1          1 #CB410B
2         2          1 #CB410B
3         3          1 #CB410B
4         4          1 #CB410B
5         5          1 #CB410B
6         6          2 #009000
7         7          1 #CB410B
8         8          1 #CB410B
9         9          3 #007FFF
10       10          1 #CB410B
..      ...        ...     ...

Мы можем суммировать это с кадром данных только с цветами для категорий, например

legend_data <- categories_df %>% 
  group_by(categories) %>% 
  summarise(color = unique(color))

> legend_data
Source: local data frame [5 x 2]

  categories   color
       (int)   (chr)
1          1 #CB410B
2          2 #009000
3          3 #007FFF
4          4 #FF033E
5          5 #3B444B

Теперь легко сгенерировать легенду:

circlize_dendrogram(dend)
legend(-1.05, 1.05, legend = legend_data$categories, fill = legend_data$color, cex = 0.7)

Что дает вам:

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

Вы можете использовать cutree(dend, k = 5) для подтверждения того, что номера цветов категорий соответствуют категории каждого элемента.

Ответ 2

В дополнение к решению Felix я хотел бы опубликовать свой собственный ответ:

library(magrittr)
library(grid)
library(gridExtra)
library(dendextend)

cols <- c("#009000", "#FF033E", "#CB410B", "#3B444B", "#007FFF")
dend <- iris[1:40,-5] %>% dist %>% hclust %>% as.dendrogram    

dend <- color_branches(dend, k = 5, col = cols)
dend %<>% set("labels_col", value = cols, k= 5)
dend %<>% set("labels_cex", .8)
dend %<>% set("branches_lwd", 2)

clust <- cutree(dend, k = 5)
colors <- labels_colors(dend)[clust %>% sort %>% names]
clust_labs <- colors %>% unique

circlize_dendrogram(dend)
grid.circle(x = .95, y = .9, r = .02, gp = gpar(fill = clust_labs[1])) 
grid.circle(x = .95, y = .85, r = .02, gp = gpar(fill = clust_labs[2]))
grid.circle(x = .95, y = .8, r = .02, gp = gpar(fill = clust_labs[3]))
grid.circle(x = .95, y = .75, r = .02, gp = gpar(fill = clust_labs[4]))
grid.circle(x = .95, y = .7, r = .02, gp = gpar(fill = clust_labs[5]))

grid.text(x = .95, y = .9, label = expression(bold(1)), gp = gpar(fontsize = 9, col = "white"))
grid.text(x = .95, y = .85, label = expression(bold(2)), gp = gpar(fontsize = 9, col = "white"))
grid.text(x = .95, y = .8, label = expression(bold(3)), gp = gpar(fontsize = 9, col = "white"))
grid.text(x = .95, y = .75, label = expression(bold(4)), gp = gpar(fontsize = 9, col = "white"))
grid.text(x = .95, y = .7, label = expression(bold(5)), gp = gpar(fontsize = 9, col = "white"))
grid.text(x = .91, y = .8, label = "CLUSTERS", rot = 90, gp = gpar(fontsize = 9))

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