2013-09-26 8 views
9

Ich partitioniere einen Datenrahmen mit split(), um parLapply() zu verwenden, um eine Funktion auf jeder Partition parallel aufzurufen. Der Datenrahmen hat 1,3 Millionen Zeilen und 20 Spalten. Ich teile/partitioniere um zwei Spalten, beide Zeichen. Sieht so aus, als gäbe es ~ 47K eindeutige IDs und ~ 12K eindeutige Codes, aber nicht jede Paarung von ID und Code ist identisch. Die resultierende Anzahl von Partitionen beträgt ~ 250K. Hier ist die split() Linie:Schnelle Alternative zum Aufteilen in R

system.time(pop_part <- split(pop, list(pop$ID, pop$code))) 

Die Partitionen werden dann in parLapply() zugeführt werden, wie folgt:

cl <- makeCluster(detectCores()) 
system.time(par_pop <- parLapply(cl, pop_part, func)) 
stopCluster(cl) 

lasse ich habe den split() Code alleine laufen fast eine Stunde und es wird nicht abgeschlossen. Ich kann durch die ID allein teilen, die ~ 10 Minuten dauert. Darüber hinaus verbrauchen R Studio und die Worker-Threads ca. 6 GB RAM.

Der Grund, warum ich die resultierende Anzahl von Partitionen kenne, ist der entsprechende Code in Pentaho Data Integration (PDI), der in 30 Sekunden läuft (für das gesamte Programm, nicht nur für den "Split" -Code). Ich hoffe nicht auf diese Art von Leistung mit R, aber etwas, das vielleicht in 10 - 15 Minuten im schlimmsten Fall abgeschlossen wird.

Die Hauptfrage: Gibt es eine bessere Alternative zu teilen? Ich habe auch versucht ddply() mit .parallel = TRUE, aber es lief auch über eine Stunde und nie abgeschlossen.

Antwort

9

Split Indizes in pop

idx <- split(seq_len(nrow(pop)), list(pop$ID, pop$code)) 

Split ist nicht langsam, zB

> system.time(split(seq_len(1300000), sample(250000, 1300000, TRUE))) 
    user system elapsed 
    1.056 0.000 1.058 

also, wenn dein ist Ich denke, einige Aspekte Ihrer Daten gibt es, die Dinge nach unten, zum Beispiel ID verlangsamt und code sind beide Faktoren mit vielen Ebenen und so ihre vollständige Interaktion, anstatt die Ebene Kombinationen in Ihrem Datensatz angezeigt werden, berechnet

> length(split(1:10, list(factor(1:10), factor(10:1)))) 
[1] 100 
> length(split(1:10, paste(letters[1:10], letters[1:10], sep="-"))) 
[1] 10 

oder vielleicht haben Sie nicht mehr genügend Arbeitsspeicher.

Verwenden Sie mclapply anstelle von parLapply, wenn Sie Prozesse auf einem Nicht-Windows-Rechner verwenden (was vermutlich der Fall ist, seit Sie nach detectCores() fragen).

par_pop <- mclapply(idx, function(i, pop, fun) fun(pop[i,]), pop, func) 

Konzeptionell klingt es wie Sie wirklich für pvec Ziel sind (eine vektorisiert Berechnung über Prozessoren verteilen), anstatt mclapply (Iterierte über einzelne Zeilen in Ihrem Datenrahmen).

Auch, und wirklich als der erste Schritt, in Betracht ziehen, die Flaschenhälse in func zu identifizieren; Die Daten sind groß, aber nicht so groß, vielleicht ist eine parallele Auswertung nicht nötig - vielleicht hast du PDI-Code statt R-Code geschrieben? Achten Sie auf Datentypen im Datenrahmen, z. B. Faktor gegenüber Zeichen. Es ist nicht ungewöhnlich, eine 100-fache Beschleunigung zwischen schlecht geschriebenem und effizientem R-Code zu erreichen, während die parallele Auswertung bestenfalls proportional zur Anzahl der Kerne ist.

+0

Danke, ich werde es versuchen. Ha, ich habe den R-Code ursprünglich geschrieben und dann nach PDI portiert (ich habe mehr Erfahrung mit R als PDI). – argoneus

+0

Ich habe den 'split()' Code, den du gepostet hast, ausgeführt und fast eine Stunde gewartet, aber er wurde nie beendet. – argoneus

+0

Ein paar zusätzliche Vorschläge hinzugefügt um Split, die in der Größenordnung von einer Sekunde oder weniger dauern sollte. Vielleicht verursachen Faktoren auch, dass die Funktion langsam ist? –

2

Split (x, f) langsam ist, wenn x eine Menge verschiedenen Elemente ein Faktor und F enthält, ist

Also, dieser Code, wenn schnell:

system.time(split(seq_len(1300000), sample(250000, 1300000, TRUE))) 

Aber dies ist sehr langsam:

system.time(split(factor(seq_len(1300000)), sample(250000, 1300000, TRUE))) 

Und das ist schnell wieder, weil es nur 25 Gruppen

system.time(split(factor(seq_len(1300000)), sample(25, 1300000, TRUE)))