5
$\begingroup$

According to some articles (e.g. here) correlation is just a centered version of cosine similarity. I use the following code to calculate the cosine similarity matrix of the column vectors of a matrix X (slightly modified from here):

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

My question
Which modifications of the code above are needed to get the correlation matrix cor(X) instead of the cosine similarity matrix. I guess the changes are minimal but I can't see them at the moment.

$\endgroup$
1
  • 6
    $\begingroup$ I can't help with R because I'm not its user. But correlation is cosine for centered data. So, if you need cosine between data columns, do as if you are computing $r$, only do not center columns (or, equivalently, do not compute sum-of-squares of deviations, compute just sum-of-squares). $\endgroup$
    – ttnphns
    May 9, 2014 at 13:26

2 Answers 2

9
$\begingroup$

The answer is really right there in your linked articles. From the first, here are the formulae for cosine and correlation (lightly edited for brevity and clarity):

\begin{align} {\rm CosSim}(x,y) &= \frac{\sum_i x_i y_i}{ \sqrt{ \sum_i x_i^2} \sqrt{ \sum_i y_i^2 } } \\ \ \\ \ \\ {\rm Corr}(x,y) &= \frac{ \sum_i (x_i-\bar{x}) (y_i-\bar{y}) }{ \sqrt{\sum (x_i-\bar{x})^2} \sqrt{ \sum (y_i-\bar{y})^2 } } \\ \ \\ {\rm Corr}(x,y) &= {\rm CosSim}(x-\bar{x},\ y-\bar{y}) \end{align}

So the simplest adaptation is just to subtract the means from your input vectors:

library(MASS)  # we need this package to generate correlated data below
set.seed(2641) # this makes the example exactly reproducible
  # now I generate correlated data:
X <- mvrnorm(1000, mu=c(100, 150), Sigma=rbind(c(30, 17),
                                               c(17, 50) ) )
  # I adapted the function somewhat, as the original was keyed to its context
cos.sim <- function(X, corr=FALSE){
  if(corr){ 
    A = X[,1] - mean(X[,1])
    B = X[,2] - mean(X[,2])
  } else {
    A = X[,1]
    B = X[,2]
  }
  return( t(A)%*%B / sqrt(sum(A^2)*sum(B^2)) )
} 
cos.sim(X)
#           [,1]
# [1,] 0.9985756
cos.sim(X, corr=TRUE)
#           [,1]
# [1,] 0.4604822
cor(X)
#           [,1]      [,2]
# [1,] 1.0000000 0.4604822
# [2,] 0.4604822 1.0000000

Here is a matrix version:

set.seed(6616)
X3 <- mvrnorm(1000, mu=c(100, 150, 175), Sigma=rbind(c(30, 17, 12),
                                                     c(17, 50, 29),
                                                     c(12, 29, 46) ))
cos.sim.mat <- function(X, corr=FALSE){
  if(corr){ X = apply(X, 2, function(x){ x-mean(x) }) }
  denom = solve(diag(sqrt(diag(t(X)%*%X))))
  return( denom%*%(t(X)%*%X)%*%denom )
} 
cos.sim.mat(X3)
#           [,1]      [,2]      [,3]
# [1,] 1.0000000 0.9984552 0.9983700
# [2,] 0.9984552 1.0000000 0.9992154
# [3,] 0.9983700 0.9992154 1.0000000
cos.sim.mat(X3, corr=TRUE)
#           [,1]      [,2]      [,3]
# [1,] 1.0000000 0.3990872 0.2584569
# [2,] 0.3990872 1.0000000 0.5900067
# [3,] 0.2584569 0.5900067 1.0000000
cor(X3)
#           [,1]      [,2]      [,3]
# [1,] 1.0000000 0.3990872 0.2584569
# [2,] 0.3990872 1.0000000 0.5900067
# [3,] 0.2584569 0.5900067 1.0000000
$\endgroup$
5
  • $\begingroup$ Thank you, yet I think the code doesn't work for matrices bigger than 2x2. Would you be so kind to check the code and perhaps modify it accordingly? Thank you again :-) $\endgroup$
    – vonjd
    May 10, 2014 at 15:49
  • 1
    $\begingroup$ If your question is just asking for code, it would be off-topic on CV (see our help center). If you want to know how to get a correlation matrix, you should use the existing function cor(). The code I wrote above is to illustrate, in a transparent way, the difference between correlation and cosine. Nonetheless, I have added code that will output a matrix, but it is no longer as clear what the code is doing & how the correlation & cosine versions differ / are calculated. $\endgroup$ May 11, 2014 at 19:17
  • $\begingroup$ Thank you, I accepted your answer. My question was indeed to make the connection clear via some simple modifications on the code, not the code itself (I know that this would be off-topic). Anyway: What seems interesting to me is that the matrix version of your code (which works ok!) seems to have very little resemblance with the original code. You e.g. used this rather convoluted array of functions: solve(diag(sqrt(diag(t(X)%*%X))))%*%(t(X)%*%X)%*%solve(diag(sqrt(diag(t(X)%*%X)))) - I would have hoped that the modification to my code would be more easily comprehensible. Thank you again. $\endgroup$
    – vonjd
    May 12, 2014 at 6:37
  • $\begingroup$ ...I just tried to add X = apply(X, 2, function(x){ x-mean(x) }) at the beginning of my code - and it did the trick (also with matrices)! Could this be all? $\endgroup$
    – vonjd
    May 12, 2014 at 6:47
  • $\begingroup$ I added this as an answer for future reference. Thank you for your help. $\endgroup$
    – vonjd
    May 12, 2014 at 6:55
0
$\begingroup$

The only line that has to be added is X <- apply(X, 2, function(x){ x-mean(x) }) which just subtracts the mean column-wise:

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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