Можно ли распараллелить rbind в R? - PullRequest
31 голосов
/ 29 августа 2011

Пока я сижу здесь и жду запуска некоторых сценариев R ... Мне было интересно ... Есть ли способ распараллелить rbind в R?

Я сижу в ожидании, чтобы этот вызов часто завершался, поскольку я имею дело с большими объемами данных.

Ответы [ 6 ]

25 голосов
/ 10 ноября 2011

До сих пор я не нашел способа сделать это параллельно.Однако для моего набора данных (это список из примерно 1500 фреймов данных общим объемом 4,5 млн. Строк), мне кажется, что следующий фрагмент кода помог:

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).На каждой итерации этот код вдвое сокращает количество фреймов данных.

18 голосов
/ 23 июля 2013

Поскольку вы сказали, что хотите связать data.frame объекты, вам следует использовать пакет data.table. У него есть функция под названием rbindlist, которая значительно улучшает rbind. Я не уверен на 100%, но могу поспорить, что любое использование rbind вызовет копию, а rbindlist - нет. В любом случае 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

Молниеносно ...

17 голосов
/ 01 сентября 2011

Я сомневаюсь, что вы можете заставить это работать быстрее, распараллелив его: кроме того факта, что вам, вероятно, придется написать это самостоятельно (нить 1 сначала связывает элементы 1 и 2, а нить 2 связывает элементы 3 и 4 и т.д.и когда они сделаны, результаты «отскок», что-то в этом роде - я не вижу способ улучшить это без C), это будет включать в себя копирование больших объемов данных между вашими потоками, чтообычно это происходит медленно.

В C вы можете обмениваться объектами между потоками, так что вы можете записать все свои потоки в одну и ту же память.Я желаю вам удачи с этим: -)

Наконец, в качестве отступления: rbinding data.frames просто медленный.Если вы заранее знаете, что структура всех ваших data.frames точно такая же, и она не содержит столбцы с чистыми символами, вы можете использовать трюк из этого ответа на один из моих вопросов .Если ваш data.frame содержит символьные столбцы, я подозреваю, что лучше всего обрабатывать их отдельно (do.call(c, lapply(LIST, "[[", "myCharColName"))), а затем выполнять трюк с остальными, после чего вы можете воссоединить их.

7 голосов
/ 23 июля 2013

Вот решение, оно естественным образом распространяется на функции 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
1 голос
/ 25 января 2019

Похоже, что на это уже хорошо ответили многие люди, но если это подходит кому-то, вот версия параллельного 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 и его варианты не будут работать, чтобы объединить все функции.

1 голос
/ 26 ноября 2015

Это расширяет ответ @Dominik.

Мы можем использовать mclapply из параллельного пакета для дальнейшего увеличения скорости.Также rbind.fill работает лучше, чем rbind, так что вот улучшенный код.ПРИМЕЧАНИЕ: это будет работать только на Mac / Linux.mclapply не поддерживается в Windows.РЕДАКТИРОВАТЬ: если вы хотите увидеть прогресс, раскомментируйте строку 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]]))
    })
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...