Using For Loop to Hierarchical Cluster Different Linkages

Viewed 85

I am trying to perform multiple hierarchical clusters on various distance matrices and using different linkages for comparison.

In order to simplify the task I attempted in making a function that would iterate over a group of linkage methods to generate the agnes object but to no avail.

This is an example of what the function should be doing:

library(cluster)
Links <- c("single","average","ward") # vector of linkage methods
ClustH <- list() # list to hold HClusts
set.seed(123)
A <- rnorm(3, mean = 5, sd = 1)
B <- rnorm(3, mean = 2, sd = 1)
C <- rnorm(3, mean = 0, sd = 1)
DMat <- as.dist(cbind(A,B,C)) # distance matrix
for(i in 1:length(Links)){
 ClustH[i] <- agnes(DMat, method = Links[i])
}
ClustH[1]
#[[1]]
#[1] 1 2 3

Instead of saving the entire agnes object in each element of the list, it instead saves just the first element of the object (i.e. order) which is obtained through <agnes object>$order

What am I doing wrong? Can you not save listed objects within lists? Any recommendations?

1 Answers

Answer

Modify the loop to use [[ instead of [:

ClustH[[i]] <- agnes(DMat, method = Links[i])

Rationale

Lists have specific behaviors when subsetting using [ and [[. With [, we actually take a subset of the list, of a given length. The output will always be a list, just a subset of it. With [[, we can directly access one element of a list. As an example:

m <- list(1, 2, 3)
m[1]
#[[1]]
#[1] 1

m[[1]]
# [1] 1

Since the output of agnes is actually a list too, it is trying to overwrite the elements of ClustH with the elements of the agnes output. Since i has a length of 1, it will only overwrite one element with the first element of the output of agnes:

List of 8
 $ order    : int [1:3] 1 2 3
 $ height   : num [1:2] 4.77 3.72
 $ ac       : num 0.147
 $ merge    : int [1:2, 1:2] -2 -1 -3 1
 $ diss     : 'dissimilarity' num [1:3] 4.77 6.56 3.72
  ..- attr(*, "Labels")= chr [1:3] "A" "B" "C"
  ..- attr(*, "Size")= num 3
  ..- attr(*, "call")= language as.dist.default(m = cbind(A, B, C))
  ..- attr(*, "Diag")= logi FALSE
  ..- attr(*, "Upper")= logi FALSE
  ..- attr(*, "Metric")= chr "unspecified"
 $ call     : language agnes(x = DMat, method = Links[1])
 $ method   : chr "single"
 $ order.lab: chr [1:3] "A" "B" "C"
 - attr(*, "class")= chr [1:2] "agnes" "twins"

In other words, the integer vector 1 2 3.

Suggestion

If you want to have a list as your output, you can also consider switching to lapply:

ClustH <- lapply(Links, function(x) agnes(DMat, methods = x))

# From R version 4.1 onwards, this also works
ClustH <- lapply(Links, \(x) agnes(DMat, methods = x))
Related