es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

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
Tags:  , ,

Answer

  1. Avatar for davy.ai

    Una forma de lograr esto es utilizar la función sparseMatrix() del paquete Matrix. 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 actual k. Finalmente, utilizamos estos vectores para construir una columna de matriz dispersa usando sparseMatrix(i, k, x, dims = c(N, K)) y los unimos todos usando cbind() para obtener la matriz dispersa final en forma de bloque diagonal.

    Aquí está el código:

    library(Matrix)
    
    # Datos de ejemplo
    vec <- 1:9
    group <- rep(1:3, c(2,4,3))
    
    # Obtener números de grupo únicos y número de filas/columnas
    K <- length(unique(group))
    N <- length(group)
    
    # Iterar a través de los grupos para construir columnas de matriz dispersa
    mat_list <- lapply(unique(group), function(k) {
    
      # Obtener valores no nulos e índices de fila para el grupo actual
      x <- vec[group == k]
      i <- which(group == k)
    
      # Construir columna de matriz dispersa
      sparseMatrix(i, k, x, dims = c(N, K))
    
    })
    
    # Combinar columnas de matriz dispersa para obtener la matriz dispersa final en forma de bloque diagonal
    M <- do.call("cbind", mat_list)
    
    M
    # Matriz dispersa de 9 x 3 de clase "dgCMatrix"
    # 
    # [1,] 1 . .
    # [2,] 2 . .
    # [3,] . 3 .
    # [4,] . 4 .
    # [5,] . 5 .
    # [6,] . 6 .
    # [7,] . . 7
    # [8,] . . 8
    # [9,] . . 9
    

    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 por K en la memoria.

Comments are closed.