Paralelizando código em R em alguns minutos

Se você acredita nos estereótipos, a linguagem R é algo altamente especializado para estatística e aprendizado de máquina. O segundo estereótipo é que o código R puro não é muito rápido: em primeiro lugar, porque é interpretado e, em segundo lugar, porque é executado sequencialmente. Claro, os estereótipos têm alguma ligação com a realidade, caso contrário não existiriam, mas é por isso que são estereótipos, que dão uma imagem extremamente simplificada do mundo, em que muitos detalhes se perdem. Em particular, hoje eu quero compartilhar uma maneira surpreendentemente simples de adicionar paralelismo a R e multiplicar a velocidade da execução do código existente sem ter que fazer grandes alterações nele. Tudo isso é feito em apenas alguns minutos.



Digamos que temos uma matriz ou tabela de dados contendo várias linhas e colunas e queremos realizar algum tipo do mesmo tipo de cálculo para cada uma das linhas. Por exemplo, calcule a soma dos quadrados de seus valores. É lógico mover os cálculos para uma função e chamá-la para cada uma das linhas.



Dados iniciais:



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


Função:



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


Você pode simplesmente fazer um loop sobre as linhas e aplicar esta função a cada uma das linhas, mas esta não é a forma mais recomendada para R. Os cálculos para cada uma das linhas serão executados sequencialmente, todos os cálculos serão executados no mesmo núcleo. Esse tipo de código não é muito eficiente. Por precaução, vamos anotar esta opção e medir o tempo de execução:



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


Medimos o tempo de execução:



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))


Nós temos:



 :  4.474074 secs


Usaremos esse tempo como um ponto de partida para comparação com outros 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 . .



Bom, isso é tudo. O método é bastante simples. Para mim, quando descobri sobre ele, foi apenas uma dádiva de Deus. É verdade que o R atual não suporta computação paralela? Depende do ponto de vista sobre o assunto, da gravidade do seu enunciado. Mas, de certo modo, podemos supor que ele apóia.




All Articles