Срез дерева и прямоугольники вокруг скоплений для горизонтальной дендрограммы в R
Я пытаюсь представить результаты иерархической кластеризации в R
как дендрограмма, с прямоугольниками, идентифицирующими кластеры.
Следующий код выполняет трюк для вертикальной дендрограммы, но для горизонтальной дендрограммы, (horiz=TRUE
), прямоугольники не нарисованы. Есть ли способ сделать то же самое для горизонтальных дендрограмм тоже.
library("cluster")
dst <- daisy(iris, metric = c("gower"), stand = FALSE)
hca <- hclust(dst, method = "average")
plot(as.dendrogram(hca), horiz = FALSE)
rect.hclust(hca, k = 3, border = "red")
Кроме того, я хотел бы построить линию, чтобы отрезать дерево на желаемом расстоянии. Как построить это в R. cutree
Функция возвращает кластеры, но есть ли возможность их построить.
cutree(hca, k = 3)
Желаемый результат, который я ищу, таков.
Как это сделать в R?
3 ответа
Ответы jlhoward и Backlin хороши.
То, что вы также можете попробовать, это использовать dendextend
пакет, предназначенный именно для такого рода вещей. Оно имеет rect.dendrogram
функция, которая работает как rect.hclust
, но с параметром horiz (плюс еще немного контроля над расположением края прямоугольника). Для нахождения соответствующей высоты вы можете использовать heights_per_k.dendrogram
функция (которая намного быстрее при использовании dendextendRcpp
пакет)
Вот простой пример того, как вы получите тот же результат, что и в приведенных выше примерах (с дополнительным бонусом цветных веток, просто для удовольствия):
install.packages("dendextend")
install.packages("dendextendRcpp")
library("dendextend")
library("dendextendRcpp")
# using piping to get the dend
dend <- iris[,-5] %>% dist %>% hclust %>% as.dendrogram
# plot + color the dend's branches before, based on 3 clusters:
dend %>% color_branches(k=3) %>% plot(horiz=TRUE, main = "The dendextend package \n Gives extended functionality to R's dendrogram object")
# add horiz rect
dend %>% rect.dendrogram(k=3,horiz=TRUE)
# add horiz (well, vertical) line:
abline(v = heights_per_k.dendrogram(dend)["3"] + .6, lwd = 2, lty = 2, col = "blue")
Вот решение с использованием ggplot
и ggdendro
пакет. В качестве дополнительного бонуса мы можем раскрасить этикетки по кластерам...
library(cluster)
dst <- daisy(iris, metric = c("gower"), stand = FALSE)
hca <- hclust(dst, method = "average")
k <- 3
clust <- cutree(hca,k=k) # k clusters
library(ggplot2)
library(ggdendro) # for dendro_data(...)
dendr <- dendro_data(hca, type="rectangle") # convert for ggplot
clust.df <- data.frame(label=rownames(iris), cluster=factor(clust))
dendr[["labels"]] <- merge(dendr[["labels"]],clust.df, by="label")
rect <- aggregate(x~cluster,label(dendr),range)
rect <- data.frame(rect$cluster,rect$x)
ymax <- mean(hca$height[length(hca$height)-((k-2):(k-1))])
ggplot() +
geom_segment(data=segment(dendr), aes(x=x, y=y, xend=xend, yend=yend)) +
geom_text(data=label(dendr), aes(x, y, label=label, hjust=0, color=cluster),
size=3) +
geom_rect(data=rect, aes(xmin=X1-.3, xmax=X2+.3, ymin=0, ymax=ymax),
color="red", fill=NA)+
geom_hline(yintercept=0.33, color="blue")+
coord_flip() + scale_y_reverse(expand=c(0.2, 0)) +
theme_dendro()
Чтобы просто выполнить работу (хотя и довольно уродливо), вы можете просто поменять местами координаты в вызове rect
в rect.hclust
:
rhc <- function (tree, k = NULL, which = NULL, x = NULL, h = NULL, border = 2,
cluster = NULL)
{
if (length(h) > 1L | length(k) > 1L)
stop("'k' and 'h' must be a scalar")
if (!is.null(h)) {
if (!is.null(k))
stop("specify exactly one of 'k' and 'h'")
k <- min(which(rev(tree$height) < h))
k <- max(k, 2)
}
else if (is.null(k))
stop("specify exactly one of 'k' and 'h'")
if (k < 2 | k > length(tree$height))
stop(gettextf("k must be between 2 and %d", length(tree$height)),
domain = NA)
if (is.null(cluster))
cluster <- cutree(tree, k = k)
clustab <- table(cluster)[unique(cluster[tree$order])]
m <- c(0, cumsum(clustab))
if (!is.null(x)) {
if (!is.null(which))
stop("specify exactly one of 'which' and 'x'")
which <- x
for (n in seq_along(x)) which[n] <- max(which(m < x[n]))
}
else if (is.null(which))
which <- 1L:k
if (any(which > k))
stop(gettextf("all elements of 'which' must be between 1 and %d",
k), domain = NA)
border <- rep_len(border, length(which))
retval <- list()
for (n in seq_along(which)) {
rect(
ybottom = m[which[n]] + 0.66,
xright = par("usr")[3L],
ytop = m[which[n] + 1] + 0.33,
xleft = mean(rev(tree$height)[(k - 1):k]),
border = border[n])
retval[[n]] <- which(cluster == as.integer(names(clustab)[which[n]]))
}
invisible(retval)
}
и позвонить rhc
как ты звонил rect.hclust
:
rhc(hca, k = 3, border = "red")