Jeśli ktoś chce wypełnić brakujące wartości zmiennej opartej na poprzedniej/posterior non NA obserwacji w grupie, komenda data.table jestuzupełnienie brakujących wartości przez grupę w 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]]
który jest dość skomplikowany. To wstyd, ponieważ roll
jest bardzo szybki i mocny opcja (szczególnie w porównaniu z zastosowaniem funkcji, takich jak zoo::na.locf
w każdej grupie)
mogę napisać funkcję wygody wypełnienie brakujących wartości
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])
}
}
a następnie Napisać
setkey(DT,id, date)
DT[, value_filled_in := fill_na(value, by = id)]
to naprawdę nie jest satysfakcjonująca, ponieważ chciałoby się napisać
setkey(DT,id, date)
DT[, value_filled_in := fill_na(value), by = id]
To jednak wymaga dużej ilości czasu. Dla użytkownika końcowego trudno jest się dowiedzieć, że należy zadzwonić pod numer fill_na
za pomocą opcji by
i nie należy go używać z data.table
by
. Czy istnieje wokół tego eleganckie rozwiązanie?
Niektóre Test prędkości
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
Dlaczego nie wystarczy użyć na.locf.default
? Nawet jeśli różnica prędkości nie jest tak naprawdę ważna, ta sama kwestia pojawia się w przypadku innych rodzajów komend data.table (tych, które polegają na scalaniu przez zmienną w "od") - szkoda, że systematycznie je ignoruje, aby uzyskać łatwiejsza składnia. Bardzo podobają mi się również wszystkie opcje roll.
jaki sposób 'rozwiązanie na.locf' porównać do tego rozwiązania w zakresie prędkości? – GSee
Czy owinięcie całej rzeczy (a la 'dplyr :: mutate') nie jest opcją? – shadowtalker
Byłoby pomocne, gdybyś podał kod [stworzyć przykładową tabelę danych] (http://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example), że możemy użyj, aby sprawdzić nasze wyniki i pomóc w benchmarkingu. – GSee