从R中的剪切树状图(即用于树状图的cutree函数)中提取标签成员身份/分类。

13
我试图从在R中切割(cut)到一定高度的树状图中提取分类信息。对于hclust对象,可以使用cutree轻松完成此操作,但我无法弄清如何在dendrogram对象上执行此操作。
此外,我不能仅使用原始hclust的聚类,因为(令人沮丧的是),cutree的类编号与cut的类编号不同。
hc <- hclust(dist(USArrests), "ave")

classification<-cutree(hc,h=70)

dend1 <- as.dendrogram(hc)
dend2 <- cut(dend1, h = 70)


str(dend2$lower[[1]]) #group 1 here is not the same as
classification[classification==1] #group 1 here

有没有办法让分类彼此映射,或者从 dendrogram 对象中提取较低层次的成员(也许可以巧妙地使用 dendrapply?)以一种更类似于 cutree 的格式呈现?

3个回答

15
我建议您使用dendextend包中的cutree函数。它包含一个树状图方法(即:dendextend:::cutree.dendrogram)。 您可以从其介绍性vignette了解有关该包的更多信息。 我应该补充说明,虽然您的函数(classify)很好,但使用dendextendcutree有几个优点:
  1. 它还允许您使用特定的k(聚类数),而不仅仅是h(特定高度)。

  2. 它与在hclust上使用cutree获得的结果一致(classify不会)。

  3. 它通常更快。

这里是使用代码的示例:
# Toy data:
hc <- hclust(dist(USArrests), "ave")
dend1 <- as.dendrogram(hc)

# Get the package:
install.packages("dendextend")
library(dendextend)

# Get the package:
cutree(dend1,h=70) # it now works on a dendrogram
# It is like using:
dendextend:::cutree.dendrogram(dend1,h=70)

顺便说一下,基于这个函数,dendextend 允许用户做更酷的事情,比如根据剪切树状图来着色分支/标签:
dend1 <- color_branches(dend1, k = 4)
dend1 <- color_labels(dend1, k = 5)
plot(dend1)

enter image description here

最后,这里有更多的代码来演示我的其他观点:
# This would also work with k:
cutree(dend1,k=4)

# and would give identical result as cutree on hclust:
identical(cutree(hc,h=70)  , cutree(dend1,h=70)  )
   # TRUE

# But this is not the case for classify:
identical(classify(dend1,70)   , cutree(dend1,h=70)  )
   # FALSE


install.packages("microbenchmark")
require(microbenchmark)
microbenchmark(classify = classify(dend1,70),
               cutree = cutree(dend1,h=70)  )
#    Unit: milliseconds
#        expr      min       lq   median       uq       max neval
#    classify  9.70135  9.94604 10.25400 10.87552  80.82032   100
#      cutree 37.24264 37.97642 39.23095 43.21233 141.13880   100
# 4 times faster for this tree (it will be more for larger trees)

# Although (if to be exact about it) if I force cutree.dendrogram to not go through hclust (which can happen for "weird" trees), the speed will remain similar:
microbenchmark(classify = classify(dend1,70),
               cutree = cutree(dend1,h=70, try_cutree_hclust = FALSE)  )
# Unit: milliseconds
#        expr       min        lq    median       uq      max neval
#    classify  9.683433  9.819776  9.972077 10.48497 29.73285   100
#      cutree 10.275839 10.419181 10.540126 10.66863 16.54034   100

如果您想改进此功能,请通过此处打补丁:

https://github.com/talgalili/dendextend/blob/master/R/cutree.dendrogram.R

我希望你或其他人能够从这个答案中获得帮助。

1
非常感谢您的回答和开发这个精彩软件包! - Oreotrephes
谢谢你的赞美之词,Oreotrephes。如果你遇到任何问题或者有新的功能建议,请告诉我。祝好,Tal。 - Tal Galili
1
有没有办法从 color_branches(dend1, k = 4) 中提取组分配? - user2117258
我对从color_branches()中提取组分配很感兴趣。 - mirkuz

8

最终我使用dendrapply创建了一个函数来完成这个任务。虽然不够优雅,但它可以实现。

classify <- function(dendrogram,height){

#mini-function to use with dendrapply to return tip labels
 members <- function(n) {
    labels<-c()
    if (is.leaf(n)) {
        a <- attributes(n)
        labels<-c(labels,a$label)
    }
    labels
 }

 dend2 <- cut(dendrogram,height) #the cut dendrogram object
 branchesvector<-c()
 membersvector<-c()

 for(i in 1:length(dend2$lower)){                             #for each lower tree resulting from the cut
  memlist <- unlist(dendrapply(dend2$lower[[i]],members))     #get the tip lables
  branchesvector <- c(branchesvector,rep(i,length(memlist)))  #add the lower tree identifier to a vector
  membersvector <- c(membersvector,memlist)                   #add the tip labels to a vector
 }
out<-as.integer(branchesvector)                               #make the output a list of named integers, to match cut() output
names(out)<-membersvector
out
}

使用该函数可以清晰地表明问题是cut按字母顺序分配类别名称,而cutree从左到右分配分支名称。
hc <- hclust(dist(USArrests), "ave")
dend1 <- as.dendrogram(hc)

classify(dend1,70) #Florida 1, North Carolina 1, etc.
cutree(hc,h=70)    #Alabama 1, Arizona 1, Arkansas 1, etc.

0
一旦你创建了树状图,使用cutree方法然后将其转换为数据框。以下代码使用dendextend库创建了一个漂亮的树状图:
library(dendextend)

# set the number of clusters
clust_k <- 8

# make the hierarchical clustering
par(mar = c(2.5, 0.5, 1.0, 7))
d <- dist(mat, method = "euclidean")
hc <- hclust(d)
dend <- d %>% hclust %>% as.dendrogram
labels_cex(dend) <- .65
dend %>% 
  color_branches(k=clust_k) %>%
  color_labels() %>%
  highlight_branches_lwd(3) %>% 
  plot(horiz=TRUE, main = "Branch (Distribution) Clusters by Heloc Attributes", axes = T)

enter image description here

根据着色方案,看起来聚类是围绕阈值4形成的。因此,要将分配转换为数据框,我们需要获取聚类,然后使用unlist()函数。

首先,您需要获取聚类本身,但它只是一个数字的单个向量,行名称是实际标签。

# creates a single item vector of the clusters    
myclusters <- cutree(dend, k=clust_k, h=4)

# make the dataframe of two columns cluster number and label
clusterDF <-  data.frame(Cluster = as.numeric(unlist(myclusters)),
                         Branch = names(myclusters))

# sort by cluster ascending
clusterDFSort <- clusterDF %>% arrange(Cluster)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接