Экспорт именованной переменной в цикле foreach

У меня есть большая таблица data.table (+12 млн строк), которую мне нужно преобразовать следующим образом:
Сверните каждую строку с одинаковым значением первого столбца (назовем ее BookId) в 1 строку и объедините другие столбцы в большую "данные". " поле. Эта таблица содержит 2,7 млн ​​уникальных BookId.

Ie :

BookId    Col1      Col2     ...      ColN
B001      Author    Bob      ...      ...
B002      Author    Marc     ...      ...
B002      Editor    Bob Inc  ...      ...
B001      Editor    MyBooks  ...      ...

Ожидаемый результат:

BookId    data
B001      Bob,MyBooks, ...
B002      Marc,Bob Inc, ...

На данный момент мне удалось воспроизвести эту структуру с помощью подмножеств, но это очень медленно, для построения строки требуется до 300 мс, а это значит, что процесс займет до 9 дней.

Поэтому я решил использовать параллельный цикл foreach, чтобы ускорить процесс.
Мой первый подход состоял в том, чтобы перебрать список bookId в цикле, но он делил бы глобальное общее время только на количество ядер, что неудовлетворительно (8 ядер означает +1 день). Кроме того, это означает, что каждый процесс будет автоматически экспортировать очень большой объем данных, поскольку всем им нужен весь объект data.table.

Я нашел другой способ улучшить процесс, разделив первичную таблицу данных на независимые подмножества на основе списка bookId, а затем заставить каждый кластер работать с этими подмножествами (меньше строк означает более быстрое создание подмножеств). К сожалению, я не могу экспортировать свои подмножества в кластеры, так как они имеют «динамическое» имя. Я попробовал параметр «.export», но я думаю, что он не знает о текущем значении «i» при оценке. Как я могу этого добиться? Это вообще возможно?

Я новичок в R, мне сказали, что всегда было много способов добиться одного и того же, выбрал ли я лучший подход для достижения этой цели?

Вот мой код:

# Create cluster based on available cores
cores = detectCores()
cl <- makeCluster(cores)
registerDoParallel(cl)

# Load datas and generate BookId lists
books <- fread("books.tab")
bookId.unique.list <- unique(books$BookId)
bookId.list <- books$BookId

# Split datatable into "equals" subsets
subset.length = ceiling(length(book.unique.list)/cores)
for (i in 1:(cores)) {
    start = (i-1)*subset.length
    end = (i)*subset.length
    list = book.unique.list[start:end]
    assign(paste("books",i,sep=""), books[books$BookId %in% list])
    assign(paste("book.list",i,sep=""), list )
}

# Prepare resulting DT
res = data.table(BookId = character(0), data = character(0))

