18
$\begingroup$

I would like to make a heatmap with row clustering based on cosine distances. I'm using R and heatmap.2() for making the figure. I can see that there's a dist parameter in heatmap.2 but I cannot find a function to generate the cosine dissimilarity matrix. The builtin dist function doesn't support cosine distances, I also found a package called arules with a dissimilarity() function but it only works on binary data.

$\endgroup$

closed as off-topic by mkt, kjetil b halvorsen, mdewey, Michael Chernick, Sycorax Sep 17 '18 at 19:17

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "This question appears to be off-topic because EITHER it is not about statistics, machine learning, data analysis, data mining, or data visualization, OR it focuses on programming, debugging, or performing routine operations within a statistical computing platform. If the latter, you could try the support links we maintain." – mkt, kjetil b halvorsen, mdewey, Michael Chernick, Sycorax
If this question can be reworded to fit the rules in the help center, please edit the question.

  • 5
    $\begingroup$ It may be faster to write your own cosine dissimilarity function. $\endgroup$ – Max 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
28
$\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$
  • $\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$ – Greg Slodkowicz 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$ – Greg Slodkowicz 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$ – Chiraz BenAbdelkader Jan 11 '15 at 17:23
6
$\begingroup$

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

$\endgroup$
4
$\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$
2
$\begingroup$

Some answers above 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$
0
$\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$

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