Marzo 2019

Paralelizando código R

  • Paralelizar código R es relativamente facil.

Paralelizando código R

  • Paralelizar código R es relativamente facil.
  • Desafíos:
    • Detectar bloques de código paralelizables (trabajoso)

Paralelizando código R

  • Paralelizar código R es relativamente facil.
  • Desafíos:
    • Detectar bloques de código paralelizables (trabajoso)
    • Aprender a llevar los ciclos for a ciclos de la familia apply (acostumbrarse)

Es paralelizable?

Supongamos que tengo la función comer_asado(carne)

Es paralelizable?

Supongamos que tengo la función comer_asado(carne)

Es paralelizable?


Es paralelizable?


Es paralelizable?


Es paralelizable?


Es paralelizable?


Es paralelizable?


Es paralelizable?

# comer_asado(carne) = 
tareas <- c(salar, asar, emplatar);
for (f in tareas) {
  carne <- f(carne)
} # la carne ya esta salada, asada, y en plato!
comer(carne)

Es paralelizable?

# comer_asado(carne) = 
tareas <- c(salar, asar, emplatar);
for (f in tareas) {
  carne <- f(carne)
} # la carne ya esta salada, asada, y en plato!
comer(carne)

Es paralelizable?

# 600 personas van a comer_asado(carne) = 
for (persona in 1:600) {
  tareas <- c(salar, asar, emplatar);
  for (f in tareas) {
    carne <- f(carne)
  } # la carne ya esta salada, asada, y en plato!
  comer(carne, persona)
}

Es paralelizable?

Es paralelizable?

Es paralelizable?

\(fib(0) = 0\)

\(fib(1) = 1\)

\(fib(n) = fib(n-1) + fib(n-2)\)

# fib(n) = 
n <- 10;
res <- c(0, 1);
for (i in 3:(n+1)) {
  res <- c(res, res[[i-1]]+res[[i-2]])
}; res
##  [1]  0  1  1  2  3  5  8 13 21 34 55

Nota al margen:

system.time:

system.time({
  Sys.sleep(2)
})
##    user  system elapsed 
##   0.000   0.000   2.002

for vs apply

  • apply mejor que for?

for vs apply

  • apply mejor que for?
    • apply es siempre más rápido que for
system.time({
  res <- c(); for (i in 1:1000) { res[[i]] <- i^2 }
})
system.time({
  res <- unlist(lapply(1:1000, function(i) { i^2 }))
})

for vs apply

  • apply mejor que for?
    • apply es siempre más rápido que for
system.time({
  res <- c(); for (i in 1:1000) { res[[i]] <- i^2 }
})
##    user  system elapsed 
##   0.003   0.000   0.004
system.time({
  res <- unlist(lapply(1:1000, function(i) { i^2 }))
})
##    user  system elapsed 
##   0.002   0.000   0.002

for vs apply

  • apply mejor que for?
    • apply es siempre suele ser más rápido que for
system.time({
  res <- numeric(1000); for (i in 1:1000) { res[[i]] <- i^2 }
})
##    user  system elapsed 
##   0.002   0.000   0.003
system.time({
  res <- unlist(lapply(1:1000, function(i) { i^2 }))
})
##    user  system elapsed 
##   0.002   0.000   0.002

for vs apply

  • apply mejor que for?
    • apply es siempre suele ser más rápido que for
    • R comenzó con tendencias de lenguaje “funcional”

for vs apply

  • apply mejor que for?
    • apply es siempre suele ser más rápido que for
    • R comenzó con tendencias de lenguaje “funcional”
    • apply no tiene efectos secundarios

for vs apply

  • apply mejor que for?
    • apply es siempre suele ser más rápido que for
    • R comenzó con tendencias de lenguaje “funcional”
    • apply no tiene efectos secundarios
  • todo ciclo for se puede traducir a un ciclo lapply.

Como funciona lapply?

?lapply
lapply(X, FUN, ...)
X      a vector (atomic or list)
FUN    the function to be applied to each element of X

lapply returns a list of the same length as X, each element
of which is the result of applying FUN to the corresponding
element of X.

Como funciona lapply?

for vs apply

res <- numeric(10)
for(i in 1:10) {
  res[[i]] <- f(i)
}

for vs apply

res <- numeric(10)
for(i in 1:10) {
  res[[i]] <- f(i)
}

==

res <- lapply(1:10, function(i) {
  f(i)
})

Ojo con el scope!

for(i in 1:10) {
  # Aca puedo leer y modificar variables
  # de fuera del for
}
res <- lapply(1:10, function(i) {
  # Aca solo puedo leer variables de fuera
  # del lapply (no las modifica)
})

Ojo con el scope! Ejemplo

n_vals <- 3 # generamos 3 valores aleatorios normales
random_vals <- rnorm(n_vals)

Ojo con el scope! Ejemplo

# guardaremos el cuadrado y cubo de cada valor
cuadrado_for <- cubo_for <- NULL

for(i in 1:n_vals) {
  cuadrado_for[[i]] <- random_vals[[i]]^2
  cubo_for[[i]] <- random_vals[[i]]^3
}
cuadrado_for; cubo_for
## [1] 1.519544 0.126510 1.555679
## [1] -1.87313926 -0.04499738  1.94034892

Ojo con el scope! Ejemplo

# guardaremos el cuadrado y cubo de cada valor
cuadrado_lapply <- cubo_lapply <- NULL

invisible(lapply(1:n_vals, function(i) {
  cuadrado_lapply[[i]] <- random_vals[[i]]^2
  cubo_lapply[[i]] <- random_vals[[i]]^3
}))
cuadrado_lapply; cubo_lapply
## NULL
## NULL

Ojo con el scope! Ejemplo

res <- lapply(1:n_vals, function(i) {
  return(c(random_vals[[i]]^2, random_vals[[i]]^3))
})
do.call(cbind, res)
##           [,1]        [,2]     [,3]
## [1,]  1.519544  0.12650998 1.555679
## [2,] -1.873139 -0.04499738 1.940349

Operador del mal




assign

<<-

Supongamos el siguiente codigo

medias_n_normales <- function(n, mu) {
  abs(mu-mean(rnorm(n, mean=mu)))
}

Si fijo mu=4