rmatrix-multiplicationcross-product

Vectorising a cross product of vector and matrix with consecutive indices in R


Some context: I have a vector e of dimensions n×1, and a correlation matrix R of n×n. I am trying to efficiently calculate the quantity

average weighted correlation

in R, without looping. So far, I have been able to do it with a single loop, using the following code:

nom <- 0; denom <- 0
for(i in 1:n){
  nom <- nom + e[i]*(R[i,-(1:i)]%*%e[-(1:i)])
  denom <- denom + e[i]*sum(e[-(1:i)])
}
beta <- nom/denom

This works, and in practice my calculations should not take long because for the task at hand, n will be at most of size 11 or 12 (so an improvement will likely not make a huge difference in measured time performance). However, I am curious as to how this can be done more efficiently, since

a) R is symmetrical and the calculations only need to use the part above (or below) the main diagonal of R, and

b) I am planning to use this in some MC simulations of a large size, so any calculation time I can shave off might make a difference for the big picture.


For replication/calculation purposes, here is an example of possible numerical values:

e <- c(0.4972,0.0902,0.02822,0.1688,0.0149,0.0028,0.01411,0.02733,0.0151,0.0391,0.01301,0.0894)
R <- matrix(data = c(1,0.9,0.4,0.75,0.5,0.3,0.4,0.4,0.25,0.25,0.5,0.4,0.9,1,0.5,0.9,0.5,0.3,0.4,0.35,0.2,0.2,0.5,0.3,0.4,0.5,1,0.3,0.5,0.4,0.25,0.2,0.2,0.2,0.3,0.3,0.75,0.9,0.3,1,0.3,0.3,0.4,0.25,0.25,0.2,0.3,0.75,0.5,0.5,0.5,0.3,1,0.5,0.35,0.8,0.8,0.3,0.7,0.45,0.3,0.3,0.4,0.3,0.5,1,0.3,0.4,0.3,0.2,0.45,0.35,0.4,0.4,0.25,0.4,0.35,0.3,1,0.3,0.3,0.2,0.5,0.5,0.4,0.35,0.2,0.25,0.8,0.4,0.3,1,0.8,0.2,0.6,0.8,0.25,0.2,0.2,0.25,0.8,0.3,0.3,0.8,1,0.3,0.7,0.8,0.25,0.2,0.2,0.2,0.3,0.2,0.2,0.2,0.3,1,0.25,0.3,0.5,0.5,0.3,0.3,0.7,0.45,0.5,0.6,0.7,0.25,1,0.7,0.4,0.3,0.3,0.75,0.45,0.35,0.5,0.8,0.8,0.3,0.7,1), nrow = 12, ncol = 12)

Solution

  • You can do:

    ee <- tcrossprod(e)
    ee0 <- ee; diag(ee0) <- 0
    
    sum(R*ee0)/sum(ee0)
    

    For your example data:

    > sum(R*ee0)/sum(ee0)
    [1] 0.5647038
    

    A little save of calculation: sum(ee0) is equal to

    sum(e)^2 - sum(e^2)
    

    Here is another variant:

    d <- diag(R)
    diag(R) <- 0
    sum(e*crossprod(e, R))/(sum(e)^2 - sum(e^2))