【问题标题】:Pedagogical way of programming hierarchical clustering algorithm in R在 R 中编程层次聚类算法的教学方法
【发布时间】:2015-11-22 20:33:24
【问题描述】:

我正在准备一场关于 R 机器学习的讲座,我想以 hierarchical clustering 为例。我在这里找到了这个很有启发性的页面:http://home.deib.polimi.it/matteucc/Clustering/tutorial_html/hierarchical.html

它从以下距离表开始(在读取数据时请注意NA 作为列/行名称,另见下文):

MI和TO之间的最短距离是138,所以我们想将这些列和行合并到一个新的列/行MI/TO这个新的复合对象MI/TO到所有的距离剩余城市等于原始城市之一的最短距离MI或TO,例如MI/TO 到 RM 是 564(来自 MI),因为它比 669(来自 TO)短。 (这种聚合方式称为single-linkage clustering)。所以我们有一个新表:

我的问题
我开始用 R 编写代码,很快发现代码变得越来越混乱 - 远非初出茅庐的程序员可以轻松理解的东西。您是否知道一种方法或程序包可以让您以自然直观的方式进行这种数据操作?


所以这是 R 中的起始表:

D <- matrix(c(0,662,877,255,412,996,
              662,0,295,468,268,400,
              877,295,0,754,564,138,
              255,468,754,0,219,869,
              412,268,564,219,0,669,
              996,400,138,869,669,0), ncol=6, byrow=T)

rownames(D) <- colnames(D) <- c("BA","FI","MI","Na","RM","TO")

D
##     BA  FI  MI  Na  RM  TO
## BA   0 662 877 255 412 996
## FI 662   0 295 468 268 400
## MI 877 295   0 754 564 138
## Na 255 468 754   0 219 869
## RM 412 268 564 219   0 669
## TO 996 400 138 869 669   0

