Pregunta Formas de mejorar el bucle para manipulaciones matriciales dependiendo de otra matriz.


Sé que la mejora de bucle se ha pedido toneladas de veces antes. Podemos aplicar funciones familiares para mejorar el bucle for en R.

Sin embargo, ¿hay una manera de mejorar las manipulaciones de una matriz donde esas manipulaciones dependen de otra matriz? Lo que quiero decir aquí es esto, donde los elementos que establecí en 2 en test se basan en otra matriz index:

for (i in 1:nrow(test)){
  test[i,index[i,]]  <- 2
}    # where index is predetermined matrix

Otro ejemplo es este, donde establezco los valores en test Basado en el orden de los elementos en las filas de otra matriz. anyMatrix:

for (i in 1:nrow(test)){
   test[i,] <- order(anyMatrix[i,])
}

Podría usar lapply o sapply aquí, pero devuelven una lista y lleva la misma cantidad de tiempo volver a convertirla en matriz.

Ejemplo reproducible:

test <- matrix(0, nrow = 10, ncol = 10)
set.seed(1234)
index <- matrix(sample.int(10, 10*10, TRUE), 10, 10)
anyMatrix <- matrix(rnorm(10*10), nrow = 10, ncol = 10)

for (i in 1:nrow(test)){
  test[i,index[i,]]  <- 2
}

for (i in 1:nrow(test)){
   test[i,] <- order(anyMatrix[i,])
}

5
2018-02-06 15:04


origen


Respuestas:


Realmente pareces tener dos problemas separados aquí.

Problema 1: Dada una matriz index, por cada fila i y columna j quieres establecer test[i,j] a 2 si j aparece en fila i de index. Esto se puede hacer con un índice de matriz simple, pasando una matriz de índices de 2 columnas donde la primera columna son las filas de todos los elementos que desea indexar y la segunda columna son las columnas de todos los elementos que desea indexar:

test[cbind(as.vector(row(index)), as.vector(index))] <- 2
test
#       [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
#  [1,]    2    2    0    2    2    2    2    0    2     2
#  [2,]    2    0    2    2    2    2    2    0    2     2
#  [3,]    2    2    2    2    0    0    2    2    0     0
#  [4,]    2    2    0    0    0    2    2    2    0     2
#  [5,]    2    2    2    2    0    0    0    0    2     0
#  [6,]    0    0    0    0    0    2    2    2    2     0
#  [7,]    2    0    2    2    2    2    2    0    0     0
#  [8,]    2    0    2    2    2    2    0    2    0     2
#  [9,]    2    2    2    2    0    0    2    0    2     2
# [10,]    2    0    2    0    0    2    2    2    2     0

Como esto hace todas las operaciones en una sola operación vectorizada, debería ser más rápido que recorrer las filas y manejarlas individualmente. Aquí hay un ejemplo con 1 millón de filas y 10 columnas:

OP <- function(test, index) {
  for (i in 1:nrow(test)){
    test[i,index[i,]]  <- 2
  }
  test
}
josliber <- function(test, index) {
  test[cbind(as.vector(row(index)), as.vector(index))] <- 2
  test
}
test.big <- matrix(0, nrow = 1000000, ncol = 10)
set.seed(1234)
index.big <- matrix(sample.int(10, 1000000*10, TRUE), 1000000, 10)
identical(OP(test.big, index.big), josliber(test.big, index.big))
# [1] TRUE
system.time(OP(test.big, index.big))
#    user  system elapsed 
#   1.564   0.014   1.591 
system.time(josliber(test.big, index.big))
#    user  system elapsed 
#   0.408   0.034   0.444 

Aquí, el enfoque vectorizado es 3.5x más rápido.

Problema 2: Quieres establecer fila i de test a order aplicado a la fila correspondiente de anyMatrix. Puedes hacer esto con apply:

(test <- t(apply(anyMatrix, 1, order)))
#       [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
#  [1,]    1   10    7    8    4    5    3    6    2     9
#  [2,]    8    7    1    6    3    4    9    5   10     2
#  [3,]    4    9    7    1    3    2    6   10    5     8
#  [4,]    1    2    6    4   10    3    9    8    7     5
#  [5,]    9    6    5    1    2    7   10    4    8     3
#  [6,]    9    3    8    6    5   10    1    4    7     2
#  [7,]    3    7    2    5    6    8    9    4    1    10
#  [8,]    9    8    1    3    4    6    7   10    5     2
#  [9,]    8    4    3    6   10    7    9    5    2     1
# [10,]    4    1    9    3    6    7    8    2   10     5

No esperaría mucho cambio en el tiempo de ejecución aquí, porque apply En realidad, solo está haciendo un bucle a través de las filas de manera similar a como estaba haciendo un bucle en su solución. Aún así, preferiría esta solución porque es mucho menos que escribir y la forma más "R" de hacer las cosas.

Tenga en cuenta que ambas aplicaciones utilizan un código bastante diferente, lo cual es bastante típico en la manipulación de datos R: hay muchos operadores especializados diferentes y debe elegir el que sea adecuado para su tarea. No creo que haya una sola función, ni siquiera un pequeño conjunto de funciones que puedan manejar todas las manipulaciones de la matriz cuando esa manipulación se basa en datos de otra matriz.


6
2018-02-06 15:32