Matriz dispersa a partir de un vector
Tengo un vector con valores (val) y un vector que indica la pertenencia a un grupo (group):
vec <- 1:9
group <- rep(1:3, c(2,4,3))
Digamos que tenemos K grupos y un total de N valores, por lo tanto, ambos vectores tienen una longitud N. El objetivo es construir eficientemente una matriz diagonal en bloques esparcida donde la primera columna contiene los valores del grupo 1, la segunda columna contiene los valores del grupo 2, y así sucesivamente. Sin embargo, los valores no deben “superponerse” en el sentido de que solo debe haber un valor por fila, vea la solución a continuación. Necesito hacer esto miles de veces con K y N muy grandes. Por lo tanto, la siguiente solución basada en bucles no es lo suficientemente eficiente:
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
Por razones de memoria, sería ideal construir directamente una matriz dispersa sin el paso intermedio basado en la matriz densa N por K.
EDITAR
Para el pequeño ejemplo dado anteriormente, resulta que la solución basada en bucles es bastante eficiente:
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
Sin embargo, al pasar a ejemplos de alta dimensión (N = 10,000 y K = 1,000), la solución de CJR es la ganadora en términos de velocidad:
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
davy.ai
Una forma de lograr esto es utilizar la función
sparseMatrix()
del paqueteMatrix
. La idea es crear una matriz dispersa una columna a la vez, donde cada columna corresponde a un grupo. Comenzamos obteniendo los números únicos de grupo y luego iteramos a través de ellos para construir cada columna de la matriz dispersa. Dentro de cada bucle, creamos un vector de valores no nulos (x
) y los índices de fila correspondientes (i
). Los índices de columna son simplemente el número de grupo actualk
. Finalmente, utilizamos estos vectores para construir una columna de matriz dispersa usandosparseMatrix(i, k, x, dims = c(N, K))
y los unimos todos usandocbind()
para obtener la matriz dispersa final en forma de bloque diagonal.Aquí está el código:
Ten en cuenta que esta solución debería ser más eficiente en términos de memoria que la basada en bucles, ya que solo creamos una columna de matriz dispersa a la vez y evitamos tener que almacenar la matriz densa completa de tamaño
N
porK
en la memoria.