R根据cutree标签从树状图中获取子树
R getting subtrees from dendrogram based on cutree labels
我对一个大型数据集进行了聚类,发现了 6 个我有兴趣深入分析的聚类。
我发现集群使用 hclust 和 "ward.D" 方法,我想知道是否有办法从 hclust/dendrogram 对象中获取 "sub-trees"。
例如
library(gplots)
library(dendextend)
data <- iris[,1:4]
distance <- dist(data, method = "euclidean", diag = FALSE, upper = FALSE)
hc <- hclust(distance, method = 'ward.D')
dnd <- as.dendrogram(hc)
plot(dnd) # to decide the number of clusters
clusters <- cutree(dnd, k = 6)
我使用 cutree
获取数据集中每一行的标签。
我知道我可以通过以下方式获取每个相应集群(例如集群 1)的数据:
c1_data = data[clusters == 1,]
是否有任何简单的方法来获取 dendextend::cutree
返回的每个相应标签的子树?例如,假设我有兴趣获得
我知道我可以访问树状图的分支做类似的事情
subtree <- dnd[[1]][[2]
但是我怎样才能准确地得到对应于簇 1 的子树呢?
我试过了
dnd[clusters == 1]
但这当然行不通。那么如何根据cutree返回的标签得到子树呢?
你是如何使用 hclust 得到 6 个集群的?你可以随时砍树,所以你只需要让 cuttree 给你更多的集群:
clusters = cutree(hclusters, number_of_clusters)
如果您有大量数据,这可能不是很方便。在这些情况下,我所做的是手动选择我想进一步研究的集群,然后仅对这些集群中的数据进行 运行 hclust。我不知道 hclust 中有什么功能可以让您自动执行此操作,但这很简单:
good_clusters = c(which(clusters==1),
which(clusters==2)) #or whichever cLusters you want
new_df = df[good_clusters,]
new_hclusters = hclust(new_df)
new_clusters = cutree(new_hclusters, new_number_of_clusters)
================= 更新的答案
现在可以使用 dendextend
中的 get_subdendrograms
来解决这个问题。
# needed packages:
# install.packages(gplots)
# install.packages(viridis)
# install.packages(devtools)
# devtools::install_github('talgalili/dendextend') # dendextend from github
# define dendrogram object to play with:
dend <- iris[,-5] %>% dist %>% hclust %>% as.dendrogram %>% set("labels_to_character") %>% color_branches(k=5)
dend_list <- get_subdendrograms(dend, 5)
# Plotting the result
par(mfrow = c(2,3))
plot(dend, main = "Original dendrogram")
sapply(dend_list, plot)
这也可以在热图中使用:
# plot a heatmap of only one of the sub dendrograms
par(mfrow = c(1,1))
library(gplots)
sub_dend <- dend_list[[1]] # get the sub dendrogram
# make sure of the size of the dend
nleaves(sub_dend)
length(order.dendrogram(sub_dend))
# get the subset of the data
subset_iris <- as.matrix(iris[order.dendrogram(sub_dend),-5])
# update the dendrogram's internal order so to not cause an error in heatmap.2
order.dendrogram(sub_dend) <- rank(order.dendrogram(sub_dend))
heatmap.2(subset_iris, Rowv = sub_dend, trace = "none", col = viridis::viridis(100))
================= 旧答案
我觉得对你有帮助的是这两个功能:
第一个只是遍历所有集群并提取子结构。它需要:
- 我们要从中获取子树状图的
dendrogram
对象
- 簇标签(例如
cutree
返回)
Returns 子树状图列表。
extractDendrograms <- function(dendr, clusters){
lapply(unique(clusters), function(clust.id){
getSubDendrogram(dendr, which(clusters==clust.id))
})
}
第二个执行深度优先搜索以确定集群存在于哪个子树中,以及它是否与整个集群匹配 returns。在这里,我们假设集群的所有元素都在一个子树中。它需要:
- 树状图对象
- 簇中元素的位置
Returns 对应给定元素簇的子树状图。
getSubDendrogram<-function(dendr, my.clust){
if(all(unlist(dendr) %in% my.clust))
return(dendr)
if(any(unlist(dendr[[1]]) %in% my.clust ))
return(getSubDendrogram(dendr[[1]], my.clust))
else
return(getSubDendrogram(dendr[[2]], my.clust))
}
使用这两个函数,我们可以使用您在问题中提供的变量并获得以下输出。 (我认为 clusters <- cutree(dnd, k = 6)
行应该是 clusters <- cutree(hc, k = 6)
)
my.sub.dendrograms <- extractDendrograms(dnd, clusters)
绘制列表中的所有六个元素给出所有子树状图
编辑
正如评论中所建议的,我添加了一个函数,作为输入采用树状图 dend
和子树的数量 k
,但它仍然使用先前定义的递归函数 getSubDendrogram
:
prune_cutree_to_dendlist <- function(dend, k, order_clusters_as_data=FALSE) {
clusters <- cutree(dend, k, order_clusters_as_data)
lapply(unique(clusters), function(clust.id){
getSubDendrogram(dend, which(clusters==clust.id))
})
}
5 个子结构的测试用例:
library(dendextend)
dend <- iris[,-5] %>% dist %>% hclust %>% as.dendrogram %>% set("labels_to_character") %>% color_branches(k=5)
subdend.list <- prune_cutree_to_dendlist(dend, 5)
#plotting
par(mfrow = c(2,3))
plot(dend, main = "original dend")
sapply(prunned_dends, plot)
我使用 rbenchmark
和 Tal Galili 建议的函数(此处命名为 prune_cutree_to_dendlist2
)执行了一些基准测试,结果对于上述 DFS 方法来说非常有希望:
library(rbenchmark)
benchmark(prune_cutree_to_dendlist(dend, 5),
prune_cutree_to_dendlist2(dend, 5), replications=5)
test replications elapsed relative user.self
1 prune_cutree_to_dendlist(dend, 5) 5 0.02 1 0.020
2 prune_cutree_to_dendlist2(dend, 5) 5 60.82 3041 60.643
我现在编写函数 prune_cutree_to_dendlist
来执行您要求的操作。我应该在将来的某个时候将它添加到 dendextend。
同时,这里有一个代码和输出的例子(功能有点慢。让它更快依赖于剪枝更快,我不会在不久的将来修复。)
# install.packages("dendextend")
library(dendextend)
dend <- iris[,-5] %>% dist %>% hclust %>% as.dendrogram %>%
set("labels_to_character")
dend <- dend %>% color_branches(k=5)
# plot(dend)
prune_cutree_to_dendlist <- function(dend, k) {
clusters <- cutree(dend,k, order_clusters_as_data = FALSE)
# unique_clusters <- unique(clusters) # could also be 1:k but it would be less robust
# k <- length(unique_clusters)
# for(i in unique_clusters) {
dends <- vector("list", k)
for(i in 1:k) {
leves_to_prune <- labels(dend)[clusters != i]
dends[[i]] <- prune(dend, leves_to_prune)
}
class(dends) <- "dendlist"
dends
}
prunned_dends <- prune_cutree_to_dendlist(dend, 5)
sapply(prunned_dends, nleaves)
par(mfrow = c(2,3))
plot(dend, main = "original dend")
sapply(prunned_dends, plot)
我对一个大型数据集进行了聚类,发现了 6 个我有兴趣深入分析的聚类。
我发现集群使用 hclust 和 "ward.D" 方法,我想知道是否有办法从 hclust/dendrogram 对象中获取 "sub-trees"。
例如
library(gplots)
library(dendextend)
data <- iris[,1:4]
distance <- dist(data, method = "euclidean", diag = FALSE, upper = FALSE)
hc <- hclust(distance, method = 'ward.D')
dnd <- as.dendrogram(hc)
plot(dnd) # to decide the number of clusters
clusters <- cutree(dnd, k = 6)
我使用 cutree
获取数据集中每一行的标签。
我知道我可以通过以下方式获取每个相应集群(例如集群 1)的数据:
c1_data = data[clusters == 1,]
是否有任何简单的方法来获取 dendextend::cutree
返回的每个相应标签的子树?例如,假设我有兴趣获得
我知道我可以访问树状图的分支做类似的事情
subtree <- dnd[[1]][[2]
但是我怎样才能准确地得到对应于簇 1 的子树呢?
我试过了
dnd[clusters == 1]
但这当然行不通。那么如何根据cutree返回的标签得到子树呢?
你是如何使用 hclust 得到 6 个集群的?你可以随时砍树,所以你只需要让 cuttree 给你更多的集群:
clusters = cutree(hclusters, number_of_clusters)
如果您有大量数据,这可能不是很方便。在这些情况下,我所做的是手动选择我想进一步研究的集群,然后仅对这些集群中的数据进行 运行 hclust。我不知道 hclust 中有什么功能可以让您自动执行此操作,但这很简单:
good_clusters = c(which(clusters==1),
which(clusters==2)) #or whichever cLusters you want
new_df = df[good_clusters,]
new_hclusters = hclust(new_df)
new_clusters = cutree(new_hclusters, new_number_of_clusters)
================= 更新的答案
现在可以使用 dendextend
中的 get_subdendrograms
来解决这个问题。
# needed packages:
# install.packages(gplots)
# install.packages(viridis)
# install.packages(devtools)
# devtools::install_github('talgalili/dendextend') # dendextend from github
# define dendrogram object to play with:
dend <- iris[,-5] %>% dist %>% hclust %>% as.dendrogram %>% set("labels_to_character") %>% color_branches(k=5)
dend_list <- get_subdendrograms(dend, 5)
# Plotting the result
par(mfrow = c(2,3))
plot(dend, main = "Original dendrogram")
sapply(dend_list, plot)
这也可以在热图中使用:
# plot a heatmap of only one of the sub dendrograms
par(mfrow = c(1,1))
library(gplots)
sub_dend <- dend_list[[1]] # get the sub dendrogram
# make sure of the size of the dend
nleaves(sub_dend)
length(order.dendrogram(sub_dend))
# get the subset of the data
subset_iris <- as.matrix(iris[order.dendrogram(sub_dend),-5])
# update the dendrogram's internal order so to not cause an error in heatmap.2
order.dendrogram(sub_dend) <- rank(order.dendrogram(sub_dend))
heatmap.2(subset_iris, Rowv = sub_dend, trace = "none", col = viridis::viridis(100))
================= 旧答案
我觉得对你有帮助的是这两个功能:
第一个只是遍历所有集群并提取子结构。它需要:
- 我们要从中获取子树状图的
dendrogram
对象 - 簇标签(例如
cutree
返回)
Returns 子树状图列表。
extractDendrograms <- function(dendr, clusters){
lapply(unique(clusters), function(clust.id){
getSubDendrogram(dendr, which(clusters==clust.id))
})
}
第二个执行深度优先搜索以确定集群存在于哪个子树中,以及它是否与整个集群匹配 returns。在这里,我们假设集群的所有元素都在一个子树中。它需要:
- 树状图对象
- 簇中元素的位置
Returns 对应给定元素簇的子树状图。
getSubDendrogram<-function(dendr, my.clust){
if(all(unlist(dendr) %in% my.clust))
return(dendr)
if(any(unlist(dendr[[1]]) %in% my.clust ))
return(getSubDendrogram(dendr[[1]], my.clust))
else
return(getSubDendrogram(dendr[[2]], my.clust))
}
使用这两个函数,我们可以使用您在问题中提供的变量并获得以下输出。 (我认为 clusters <- cutree(dnd, k = 6)
行应该是 clusters <- cutree(hc, k = 6)
)
my.sub.dendrograms <- extractDendrograms(dnd, clusters)
绘制列表中的所有六个元素给出所有子树状图
编辑
正如评论中所建议的,我添加了一个函数,作为输入采用树状图 dend
和子树的数量 k
,但它仍然使用先前定义的递归函数 getSubDendrogram
:
prune_cutree_to_dendlist <- function(dend, k, order_clusters_as_data=FALSE) {
clusters <- cutree(dend, k, order_clusters_as_data)
lapply(unique(clusters), function(clust.id){
getSubDendrogram(dend, which(clusters==clust.id))
})
}
5 个子结构的测试用例:
library(dendextend)
dend <- iris[,-5] %>% dist %>% hclust %>% as.dendrogram %>% set("labels_to_character") %>% color_branches(k=5)
subdend.list <- prune_cutree_to_dendlist(dend, 5)
#plotting
par(mfrow = c(2,3))
plot(dend, main = "original dend")
sapply(prunned_dends, plot)
我使用 rbenchmark
和 Tal Galili 建议的函数(此处命名为 prune_cutree_to_dendlist2
)执行了一些基准测试,结果对于上述 DFS 方法来说非常有希望:
library(rbenchmark)
benchmark(prune_cutree_to_dendlist(dend, 5),
prune_cutree_to_dendlist2(dend, 5), replications=5)
test replications elapsed relative user.self
1 prune_cutree_to_dendlist(dend, 5) 5 0.02 1 0.020
2 prune_cutree_to_dendlist2(dend, 5) 5 60.82 3041 60.643
我现在编写函数 prune_cutree_to_dendlist
来执行您要求的操作。我应该在将来的某个时候将它添加到 dendextend。
同时,这里有一个代码和输出的例子(功能有点慢。让它更快依赖于剪枝更快,我不会在不久的将来修复。)
# install.packages("dendextend")
library(dendextend)
dend <- iris[,-5] %>% dist %>% hclust %>% as.dendrogram %>%
set("labels_to_character")
dend <- dend %>% color_branches(k=5)
# plot(dend)
prune_cutree_to_dendlist <- function(dend, k) {
clusters <- cutree(dend,k, order_clusters_as_data = FALSE)
# unique_clusters <- unique(clusters) # could also be 1:k but it would be less robust
# k <- length(unique_clusters)
# for(i in unique_clusters) {
dends <- vector("list", k)
for(i in 1:k) {
leves_to_prune <- labels(dend)[clusters != i]
dends[[i]] <- prune(dend, leves_to_prune)
}
class(dends) <- "dendlist"
dends
}
prunned_dends <- prune_cutree_to_dendlist(dend, 5)
sapply(prunned_dends, nleaves)
par(mfrow = c(2,3))
plot(dend, main = "original dend")
sapply(prunned_dends, plot)