Можно ли распараллелить в R?
Поскольку я сижу здесь, ожидая запуска некоторых скриптов R... Мне было интересно... есть ли способ распараллеливать rbind в R?
Я сижу, ожидая, когда этот вызов будет завершен, когда я буду разбираться с большими объемами данных.
do.call("rbind", LIST)
Ответы
Ответ 1
Я сомневаюсь, что вы можете заставить это работать быстрее, паралеллизируя его: кроме того, что вам, вероятно, придется написать его самостоятельно (сначала прокрутите первый пункт 1 и 2, в то время как поток два переводит элементы 3 и 4 и т.д., и когда они будут сделаны, результаты будут "отскок", что-то вроде этого - я не вижу способ улучшения, отличный от C), он будет включать в себя копирование больших объемов данных между вашими потоками, что обычно то, что идет медленно в первую очередь.
В C вы можете обмениваться объектами между потоками, поэтому вы можете записать все ваши потоки в одну и ту же память. Желаю вам удачи в этом: -)
Наконец, поскольку в стороне: rbinding data.frames просто медленно. Если вы знаете, что структура всех ваших data.frames точно такая же, и она не содержит чистых столбцов символов, вы, вероятно, можете использовать трюк из этого ответа на один моих вопросов. Если ваш data.frame содержит столбцы символов, я подозреваю, что вам лучше разобраться с ними отдельно (do.call(c, lapply(LIST, "[[", "myCharColName"))
), а затем выполнить трюк с остальными, после чего вы сможете воссоединить их.
Ответ 2
Я не нашел способ сделать это параллельно или до сих пор. Однако для моего набора данных (этот список содержит около 1500 фреймов данных, состоящих из 4.5M строк), следующий сниппет, казалось, помог:
while(length(lst) > 1) {
idxlst <- seq(from=1, to=length(lst), by=2)
lst <- lapply(idxlst, function(i) {
if(i==length(lst)) { return(lst[[i]]) }
return(rbind(lst[[i]], lst[[i+1]]))
})
}
где lst - список. Это примерно в 4 раза быстрее, чем при использовании do.call(rbind, lst)
или даже do.call(rbind.fill, lst)
(с rbind.fill из пакета plyr). На каждой итерации этот код сокращает вдвое количество кадров данных.
Ответ 3
Поскольку вы сказали, что хотите привязать объекты data.frame
, вы должны использовать пакет data.table
. Он имеет функцию, называемую rbindlist
, которая резко увеличивает rbind
. Я не уверен на 100%, но я бы поставил на то, что любое использование rbind
приведет к копированию, когда rbindlist
не будет.
В любом случае a data.table
является data.frame
, поэтому вы не потеряете ничего, чтобы попробовать.
ИЗМЕНИТЬ:
library(data.table)
system.time(dt <- rbindlist(pieces))
utilisateur système écoulé
0.12 0.00 0.13
tables()
NAME NROW MB COLS KEY
[1,] dt 1,000 8 X1,X2,X3,X4,X5,X6,X7,X8,...
Total: 8MB
Молния быстро...
Ответ 4
Здесь решение, естественно, распространяется на функции rbind.fill, merge и других функций списка данных:
Но, как и со всеми моими ответами/вопросами, проверьте:)
require(snowfall)
require(rbenchmark)
rbinder <- function(..., cores=NULL){
if(is.null(cores)){
do.call("rbind", ...)
}else{
sequ <- as.integer(seq(1, length(...), length.out=cores+1))
listOLists <- paste(paste("list", seq(cores), sep=""), " = ...[", c(1, sequ[2:cores]+1), ":", sequ[2:(cores+1)], "]", sep="", collapse=", ")
dfs <- eval(parse(text=paste("list(", listOLists, ")")))
suppressMessages(sfInit(parallel=TRUE, cores))
dfs <- sfLapply(dfs, function(x) do.call("rbind", x))
suppressMessages(sfStop())
do.call("rbind", dfs)
}
}
pieces <- lapply(seq(1000), function(.) data.frame(matrix(runif(1000), ncol=1000)))
benchmark(do.call("rbind", pieces), rbinder(pieces), rbinder(pieces, cores=4), replications = 10)
#test replications elapsed relative user.self sys.self user.child sys.child
#With intel i5 3570k
#1 do.call("rbind", pieces) 10 116.70 6.505 115.79 0.10 NA NA
#3 rbinder(pieces, cores = 4) 10 17.94 1.000 1.67 2.12 NA NA
#2 rbinder(pieces) 10 116.03 6.468 115.50 0.05 NA NA
Ответ 5
Это расширяется на ответ @Dominik.
Мы можем использовать mclapply из параллельного пакета для дальнейшего увеличения скорости. Также rbind.fill делает лучшую работу, чем rbind, так что здесь улучшенный код.
ПРИМЕЧАНИЕ. Это будет работать только с mac/linux. mclapply не поддерживается в Windows.
EDIT: если вы хотите увидеть прогресс, раскомментируйте строку print (i) и убедитесь, что вы работаете с терминалом, а не с RStudio. Печать в RStudio из параллельного процесса, типа беспорядков RStudio up.
library(parallel)
rbind.fill.parallel <- function(list){
while(length(list) > 1) {
idxlst <- seq(from=1, to=length(list), by=2)
list <- mclapply(idxlst, function(i) {
#print(i) #uncomment this if you want to see progress
if(i==length(list)) { return(list[[i]]) }
return(rbind.fill(list[[i]], list[[i+1]]))
})
}
}
Ответ 6
Похоже, что на это уже хорошо ответили многие люди, но если это подходит кому-то, вот версия параллельного rbind для объектов не-data.table/data.frame-esque:
rbind.parallel <- function(list,ncore)
{
library(parallel)
do.call.rbind<-function(x){do.call(rbind,x)}
cl<-makeCluster(ncore)
list.split<-split(list,rep(1:ncore,length(list)+1)[1:length(list)])
list.join<-parLapply(cl,list.split,do.call.rbind)
stopCluster(cl)
list.out<-do.call(rbind,list.join)
return(list.out)
}
Это эффективно работает с объектами типа sf. Например, если вы читаете список шейп файлов из каталога, используя lapply(.,st_read)
, очевидно, что rbind.fill и его варианты не будут работать для объединения всех функций.