I have a vector with values (val) and a vector indicating group membership (group):
vec <- 1:9
group <- rep(1:3, c(2,4,3))
Let's say we have K groups and a total of N values, hence both vectors have length N. The goal is to efficiently construct a sparse 'block-diagonal' matrix where the first column holds the values of group 1, the second column holds the values of group 2, and so on. However, the values should not 'overlap' in the sense that there should be only one value per row, see the solution below. I need to do this thousands of times with K and N very large. Hence, the following, loop-based solution is not efficient enough:
K <- length(unique(group))
N <- length(group)
M <- matrix(0, N, K)
for(k in 1:K){
M[group == k, k] <- vec[group == k]
}
Matrix::Matrix(M, sparse = T)
9 x 3 sparse Matrix of class "dgCMatrix"
[1,] 1 . .
[2,] 2 . .
[3,] . 3 .
[4,] . 4 .
[5,] . 5 .
[6,] . 6 .
[7,] . . 7
[8,] . . 8
[9,] . . 9
For memory reasons, it would be ideal to directly construct a sparse matrix without the intermediate step based on the dense N times K matrix.
EDIT
For the small example given above, it turns out that the loop based solution is quite efficient:
Unit: microseconds
expr min lq mean median uq max neval cld
ben 734.280 771.7000 826.8372 787.5230 805.2710 3185.158 100 b
CJR 711.187 745.1855 813.9948 766.9960 781.7495 4832.476 100 b
original 199.714 221.9520 235.4320 227.9395 236.7065 379.757 100 a
However, when turning to high-dimensional examples (N = 10,000 and K = 1,000), the solution by CJR is the winner speed-wise:
Unit: milliseconds
expr min lq mean median uq max neval cld
ben 128.529311 133.308972 140.032070 135.921289 139.272589 289.668852 100 b
CJR 1.841474 2.055513 2.261732 2.201557 2.395925 6.330544 100 a
original 93.387806 118.348398 171.380301 125.884493 244.421699 365.871433 100 c