2017-11-14 2 views
0

Ich habe eine große data.table (+ 12M Zeilen), die ich auf diese Weise konvertieren müssen:
Collapse jede Zeile mit der gleichen ersten Spalte Wert (nennen wir es BookId) in 1 Zeile und füge andere Spalten in ein großes "Daten" -Feld ein. Diese Tabelle enthält 2.7M einzigartige BookID desExport benannte Variable in foreach Schleife

Ie:

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

Expeted Ergebnis:

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

Vorerst habe ich in der Lage, diese Struktur zu reproduzieren Teilmengen verwenden, aber das ist wirklich Langsam dauert es bis zu 300ms, um eine Reihe zu bauen, was bedeutet, dass es bis zu 9 Tage dauern wird, um den Prozess zu erreichen.

Also entschied ich mich, eine parallele foreach-Schleife zu verwenden, um den Prozess zu beschleunigen.
Meine erste Annäherung war es, die bookId-Liste zu durchlaufen, aber es würde nur die globale Gesamtzeit durch die Anzahl der Kerne teilen, die nicht zufriedenstellend ist (8 Kerne bedeutet +1 Tag). Das bedeutet auch, dass jeder Prozess eine sehr große Datenmenge automatisch exportiert, da alle das gesamte data.table-Objekt benötigen.

Ich fand einen anderen Ansatz, um den Prozess zu verbessern, indem Sie die primäre data.table in unabhängige Subsets basierend auf bookId-Liste aufteilen und dann jeden Cluster an diesen Subsets arbeiten lassen (weniger Zeilen bedeuten schnellere Generierung von Subsets). Leider kann ich meine Untergruppen nicht in Cluster exportieren, da sie einen "dynamischen" Namen haben. Ich habe versucht ".export" param, aber ich denke, es ist nicht bewusst, den aktuellen "i" -Wert bei der Auswertung. Wie kann ich das erreichen? Ist es überhaupt möglich ?

Ich bin neu in R, mir wurde gesagt, dass es immer viele Möglichkeiten gibt, um das Gleiche zu erreichen, habe ich den besten Ansatz gewählt, um dies zu erreichen?

Hier ist mein Code:

# 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 
} 

Hier ist das Ergebnis von dput[head(books)):

structure(list(BookId = c("BOOKXXXX774051532082", "BOOKXXXX776514515608", 
    "BOOKXXXX776287821289", "BOOKXXXX776514515608", "BOOKXXXX774051532082", 
    "BOOKXXXX774051532082"), V2 = c("ZUSRXXXX8429", 
    "ZUSRXXXX371255229634", 
    "ZUSRXXXX656080986411", "ZUSRXXXX371255229634", "ZUSRXXXX8429", 
    "ZUSRXXXX8429"), 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)) 

Hier ist ein Beispiel meiner "echte" Dateneingabe:

BOOKXXXX774051532082 ZUSRXXXX8429BOOKEVTX776757835463 ZACTIONX215229995154        
BOOKXXXX776514515608 ZUSRXXXX371255229634 BOOKEVTX776762775464 ZACTIONX533300043134        
BOOKXXXX776287821289 ZUSRXXXX656080986411 BOOKEVTX776772854465 ZACTIONX533300043134        
BOOKXXXX776514515608 ZUSRXXXX371255229634 BOOKEVTX776773643466 ZACTIONX533300043134        
BOOKXXXX774051532082 ZUSRXXXX8429MAILOUTX776774376684      
BOOKXXXX774051532082 ZUSRXXXX8429BOOKEVTX776995487467 ZACTIONX215229995154        
BOOKXXXX776287821289 ZUSRXXXX656080986411 BOOKEVTX777107387468 ZACTIONX533300043134  

und die erwartete Ausgabe

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

Können Sie 'dput (Kopf (Bücher))' –

+3

Bitte geben Sie genaue reproduzierbare Eingabe und die volle entsprechende Ausgabe. Die Lösung für Ihr Problem ist nicht die Parallelisierung, sondern das Schreiben von effizienterem R-Code. – Roland

+0

@Airmoi, bitte fügen Sie diese Ausgabe zu Ihrem Beitrag hinzu, nicht in den Kommentaren. Verwenden Sie die Schaltfläche "Bearbeiten" –

Antwort

1

Die OP hat zwei collapse Operationen angefordert:

  1. Für jede Zeile alle Spalten kollabieren (außer der ID-Spalte zkf_BOOK) in ein Datenfeld von | getrennt.
  2. Für jede zkf_BOOK Gruppe, kollabieren die Reihen von ; getrennt

innerhalb der Spalten Einstürzen durch einen Aufruf von Reduce() getan wird, während über die Reihen Kollabieren groupwise paste() Verwendung gemacht. Bei data.table sind die Spalten im Parameter by = in den Vorgängen .SD nicht enthalten.

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: ZUSRXXXX8429|BOOKEVTX776757835463|ZACTIONX215229995154|||||||;ZUSRXXXX8429||||MAILOUTX776774376684|||||;ZUSRXXXX8429|BOOKEVTX776995487467|ZACTIONX215229995154||||||| 
2:             ZUSRXXXX371255229634|BOOKEVTX776762775464|ZACTIONX533300043134|||||||;ZUSRXXXX371255229634|BOOKEVTX776773643466|ZACTIONX533300043134||||||| 
3:                               ZUSRXXXX656080986411|BOOKEVTX776772854465|ZACTIONX533300043134||||||| 

Hinweis, dass die Diskrepanz zu dem erwarteten Ergebnis, um dput[head(books)) wegen nur 6 Zeilen, während der gedruckten Datenein- und erwartete Ausgangs auf 7 Reihen zurückzukehr basieren (oder mehr).

+0

Awesome .. Ich war so weit weg von dieser intelligenten Lösung ... – Airmoi

Verwandte Themen