2016-07-15 30 views
4

Ich habe oft eine Notwendigkeit, einen Datenrahmen durch die zusätzliche von mehreren Spalten gleichzeitig mit einer benutzerdefinierten Funktion, vorzugsweise mit Parallelisierung zu mutieren. Im Folgenden sind die Möglichkeiten, wie ich das bereits mache.Möglichkeiten zum Hinzufügen mehrerer Spalten zu Datenrahmen mit plyr/dplyr/purrr

Setup-

library(dplyr) 
library(plyr) 
library(purrr) 
library(doMC) 
registerDoMC(2) 

df <- data.frame(x = rnorm(10), y = rnorm(10), z = rnorm(10)) 

Nehmen wir an, ich zwei neue Spalten wollen, foocol = x + y und barcol = (x + y) * 100, sondern dass diese tatsächlich komplex in einer benutzerdefinierten Funktion getan Berechnungen.

Methode 1: Spalten hinzufügen separat mit rowwise und mutate

foo <- function(x, y) return(x + y) 
bar <- function(x, y) return((x + y) * 100) 

df_out1 <- df %>% rowwise() %>% mutate(foocol = foo(x, y), barcol = bar(x, y)) 

Dies ist keine gute Lösung, da sie zwei Funktion für jede Zeile und zwei „teuer“ Berechnungen von x + y Anrufe erfordern. Es ist auch nicht parallelisiert.

Methode 2: Ich habe gerade Trick ddply in Betrieb zeilenweise

df2 <- df 
df2$id <- 1:nrow(df2) 

df_out2 <- ddply(df2, .(id), function(r) { 
    foocol <- r$x + r$y 
    barcol <- foocol * 100 
    return(cbind(r, foocol, barcol)) 
}, .parallel = T) 

I ddply Hier verleiten, eine Funktion in jeder Zeile durch Spalten auf einer einzigartigen id Spalte aufgerufen wird. Es ist jedoch klobig und benötigt eine nutzlose Spalte.

Methode 3: splat

foobar <- function(x, y, ...) { 
    foocol <- x + y 
    barcol <- foocol * 100 
    return(data.frame(x, y, ..., foocol, barcol)) 
} 

df_out3 <- splat(foobar)(df) 

Ich mag diese Lösung, da Sie die Spalten von df in der benutzerdefinierten Funktion verweisen können (was, wenn gewünscht anonym sein kann) ohne Array Verständnis. Diese Methode wird jedoch nicht parallelisiert.

Methode 4: by_row

df_out4 <- df %>% by_row(function(r) { 
    foocol <- r$x + r$y 
    barcol <- foocol * 100 
    return(data.frame(foocol = foocol, barcol = barcol)) 
}, .collate = "cols") 

Die by_row Funktion von purrr eliminiert die Notwendigkeit für die einzigartige id Spalte, aber dieser Vorgang nicht parallelisiert.

Methode 5: pmap_df

df_out5 <- pmap_df(df, foobar) 
# or equivalently... 
df_out5 <- df %>% pmap_df(foobar) 

Dies ist die beste Option, die ich gefunden habe. Die Funktionsfamilie pmap akzeptiert auch anonyme Funktionen, die auf die Argumente angewendet werden. Ich glaube, pmap_df konvertiert df zu einer Liste und zurück, so, vielleicht gibt es einen Leistungseinbruch.

Es ist auch ein bisschen nervig, dass ich alle Spalten referenzieren muss, die ich für die Berechnung in der Funktionsdefinition function(x, y, ...) anstelle von nur function(r) für das Zeilenobjekt verwenden.


Fehle ich irgendwelche guten oder besseren Optionen? Gibt es Bedenken bei den beschriebenen Methoden?

+2

Nach dem Laden von 'dplyr' sollte man' plyr' * wirklich nicht laden. Es gibt eine Warnung, die Sie hören sollten. – Gregor

+1

Sie könnten mit 'purrr :: invoke' und seinen Varianten, z. 'invoke (foobar, df)' (entspricht 'do.call (foobar, df)'). Wirklich ist es in der Regel eine gute Sache, wenn Sie referenzieren müssen, welche Spalten an welche Parameter übergeben werden, oder Sie können stillschweigend Fehler machen. – alistaire

+0

Wenn ich etwas nicht vermisse, benötigt Methode 1 nicht 'rowwise()'. –

Antwort

1

Wie wäre es mit data.table?

library(data.table) 

foo <- function(x, y) return(x + y) 
bar <- function(x, y) return((x + y) * 100) 

dt <- as.data.table(df) 

dt[, foocol:=foo(x,y)] 
dt[, barcol:=bar(x,y)] 

Die data.table Bibliothek ist sehr schnell und hat zumindest einige some Potenzial für Parallelisierung.