# Parallel loop
res  <- foreach(i = 1:cores, .combine = rbind, .export = paste0("book", i),  .packages = c("data.table")) %dopar% {

    #Try to get the named subset corresponding to the current iteration (i)
    # IE : Books1, Books2...
    BookSubset = get(paste0("book", i))
    Book.list.subset = unique(BookSubset$BookId)

    temp = data.table(BookId = character(0), data = character(0))

    for (i in 1:length(Book.list.subset)) {
        bookId = Book.list.subset[i]

        subset <- BookSubset[which(Book.list.subset ==bookId)]
        output = capture.output(write.table(subset, stdout()quote=FALSE, row.names=FALSE,col.names=FALSE)

      temp <- rbind(hist, data.table(zkf_BOOK = c(bookId), data = c(output)))
    }
    temp
}

Вот результат dput[head(books)):

structure(list(BookId = c("BOOKXXXX774051532082", "BOOKXXXX776514515608", 
    "BOOKXXXX776287821289", "BOOKXXXX776514515608", "BOOKXXXX774051532082", 
    "BOOKXXXX774051532082"), V2 = c("ZUSRXXXX842901236553", 
    "ZUSRXXXX371255229634", 
     "ZUSRXXXX656080986411", "ZUSRXXXX371255229634", "ZUSRXXXX842901236553", 
    "ZUSRXXXX842901236553"), V3 = c("BOOKEVTX776757835463", 
    "BOOKEVTX776762775464", 
    "BOOKEVTX776772854465", "BOOKEVTX776773643466", "", "BOOKEVTX776995487467"
    ), V4 = c("ZACTIONX215229995154", "ZACTIONX533300043134", 
    "ZACTIONX533300043134", 
    "ZACTIONX533300043134", "", "ZACTIONX215229995154"), V5 = c("", 
    "", "", "", "", ""), V6 = c("", "", "", "", "MAILOUTX776774376684", 
    ""), V7 = c("", "", "", "", "", ""), V8 = c("", "", "", "", "", 
    ""), V9 = c("", "", "", "", "", ""), V10 = c("", "", "", "", 
    "", ""), V11 = c("", "", "", "", "", "")), .Names = c("zkf_BOOK", 
    "V2", "V3", "V4", "V5", "V6", "V7", "V8", "V9", "V10", "V11"), class = 
    c("data.table", 
    "data.frame"), row.names = c(NA, -6L))

Вот пример моего «реального» ввода данных:

BOOKXXXX774051532082    ZUSRXXXX842901236553    BOOKEVTX776757835463    ZACTIONX215229995154                            
BOOKXXXX776514515608    ZUSRXXXX371255229634    BOOKEVTX776762775464    ZACTIONX533300043134                            
BOOKXXXX776287821289    ZUSRXXXX656080986411    BOOKEVTX776772854465    ZACTIONX533300043134                            
BOOKXXXX776514515608    ZUSRXXXX371255229634    BOOKEVTX776773643466    ZACTIONX533300043134                            
BOOKXXXX774051532082    ZUSRXXXX842901236553                MAILOUTX776774376684                    
BOOKXXXX774051532082    ZUSRXXXX842901236553    BOOKEVTX776995487467    ZACTIONX215229995154                            
BOOKXXXX776287821289    ZUSRXXXX656080986411    BOOKEVTX777107387468    ZACTIONX533300043134    

и ожидаемый результат

BOOKXXXX774051532082    ZUSRXXXX842901236553|BOOKEVTX776757835463|ZACTIONX215229995154|||||||;ZUSRXXXX842901236553||||MAILOUTX776774376684|||||;ZUSRXXXX842901236553|BOOKEVTX776995487467|ZACTIONX215229995154|||||||
BOOKXXXX776514515608    ZUSRXXXX371255229634|BOOKEVTX776762775464|ZACTIONX533300043134|||||||;ZUSRXXXX371255229634|BOOKEVTX776773643466|ZACTIONX533300043134|||||||
BOOKXXXX776287821289    ZUSRXXXX656080986411|BOOKEVTX776772854465|ZACTIONX533300043134|||||||;ZUSRXXXX656080986411|BOOKEVTX777107387468|ZACTIONX533300043134|||||||

person Airmoi    schedule 14.11.2017    source источник
comment
Не могли бы вы dput(head(books))   -  person Hack-R    schedule 14.11.2017
comment
Пожалуйста, предоставьте точный воспроизводимый ввод и полный соответствующий вывод. Решение вашей проблемы не в распараллеливании, а в написании более эффективного кода R.   -  person Roland    schedule 14.11.2017
comment
@Airmoi, пожалуйста, добавьте этот вывод в свой пост, а не в комментарии. Используйте кнопку редактирования   -  person talat    schedule 14.11.2017


Ответы (1)


ОП запросил две операции сворачивания:

  1. Для каждой строки сверните все столбцы (кроме столбца идентификатора zkf_BOOK) в одно поле данных, разделенное |.
  2. Для каждой группы zkf_BOOK свернуть строки, разделенные ;

Свертывание внутри столбцов выполняется вызовом Reduce(), а свертывание по строкам выполняется по группам с использованием paste(). С data.table столбцы в параметре by = не включаются в операции над .SD.

library(data.table)
setDT(books)[, paste(Reduce(function(x, y) paste(x, y, sep = "|"), .SD), collapse = ";"), 
             by = zkf_BOOK]
               zkf_BOOK
1: BOOKXXXX774051532082
2: BOOKXXXX776514515608
3: BOOKXXXX776287821289
                                                                                                                                                                                              V1
1: ZUSRXXXX842901236553|BOOKEVTX776757835463|ZACTIONX215229995154|||||||;ZUSRXXXX842901236553||||MAILOUTX776774376684|||||;ZUSRXXXX842901236553|BOOKEVTX776995487467|ZACTIONX215229995154|||||||
2:                                                   ZUSRXXXX371255229634|BOOKEVTX776762775464|ZACTIONX533300043134|||||||;ZUSRXXXX371255229634|BOOKEVTX776773643466|ZACTIONX533300043134|||||||
3:                                                                                                                         ZUSRXXXX656080986411|BOOKEVTX776772854465|ZACTIONX533300043134|||||||

Обратите внимание, что несоответствие ожидаемому результату связано с тем, что dput[head(books)) возвращает только 6 строк, в то время как ввод и вывод данных для печати основаны на 7 строках (или более).

person Uwe    schedule 14.11.2017