una - Eficientemente replicar matrices en R
r vector to matrix (5)
Tengo una matriz y busco una manera eficiente de replicarla n veces (donde n es el número de observaciones en el conjunto de datos). Por ejemplo, si tengo una matriz A
A <- matrix(1:15, nrow=3)
entonces quiero un resultado de la forma
rbind(A, A, A, ...) #n times
.
Obviamente, hay muchas maneras de construir una matriz tan grande, por ejemplo, utilizando un bucle for
o apply
o funciones similares. Sin embargo, la llamada a la "función de replicación matricial" tiene lugar en el núcleo mismo de mi algoritmo de optimización, donde se llama decenas de miles de veces durante una ejecución de mi programa. Por lo tanto, los bucles, el tipo de aplicación de funciones y cualquier cosa similar no son lo suficientemente eficientes. (Dicha solución básicamente significaría que un ciclo sobre n se realiza decenas de miles de veces, lo cual es obviamente ineficiente.) Ya traté de usar la función rep
común, pero no he encontrado una forma de organizar la salida de rep
en una matriz del formato deseado.
La solución do.call("rbind", replicate(n, A, simplify=F))
también es demasiado ineficiente porque rbind
se usa con demasiada frecuencia en este caso. (Entonces, aproximadamente el 30% del tiempo de ejecución total de mi programa se gasta en realizar los rbinds).
¿Alguien sabe una mejor solución?
¿Qué hay de transformarlo en una matriz, replicar el contenido y crear una nueva matriz con el número actualizado de filas?
A <- matrix(...)
n = 2 # just a test
a = as.integer(A)
multi.a = rep(a,n)
multi.A = matrix(multi.a,nrow=nrow(A)*n,byrow=T)
Dos soluciones más:
El primero es una modificación del ejemplo en la pregunta
do.call("rbind", rep(list(A), n))
El segundo implica desenrollar la matriz, replicarla y volver a ensamblarla.
matrix(rep(t(A),n), ncol=ncol(A), byrow=TRUE)
Dado que la eficiencia es lo que se solicitó, la evaluación comparativa es necesaria
library("rbenchmark")
A <- matrix(1:15, nrow=3)
n <- 10
benchmark(rbind(A, A, A, A, A, A, A, A, A, A),
do.call("rbind", replicate(n, A, simplify=FALSE)),
do.call("rbind", rep(list(A), n)),
apply(A, 2, rep, n),
matrix(rep(t(A),n), ncol=ncol(A), byrow=TRUE),
order="relative", replications=100000)
lo que da:
test replications elapsed
1 rbind(A, A, A, A, A, A, A, A, A, A) 100000 0.91
3 do.call("rbind", rep(list(A), n)) 100000 1.42
5 matrix(rep(t(A), n), ncol = ncol(A), byrow = TRUE) 100000 2.20
2 do.call("rbind", replicate(n, A, simplify = FALSE)) 100000 3.03
4 apply(A, 2, rep, n) 100000 7.75
relative user.self sys.self user.child sys.child
1 1.000 0.91 0 NA NA
3 1.560 1.42 0 NA NA
5 2.418 2.19 0 NA NA
2 3.330 3.03 0 NA NA
4 8.516 7.73 0 NA NA
Por lo tanto, el más rápido es la llamada de rbind
, pero eso supone que n
es fijo y conocido de antemano. Si n
no es fijo, entonces el más rápido es do.call("rbind", rep(list(A), n)
. Estos fueron para una matriz de 3x5 y 10 repeticiones. Matrices de diferentes tamaños podrían dar diferentes ordenamientos.
EDITAR:
Para n = 600, los resultados están en un orden diferente ( rbind
versión rbind
explícita):
A <- matrix(1:15, nrow=3)
n <- 600
benchmark(do.call("rbind", replicate(n, A, simplify=FALSE)),
do.call("rbind", rep(list(A), n)),
apply(A, 2, rep, n),
matrix(rep(t(A),n), ncol=ncol(A), byrow=TRUE),
order="relative", replications=10000)
dando
test replications elapsed
4 matrix(rep(t(A), n), ncol = ncol(A), byrow = TRUE) 10000 1.74
3 apply(A, 2, rep, n) 10000 2.57
2 do.call("rbind", rep(list(A), n)) 10000 2.79
1 do.call("rbind", replicate(n, A, simplify = FALSE)) 10000 6.68
relative user.self sys.self user.child sys.child
4 1.000 1.75 0 NA NA
3 1.477 2.54 0 NA NA
2 1.603 2.79 0 NA NA
1 3.839 6.65 0 NA NA
Si incluye la versión explícita de rbind
, es ligeramente más rápida que la do.call("rbind", rep(list(A), n))
, pero no mucho, y más lenta que la versión de apply
o matrix
. Por lo tanto, una generalización a n
arbitraria no requiere una pérdida de velocidad en este caso.
Probablemente esto sea más eficiente:
apply(A, 2, rep, n)
Puedes usar indexación
A[rep(seq(nrow(A)), n), ]
También hay esta manera:
rep(1, n) %x% A