Paralelización

R en paralelo (pero ahora, con futuros)

R

Esta entrada extiende y mejora una homónima de 2014.

El problema de entonces consistía en calcular por separado y en paralelo objetos A, B y C para combinarlos después. Cuando, por supuesto, el cálculo de A, B y C es pesado.

El muy reciente paquete future incorpora a R un mecanismo disponible en otros lenguajes de programación: un cierto tipo de datos, los futuros, que contienen promesas de valores que se calculan fuera del hilo principal del programa. Se usan, por ejemplo, para realizar llamadas a APIs, operaciones de IO o (y esto es más pertinente para usuarios de R) cálculos que llevan su tiempico.

Estrategias escalables con R

R

Recomiendo leer Scalable Strategies for Computing with Massive Data, un artículo que trata dos de los problemas de escalabilidad con que tropezamos los usuarios de R:

  • Los de memoria, para los que proponen e ilustran el uso del paquete bigmemory.
  • Los de velocidad de ejecución, a los que se enfrentan paralelizando el código, tanto en una única máquina como en un clúster, con foreach.

En el artículo no solo discute los dos paquetes por separado sino que ilustra, además, cómo usarlos conjuntamente en su propuesta de estrategia escalable con R.

Paralelismo en R: memo[rándum]

R

Esta es una nota que me dejo a mí mismo sobre paralelización en R para no tener que ir buscándola en otras partes:

library(parallel)

foo <- function(i){
  Sys.sleep(i)
}

cl <- makeCluster(4)

system.time(parSapply(cl, 1:4, foo))
# user  system elapsed
# 0.025   0.006   4.007

system.time(sapply(1:4, foo))
# user  system elapsed
# 0.039   0.033  10.001

stopCluster(cl)

Paralelización en R con snow

Suelo trabajar un servidor con ocho CPUs. Cuando quiero paralelizar código en R, suelo utilizar [parallel::mclapply](https://stat.ethz.ch/R-manual/R-devel/library/parallel/html/mclapply.html) (como aquí). Pero no tengo una máquina. Tengo varias. Y antes, de hecho, muchas.

¿Cómo paralelizar en distintas máquinas?

Se puede usar Spark (y SparkR), por ejemplo. Pero una ruta que no había ensayado jamás es la de la vieja escuela, i.e., MPI, snow y demás.

Pero si

  • tienes varios servidores corriendo un sistema operativo decente,
  • instalas R y snow (y todo lo que necesites) en todos ellos y
  • configuras los servidores para poder acceder a través de ssh sin contraseña desde uno central,

y, entonces, ejecutas

Estrategias escalables (con R)

Hay quienes preguntan cómo cargar con R un csv de 8GB en un portátil de 4GB de RAM. La verdad, he leído respuestas la mar de extravagantes a este tipo de cuestiones: p.e., recomendar SQLite.

Yo recomendaría Scalable Strategies for Computing with Massive Data. Entre otras cosas, porque para eso lo escribieron sus autores: para que se lea. Y porque está cargado de razón y buenos consejos.

Una cosa con la que tropezará enseguida quien lo hojee es:

Validación cruzada en paralelo

R

Estoy sin tiempo, así que os suelto el código y me largo a casa a no cenar. Es así:

library(parallel)

cl <- makeCluster(8)

# solo si hay aleatorización
# clusterSetRNGStream(cl, 123)

clusterEvalQ(cl,
{
	# las librerías necesarias tienen que cargarse
	# en cada esclavo
	library(rpart)

	# en la práctica, hay que cargar los datos
	# (¿desde fichero?) en cada esclavo
	my.data <- iris

	# lo mismo con las funciones necesarias
	foo <- function(x, dat){
		train <- 1:nrow(dat) %% 10 != 1
		mod <- rpart(Species ~ ., data = dat[train,])
		res <- predict(mod, dat[!train,])
	}
})

res <- parSapply(cl, 0:9,
	function(x) foo(x, my.data), simplify = F)

R en paralelo

R

Trabajo sobre una máquina de 8 núcleos y 24 GB de RAM. Y que conste que se me ha llegado a quedar chica.

Algunos programas que ejecuto tienen (o contienen pedazos de) la forma

  1. calcula A
  2. calcula B
  3. calcula C
  4. combina A, B y C

Obviamente, se me ocurre ejecutarlos así:

  1. calcula A, B y C en paralelo
  2. cuando acabe el paso anterior, combina A, B y C

Y aún me sobrarían 5 núcleos y bastante RAM. La pregunta es: ¿cómo?

Veinte paquetes de R para científicos de datos

R

Me llegó recientemente un artículo con una lista de veinte paquetes de R para data scientists. Y no la encuentro afortunada. Voy a agrupar esos veinte paquetes en algunas categorías y añadiré comentarios. La primera de ellas es la de manipulación de datos, tal vez la más amplia, que recoge los siguientes: sqldf, plyr, stringr (para procesar texto), lubridate (para procesar fechas),reshape2 y los paquetes de acceso a bases de datos.

Dont be loopy! (III: jackknife y paralelismo)

R

Esta es la tercera entrega de una serie de artículos en los que comparo SAS y R a la hora de realizar diversos tipos de simulaciones basados en Don’t Be Loopy: Re-Sampling and Simulation the SAS® Way.

Esta vez toca compararlos a la hora de aplicar el método del jackknife.

Primero, el código SAS que recomienda el autor del artículo, que calcula la curtosis de un conjunto de datos trivial (una muestra de 10k valores que siguen una distribución uniforme):

Paralelización de bucles con foreach

R

Parcialmente en agradecimiento a Revolution Analytics por haber concedido una subvención a las III Jornadas de usuarios de R voy a discutir en esta entrada cómo paralelizar bucles usando los paquetes foreach y doMC desarrollados por dicha empresa.

El paquete foreach contiene, esencialmente, una única función, foreach, que, en su forma más básica, permite ejecutar bucles con una sintaxis un tanto peculiar:

foreach( i = 1:3 ) %do% log( i )

Volveré sobre algunas operaciones interesantes y bastante útiles que permite realizar esta función porque, de todas ellas, hoy me ocuparé sólo de una: la que abre la puerta de una manera sencilla a la paralelización de bucles.

El paquete multicore de R

R

Tengo acceso a una máquina que, aunque anda un poco corta de memoria, cuenta con ocho CPUs. Tenía unas simulaciones bastante pesadas que correr y quise aprovechar su naturaleza perfectamente paralelizable. Y, de paso, hacer con R lo mismo por lo que he visto a un consultor de SAS cobrar a razón de 3.000 dólares diarios.

En el fondo, es una trivialidad. Supongamos que la función que implementa la simulación se llama foo. Habitualmente, haríamos