25
$\begingroup$

I would like to make heatmaps based upon cosine dissimilarity.

I'm using R and have explored several packages, but cannot find a function to generate a standard cosine dissimilarity matrix. The built in dist() function doesn't support cosine distances, also within the package arules there is a dissimilarity() function, but it only works on binary data.

Can anybody recommend a package? Or demonstrated how to calculate cosine dissimilarity within R?

$\endgroup$
3
  • 5
    $\begingroup$ It may be faster to write your own cosine dissimilarity function. $\endgroup$ Jul 3 '12 at 12:45
  • 2
    $\begingroup$ Cosine is similarity, not dissimilarity. You can, however, turn cosine into euclidean distance of scaled data: d=sqrt(2*(1-cos)). $\endgroup$
    – ttnphns
    Jul 3 '12 at 14:51
  • $\begingroup$ Same question over on SO: Find cosine similarity between two arrays $\endgroup$
    – smci
    Mar 30 '17 at 21:03
34
$\begingroup$

As @Max indicated in the comments (+1) it would be simpler to "write your own" than to spend time looking for it somewhere else. As we know, the cosine similarity between two vectors $A,B$ of length $n$ is

$$ C = \frac{ \sum \limits_{i=1}^{n}A_{i} B_{i} }{ \sqrt{\sum \limits_{i=1}^{n} A_{i}^2} \cdot \sqrt{\sum \limits_{i=1}^{n} B_{i}^2} } $$

which is straightforward to generate in R. Let X be the matrix where the rows are the values we want to compute the similarity between. Then we can compute the similarity matrix with the following R code:

cos.sim <- function(ix) 
{
    A = X[ix[1],]
    B = X[ix[2],]
    return( sum(A*B)/sqrt(sum(A^2)*sum(B^2)) )
}   
n <- nrow(X) 
cmb <- expand.grid(i=1:n, j=1:n) 
C <- matrix(apply(cmb,1,cos.sim),n,n)

Then the matrix C is the cosine similarity matrix and you can pass it to whatever heatmap function you like (the only one I'm familiar with is image()).

$\endgroup$
5
  • $\begingroup$ Thanks, this is helpful. Actually, I don't want to plot the matrix itself but rather have a distance function for clustering of another heatmap that I have. $\endgroup$ Jul 5 '12 at 12:01
  • $\begingroup$ @GregSlodkowicz, OK well perhaps you can pass this matrix to the function you're using. Also, if you've found this answer helpful please consider an upvote (or accepting the answer if you consider it definitive) :) $\endgroup$
    – Macro
    Jul 5 '12 at 12:26
  • $\begingroup$ Great, thanks to your reply and ttnphns's comment I was able to do what I want. Now I would like to have a different metric when clustering rows than when clustering columns but maybe that's pushing it... $\endgroup$ Jul 7 '12 at 10:20
  • $\begingroup$ Apparently I don't have enough points to be able to comment. I just wanted to offer a slightly modified version of Macro's nice answer. Here it is. # ChirazB's version of cos.sim() by Macro # where S = X %*% t(X) cos.sim.2 <- function(S,ix) { i <- ix[1] j <- ix[2] return( S[i,j]/sqrt(S[i,i]*S[j,j]) ) } #test X <- matrix(rnorm(20),nrow=5,ncol=4) S <- X%*%t(X) n <- nrow(X) idx.arr <- expand.grid(i=1:n, j=1:n) C <- matrix(apply(idx.arr,1,cos.sim,X),n,n) C2 <- matrix(apply(idx.arr,1,cos.sim.2,S),n,n) I don't like global variable, that's why I included S as a parameter. $\endgroup$ Jan 11 '15 at 17:23
  • $\begingroup$ it should be sqrt(sum(A^2))*sqrt(sum(B^2)) instead of sqrt(sum(A^2)*sum(B^2)) $\endgroup$ Aug 25 at 9:02
22
$\begingroup$

Many answers here are computationally inefficient, try this;


For cosine similarity matrix

Matrix <- as.matrix(DF)
sim <- Matrix / sqrt(rowSums(Matrix * Matrix))
sim <- sim %*% t(sim)

Convert to cosine dissimilarity matrix (distance matrix).

D_sim <- as.dist(1 - sim)
$\endgroup$
3
  • 4
    $\begingroup$ Awesome answer. At least 250x as fast as library(lsa) cosine() when using OpenBLAS and 96 cores. $\endgroup$
    – webb
    Nov 13 '20 at 11:27
  • 3
    $\begingroup$ I'm glad you appreciate good computational mathematics. $\endgroup$
    – Brad
    Jan 28 at 12:41
  • 2
    $\begingroup$ Way faster than other functions. Thank you Brad. $\endgroup$
    – patL
    May 4 at 17:09
8
$\begingroup$

You can use the cosine function from the lsa package:
http://cran.r-project.org/web/packages/lsa

$\endgroup$
3
  • 3
    $\begingroup$ Brad's answer is at least 250x as fast as library(lsa) cosine() when using OpenBLAS and 96 cores. $\endgroup$
    – webb
    Nov 13 '20 at 11:27
  • $\begingroup$ @Downvoter: What is wrong with my answer??? $\endgroup$
    – vonjd
    Aug 26 at 8:40
  • 1
    $\begingroup$ I did not downvote your answer, so I don't know why it was downvoted. $\endgroup$
    – webb
    Aug 26 at 12:49
6
$\begingroup$

The following function might be useful when working with matrices, instead of 1-d vectors:

# input: row matrices 'ma' and 'mb' (with compatible dimensions)
# output: cosine similarity matrix

cos.sim=function(ma, mb){
  mat=tcrossprod(ma, mb)
  t1=sqrt(apply(ma, 1, crossprod))
  t2=sqrt(apply(mb, 1, crossprod))
  mat / outer(t1,t2)
}
$\endgroup$
1
$\begingroup$

Ramping up some of the previous code (from @Macro) on this issue, we can wrap this into a cleaner version in the following:

df <- data.frame(t(data.frame(c1=rnorm(100),
                              c2=rnorm(100),
                              c3=rnorm(100),
                              c4=rnorm(100),
                              c5=rnorm(100),
                              c6=rnorm(100))))

#df[df > 0] <- 1
#df[df <= 0] <- 0



apply_cosine_similarity <- function(df){
  cos.sim <- function(df, ix) 
  {
    A = df[ix[1],]
    B = df[ix[2],]
    return( sum(A*B)/sqrt(sum(A^2)*sum(B^2)) )
  }   
  n <- nrow(df) 
  cmb <- expand.grid(i=1:n, j=1:n) 
  C <- matrix(apply(cmb,1,function(cmb){ cos.sim(df, cmb) }),n,n)
  C
}
apply_cosine_similarity(df)

Hope this helps!

$\endgroup$
1
  • $\begingroup$ it should be sqrt(sum(A^2))*sqrt(sum(B^2)) instead of sqrt(sum(A^2)*sum(B^2)) $\endgroup$ Aug 25 at 9:06

Not the answer you're looking for? Browse other questions tagged or ask your own question.