2014-10-03 20 views
11

Wenn man in fehlenden Werten einer Variablen bei früheren/posterior nicht NA Beobachtung innerhalb einer Gruppe auf Basis füllen möge, der data.table Befehlin fehlenden Werten durch Gruppe in data.table

setkey(DT,id,date) 
DT[, value_filled_in := DT[!is.na(value), list(id, date, value)][DT[, list(id, date)], value, roll = TRUE]] 

welche ist ziemlich komplex. Es ist schade, da roll ist eine sehr schnelle und leistungsfähige Option (va im Vergleich mit einer Funktion wie zoo::na.locf innerhalb jeder Gruppe der Anwendung)

ich eine Komfortfunktion in fehlenden Werten schreibe

fill_na <- function(x , by = NULL, roll =TRUE , rollends= if (roll=="nearest") c(TRUE,TRUE) 
      else if (roll>=0) c(FALSE,TRUE) 
      else c(TRUE,FALSE)){ 
    id <- seq_along(x) 
    if (is.null(by)){ 
     DT <- data.table("x" = x, "id" = id, key = "id") 
     return(DT[!is.na(x)][DT[, list(id)], x, roll = roll, rollends = rollends, allow.cartesian = TRUE]) 

    } else{ 
     DT <- data.table("x" = x, "by" = by, "id" = id, key = c("by", "id")) 
     return(DT[!is.na(x)][DT[, list(by, id)], x, roll = roll, rollends = rollends, allow.cartesian = TRUE]) 
    } 
    } 

Und dann zu füllen schreiben

setkey(DT,id, date) 
DT[, value_filled_in := fill_na(value, by = id)] 

Das ist nicht wirklich befriedigend ist, da man

setkey(DT,id, date) 
DT[, value_filled_in := fill_na(value), by = id] 
schreiben möchten

Dies dauert jedoch sehr viel Zeit. Und für den Endbenutzer ist es umständlich zu erfahren, dass fill_na mit der Option by aufgerufen werden sollte und nicht mit data.tableby verwendet werden sollte. Gibt es dafür eine elegante Lösung?

Einige Geschwindigkeitstest

N <- 2e6 
set.seed(1) 
DT <- data.table(
     date = sample(10, N, TRUE), 
      id = sample(1e5, N, TRUE), 
     value = sample(c(NA,1:5), N, TRUE), 
     value2 = sample(c(NA,1:5), N, TRUE)     
    ) 
setkey(DT,id,date) 
DT<- unique(DT) 

system.time(DT[, filled0 := DT[!is.na(value), list(id, date, value)][DT[, list(id, date)], value, roll = TRUE]]) 
#> user system elapsed 
#> 0.086 0.006 0.105 
system.time(DT[, filled1 := zoo::na.locf.default(value, na.rm = FALSE), by = id]) 
#> user system elapsed 
#> 5.235 0.016 5.274 
# (lower speed and no built in option like roll=integer or roll=nearest, rollend, etc) 
system.time(DT[, filled2 := fill_na(value, by = id)]) 
#> user system elapsed 
#> 0.194 0.019 0.221 
system.time(DT[, filled3 := fill_na(value), by = id]) 
#> user system elapsed 
#> 237.256 0.913 238.405 

Warum ich na.locf.default einfach nicht benutzen? Obwohl der Geschwindigkeitsunterschied nicht wirklich wichtig ist, tritt das gleiche Problem für andere Arten von data.table-Befehlen auf (solche, die auf einer Zusammenführung durch die Variable in "by" beruhen) - es ist eine Schande, sie systematisch zu ignorieren, um einen zu erhalten leichtere Syntax Ich mag auch wirklich alle Rollenoptionen.

+2

Wie funktioniert die 'na.locf' Lösung zu dieser Lösung in Bezug auf Geschwindigkeit vergleichen? – GSee

+0

Ist das ganze Ding (a la 'dplyr :: mutate') nicht eine Option? – shadowtalker

+0

Es wäre hilfreich, wenn Sie Code zur Verfügung stellen würden, um [Beispieldaten.table] (http://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproductible-example) zu erstellen Verwenden Sie, um unsere Ergebnisse zu überprüfen und beim Benchmarking zu helfen. – GSee

Antwort

9

Hier ist ein wenig schneller und kompakte Weise es (Version 1.9.3+) zu tun:

DT[, filled4 := DT[!is.na(value)][DT, value, roll = T]] 
+0

Mein Fehler! Ich dachte, dass dies die gesamte Tabelle zweimal kopieren würde (einmal durch DT [! Is.na (Wert)] ', die andere durch' X [Y] '), was für einen typischen breiten Datensatz problematisch wäre. Ist das nicht der Fall (zumindest für 'DT [! Is.na (Wert)]')? – Matthew

+1

Ok. Unterspalten in Y ändert nichts. Es scheint jedoch '(DT [, gefüllte4: = DT [! Is.na (Wert), Liste (Datum, ID, Wert)] [DT, Wert, Rolle = T]]' ist schneller als Ihre Antwort in einem weiten Datenbank – Matthew

Verwandte Themen