2010-10-19 10 views
8

I eine data.frame habe (link to file) mit 18 Spalten und Zeilen, die 11520 I wie folgt transformieren:wie dieser R-Code beschleunigen

library(plyr) 
df.median<-ddply(data, .(groupname,starttime,fPhase,fCycle), 
       numcolwise(median), na.rm=TRUE) 

nach System.Time(), dauert es etwa Diese lang zum laufen:

user system elapsed 
    5.16 0.00 5.17 

Dieser Aufruf ist Teil einer Webanwendung, so dass die Laufzeit ziemlich wichtig ist. Gibt es eine Möglichkeit, diesen Aufruf zu beschleunigen?

+2

Können Sie die Ergebnisse zwischenspeichern? – Shane

+0

'ddply()' ist in erster Linie * praktisch *. Wenn Sie etwas schnell brauchen, müssen Sie möglicherweise die Logik neu implementieren. –

+0

@Shane: Es gibt derzeit 3 ​​* 400 mögliche Datensätze (und täglich steigend), die ein Benutzer anfordern könnte. Es ist unwahrscheinlich, dass ein Benutzer auf denselben Datensatz wie ein anderer trifft. Caching wäre also nur in einer Sitzung sinnvoll. Da die Ausgabe der Webanwendung im Wesentlichen ein vordefinierter Bericht ist, glaube ich nicht, dass der Benutzer sie normalerweise mehr als einmal anfordern würde. Würden Sie Caching für die von mir beschriebene Situation implementieren? Ich habe es noch nie gemacht, also bin ich etwas verloren. – dnagirl

Antwort

9

Gerade aggregate mit ist ein bisschen schneller ...

> groupVars <- c("groupname","starttime","fPhase","fCycle") 
> dataVars <- colnames(data)[ !(colnames(data) %in% c("location",groupVars)) ] 
> 
> system.time(ag.median <- aggregate(data[,dataVars], data[,groupVars], median)) 
    user system elapsed 
    1.89 0.00 1.89 
> system.time(df.median <- ddply(data, .(groupname,starttime,fPhase,fCycle), numcolwise(median), na.rm=TRUE)) 
    user system elapsed 
    5.06 0.00 5.06 
> 
> ag.median <- ag.median[ do.call(order, ag.median[,groupVars]), colnames(df.median)] 
> rownames(ag.median) <- 1:NROW(ag.median) 
> 
> identical(ag.median, df.median) 
[1] TRUE 
+0

'aggregat' behebt dieses Problem handlich. – dnagirl

7

nur einige der Punkte aus den Kommentaren zusammenfassen:

  1. Bevor Sie zu optimieren beginnen, sollten Sie einen Sinn für „akzeptabel“ Leistung. Abhängig von der erforderlichen Leistung können Sie dann genauer festlegen, wie der Code verbessert werden soll. Zum Beispiel müssten Sie bei einem bestimmten Schwellenwert aufhören, R zu verwenden und in eine kompilierte Sprache zu wechseln.
  2. Sobald Sie eine erwartete Laufzeit haben, können Sie Ihren vorhandenen Code profilieren, um mögliche Engpässe zu finden. R hat mehrere Mechanismen dafür, einschließlich Rprof (es gibt Beispiele für stackoverflow, wenn Sie search for [r] + rprof).
  3. plyr ist in erster Linie für die Benutzerfreundlichkeit konzipiert, nicht für die Leistung (obwohl die aktuelle Version einige nette Leistungsverbesserungen hatte). Einige der Basisfunktionen sind schneller, da sie weniger Overhead haben. @JDLong zeigte auf a nice thread, die einige dieser Probleme behandelt, einschließlich einiger spezieller Techniken von Hadley.
+0

Danke für die Zusammenfassung. Und danke an alle, die solch nützliche Informationen beigetragen haben. Ich habe viel zu lesen! – dnagirl

2

Nun, ich habe nur ein paar einfache Transformationen auf einem großen Datenrahmen (die Baseball-Daten, die im plyr Paket) mit dem Standard Bibliotheksfunktionen (zB 'table', 'tapply', 'aggregate', etc.) und die analoge plyr-Funktion - in jedem Fall fand ich plyr als wesentlich langsamer. ZB

> system.time(table(BB$year)) 
    user system elapsed 
    0.007 0.002 0.009 

> system.time(ddply(BB, .(year), 'nrow')) 
    user system elapsed 
    0.183 0.005 0.189 

Zweitens, und ich habe nicht untersuchen, ob diese Leistung in Ihrem Fall verbessern würde, aber für den Datenrahmen der Größe, die Sie arbeiten mit jetzt und größer, ich benutze die data.table Bibliothek, verfügbar auf CRAN. Es ist einfach data.table Objekte sowie zur Umwandlung noch vorhandenen data.frames zu data.tables erstellen - einfach anrufen data.table auf dem data.frame Sie konvertieren möchten:

dt1 = data.table(my_dataframe) 
3

zu Joshuas hinzufügen Lösung. Wenn Sie statt Median zu verwenden entscheiden bedeutet, können Sie die Berechnung noch 4 mal beschleunigen:

> system.time(ag.median <- aggregate(data[,dataVars], data[,groupVars], median)) 
    user system elapsed 
    3.472 0.020 3.615 
> system.time(ag.mean <- aggregate(data[,dataVars], data[,groupVars], mean)) 
    user system elapsed 
    0.936 0.008 1.006 
+1

sehr interessant! Ich werde mir das merken. Leider müssen diese Daten Mediane vergleichen. – dnagirl

4

Die Reihenfolge der Daten unabhängig davon, wann Sie Mediane sind Berechnung: Wenn die Daten in Ordnung sind vom kleinsten zum größten, dann ist die Berechnung ein bisschen schneller.

x <- 1:1e6 
y <- sample(x) 
system.time(for(i in 1:1e2) median(x)) 
    user system elapsed 
    3.47 0.33 3.80 

system.time(for(i in 1:1e2) median(y)) 
    user system elapsed 
    5.03 0.26 5.29 

Sortieren Sie für die neuen Datensätze die Daten beim Importieren in eine entsprechende Spalte. Bei vorhandenen Datensätzen können Sie diese als Stapeljob sortieren (außerhalb der Web-App).

2

mit diesen Daten zu arbeiten, ist wesentlich schneller mit dplyr:

library(dplyr) 

system.time({ 
    data %>% 
    group_by(groupname, starttime, fPhase, fCycle) %>% 
    summarise_each(funs(median(., na.rm = TRUE)), inadist:larct) 
}) 
#> user system elapsed 
#> 0.391 0.004 0.395 

(Sie dplyr 0.2 benötigen %>% und summarise_each zu bekommen)

Im Vergleich günstig plyr:

library(plyr) 
system.time({ 
    df.median <- ddply(data, .(groupname, starttime, fPhase, fCycle), 
    numcolwise(median), na.rm = TRUE) 
}) 
#> user system elapsed 
#> 0.991 0.004 0.996 

Und zu aggregate() (Code von @ joshua-ulrich)

groupVars <- c("groupname", "starttime", "fPhase", "fCycle") 
dataVars <- colnames(data)[ !(colnames(data) %in% c("location", groupVars))] 
system.time({ 
    ag.median <- aggregate(data[,dataVars], data[,groupVars], median) 
}) 
#> user system elapsed 
#> 0.532 0.005 0.537