Paralelizar código en R en un par de minutos

Si cree en los estereotipos, entonces el lenguaje R es algo altamente especializado en estadística y aprendizaje automático. El segundo estereotipo es que el código R puro no es muy rápido: en primer lugar, porque se interpreta y, en segundo lugar, porque se ejecuta de forma secuencial. Por supuesto, los estereotipos tienen alguna conexión con la realidad, de lo contrario no existirían, pero por eso son estereotipos, que dan una imagen del mundo sumamente simplificada, en la que se pierden muchos detalles. En particular, hoy quiero compartir una manera sorprendentemente simple de agregar paralelismo a R y multiplicar la velocidad de ejecución del código existente sin tener que realizar cambios importantes en él. Todo esto se hace en solo un par de minutos.



Digamos que tenemos una matriz o tabla de datos que contiene una serie de filas y columnas, y queremos realizar algún tipo de cálculo del mismo tipo para cada una de las filas. Por ejemplo, calcule la suma de los cuadrados de sus valores. Es lógico mover los cálculos a una función y llamarla para cada una de las líneas.



Datos iniciales:



a <- matrix(rnorm(500000, mean=0, sd=2), 100000, 50)


Función:



sum.of.squares <- function(n) {
  n_sq <- sapply(n, function(x) x^2)
  sum(n_sq)
}


Puede simplemente recorrer las líneas y aplicar esta función a cada una de las líneas, pero esta no es la forma más recomendada para R. Los cálculos para cada una de las líneas se realizarán secuencialmente, todos los cálculos se realizarán en el mismo núcleo. Este tipo de código no es realmente muy eficaz. Por si acaso, anotemos esta opción y midamos el tiempo de ejecución:



b <- vector()
for(i in 1:dim(a)[1]) {
  b[i] <- sum.of.squares(a[i,])
}


Medimos el tiempo de ejecución:



b <- vector()
start_time <- Sys.time()
for(i in 1:dim(a)[1]) {
  b[i] <- sum.of.squares(a[i,])
}
timediff <- difftime(Sys.time(), start_time)
cat(" : ", timediff, units(timediff))


Obtenemos:



 :  4.474074 secs


Usaremos este tiempo como un punto de partida para comparar con otros métodos.



. R apply(). , : 1, 2. , . – sapply(), . – . , apply() :



b <- apply(a, 1, function(x) sum.of.squares(x))


, . , , :



start_time <- Sys.time()
b <- apply(a, 1, function(x) sum.of.squares(x))
timediff <- difftime(Sys.time(),start_time)
cat(" : ", timediff, units(timediff))


:



 : 4.484046 secs


, . : , .



, , R , . : apply(), , . , , . , apply(). apply() by(), eapply(), lapply(), Map(), .mapply(), mapply(), replicate(), sapply(), tapply(), vapply(). , future_apply:



install.packages("future.apply") 


– . , :



library("future.apply")
plan(multiprocess)


. , . future::plan(). , , apply "future_". :



b <- future_apply(a, 1, function(x) sum.of.squares(x))


:



start_time <- Sys.time()
b <- future_apply(a, 1, function(x) sum.of.squares(x))
timediff <- difftime(Sys.time(),start_time)
cat(" : ", timediff, units(timediff))


:



 :  1.283569 secs


Intel Core i7-8750H 12 . 12-, .



. , , , , , . , , future_sapply, . . – . , , , (a <- data.frame(a)), , 8 . .



Bueno eso es todo. El método es bastante sencillo. Para mí, cuando me enteré de él, fue solo un regalo del cielo. ¿Es cierto que la R actual no es compatible con la computación en paralelo? Depende del punto de vista sobre este tema, de la gravedad de su afirmación. Pero, en cierto sentido, podemos asumir que es compatible.




All Articles