Calculating the row wise cosine similarity using matrix multiplication in R

In the below example I have calculated the row wise cosine similarity for data in a matrix using a custom function and a for loop. The output that I would like is a symmetric matrix.

I would like to implement this calculation using matrix multiplication (linear algebra) without a for loop as the actual input matrix I need to work on is much larger and a loop will be too slow.

x = c(0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1)
x = matrix(x, nrow = 3, byrow = TRUE)

cosine_similarity = function(a, b){
  y = crossprod(a, b) / sqrt(crossprod(a) * crossprod(b))
  return(y)
}

N_row = dim(x)[1]

similarity_matrix = matrix(0, nrow = N_row, ncol = N_row)

for (i in 1:(N_row-1)) {
  for (j in (i + 1):N_row) {
    similarity_matrix[i,j] = cosine_similarity(x[i,], x[j,])
  }
}

similarity_matrix = similarity_matrix + t(similarity_matrix)

1 answer

  • answered 2020-10-24 22:11 akrun

    We could use outer to make this faster

    outer(seq_len(nrow(x)), seq_len(nrow(x)), 
        FUN = Vectorize(function(i, j) cosine_similarity(x[i,], x[j, ])))
    

    -output

    #          [,1]      [,2]      [,3]
    #[1,] 1.0000000 0.5000000 0.4082483
    #[2,] 0.5000000 1.0000000 0.4082483
    #[3,] 0.4082483 0.4082483 1.0000000
    

    or another option is combn

    out <- diag(nrow(x)) * 0
    out[upper.tri(out)] <-  combn(seq_len(nrow(x)), 2,
         FUN = function(i) c(cosine_similarity(x[i[1], ], x[i[2],])))
    out <- out + t(out)
    diag(out) <- 1