【问题讨论】:

    标签: r algorithm data-manipulation hierarchical-clustering


    【解决方案1】:

    内置函数“hclust”已经是一个很好的函数了。

    hc1 = hclust(as.dist(D), method = "single")
    hc1$merge
    plot(hc1)
    

    如果您需要澄清,我可以详细描述。

    按照hclust的逻辑,可以试试:

    savemat = list()
    D1 = D; diag(D1) = Inf # a trick to make zero a infinity
    m = 1
    while(dim(D1)[1] > 2) {
        # get the location of minimum distance
        minloc = which(D1 == min(D1), arr.ind = T)[1,]
        # make a two-column matrix then find out the minimum value of each row
        u = apply(cbind(D1[minloc[2],],D1[minloc[1],]),1,min)
        # updating the matrix
        D1[minloc[2],] = u 
        D1[,minloc[2]] = u
        u = paste0(rownames(D1)[minloc[2]],'/',rownames(D1)[minloc[1]])
        rownames(D1)[minloc[2]] = u
        colnames(D1)[minloc[2]] = u
        # deleting the merged column/row
        D1 = D1[-minloc[1],-minloc[1]]
        diag(D1) = Inf
        # save the steps into a list element mth
        savemat[[m]] = D1
        m = m + 1
    }
    savemat
    

    【讨论】:

    • 我知道这个函数,但我的问题是从头开始编写这个例子!
    • +1:这令人印象深刻,我将详细分析您的代码。我不想问太多,但是当一些距离出现不止一次时,有没有一种简单的方法来考虑这种情况?这将导致同时添加多个城市的新集群。另一件事是:您将如何打印生成的层次结构(基本上是树状图)?无论如何:非常感谢你:-)
    【解决方案2】:

    将代码更新为递归函数和单独的打印函数,以便更好地跟踪正在发生的事情。与hcl(&lt;data.frame&gt;,&lt;log_level&gt;) 一起使用。日志级别可以为 0 仅用于最终结果,1 用于打印中间数据集,2 用于打印每个步骤

    # To be allowed to add column later, don't know a better way than coercing to data.frame
    d <- data.frame(D,stringsAsFactors=F) 
    
    myprt <- function(message,var) {
      print(message)
      print(var)
    }
    
    hcl <- function(d,prt=0) {
      if (prt) myprt("Starting dataset:",d)
    
      # 1) Get the shortest distance informations:
      Ref <- which( d==min(d[d>0]), useNames=T, arr.ind=T ) 
      if (prt>1) myprt("Ref is:",Ref)
      # 2) Subset the original entry to remove thoose towns:
      res <- d[-Ref[,1],-Ref[,1]]
      if (prt>1) myprt("Res is:", res)
    
      # 3) Get the subset for the two nearest towns:
      tmp <- d[-Ref[,1],Ref[,1]]
      if (prt>1) myprt("Tmp is:",tmp)
    
      # 4) Get the vector of minimal distance from original dataset with the two town (row by row on t)
      dists <- apply( tmp, 1, function(x) { x[x==min(x)] } )
      #dists <- tmp[ tmp == pmin( tmp[,1], tmp[,2] ) ]
      if (prt>1) myprt("Dists is:",dists)
    
      # 5) Let's build the resulting matrix:
      tnames <- paste(rownames(Ref),collapse="/") # Get the names of town to the new name
      if (length(res) == 1) {
    
        # Nothing left in the original dataset just concat the names and return
        tnames <- paste(c(tnames,names(dists)),collapse="/")
        Finalres <- data.frame(tnames = dists) # build the df
        names(Finalres) <- rownames(Finalres) <- tnames # Name it
    
        if (prt>0) myprt("Final result:",Finalres)
        return(Finalres) # Last iteration
    
      } else {
    
        Finalres <- res
        Finalres[tnames,tnames] <- 0 # Set the diagonal to 0
        Finalres[is.na(Finalres)] <- dists # the previous assignment has set NAs, replae them by the dists values
    
        if (prt>0) myprt("Dataset before recursive call:",Finalres)
        return(hcl(Finalres,prt)) # we're not at end, recall ourselves with actual result
    
      }
    }
    

    另一个想法的步骤:

    # To be allowed to add column later, don't know a better way than coercing to data.frame
    d <- data.frame(D,stringsAsFactors=F) 
    
    # 1) Get the shortest distance informations:
    Ref <- which( d==min(d[d>0]), useNames=T, arr.ind=T ) 
    
    # 2) Subset the original entry to remove thoose towns:
    res <-d[-Ref[,1],-Ref[,1]]
    
    # 3) Get the subset for the two nearest towns:
    tmp <- d[-Ref[,1],Ref[,1]]
    
    # 4) Get the vector of minimal distance from original dataset with the two town (row by row on tpm), didn't find a proper way to avoid apply
    dists <- apply( tmp, 1, function(x) { x[x==min(x)] } )
    
    dists <- dists <- tmp[ tmp == pmin( tmp[,1], tmp[,2] ) ]
    
    # 5) Let's build the resulting matrix:
    tnames <- paste(rownames(Ref),collapse="/") # Get the names of town to the new name
    Finalres <- res
    Finalres[tnames,tnames] <- 0 # Set the diagonal to 0
    Finalres[is.na(Finalres)] <- dists # the previous assignment has set NAs, replae them by the dists values
    

    输出:

    > Finalres
           BA  FI  Na  RM TO/MI
    BA      0 662 255 412   877
    FI    662   0 468 268   295
    Na    255 468   0 219   754
    RM    412 268 219   0   564
    TO/MI 877 295 754 564     0
    

    以及每一步的输出:

    > #Steps:
    > 
    > Ref
       row col
    TO   6   3
    MI   3   6
    > res
        BA  FI  Na  RM
    BA   0 662 255 412
    FI 662   0 468 268
    Na 255 468   0 219
    RM 412 268 219   0
    > tmp
        TO  MI
    BA 996 877
    FI 400 295
    Na 869 754
    RM 669 564
    > dists
    [1] 877 295 754 564
    

    这里有很多对象复制可以避免以节省性能,我使它有一个更好的逐步视图。

    【讨论】:

    • 赞成:谢谢,这也令人印象深刻。我会详细看看你的代码:-)
    猜你喜欢
    • 2020-10-22
    • 2020-07-20
    • 2020-08-27
    • 2021-07-21
    • 2015-08-12
    • 2015-12-14
    • 1970-01-01
    • 2021-08-28
    • 2011-12-01
    相关资源
    最近更新 更多