Se si vuole inserire i valori di una variabile basata sull'osservazione NA non precedente/posteriore all'interno di un gruppo mancante, il comando data.table èCompilare i valori mancanti, per gruppo, 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]]
che è abbastanza complesso E 'un peccato perché roll
è un'opzione molto veloce e potente (specialmente in confronto con l'applicazione di una funzione come zoo::na.locf
all'interno di ciascun gruppo)
posso scrivere una funzione di convenienza per riempire i valori mancanti
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])
}
}
E poi scrivere
setkey(DT,id, date)
DT[, value_filled_in := fill_na(value, by = id)]
Questo non è davvero soddisfacente in quanto si vorrebbe scrivere
setkey(DT,id, date)
DT[, value_filled_in := fill_na(value), by = id]
Tuttavia, questa operazione richiede molto tempo. E, per l'utente finale, è improbabile sapere che fill_na
deve essere chiamato con l'opzione by
e non deve essere utilizzato con data.table
by
. C'è una soluzione elegante intorno a questo?
Alcuni test di velocità
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
Perché non mi basta usare na.locf.default
? Anche se la differenza di velocità non è veramente importante, lo stesso problema si pone per altri tipi di comandi data.table (quelli che si basano su un'unione con la variabile in "by") - è un peccato ignorarli sistematicamente per ottenere un sintassi più semplice. Mi piacciono molto anche tutte le opzioni di lancio.
Come funziona la soluzione 'na.locf' confronta con questa soluzione in termini di velocità? – GSee
È l'involucro dell'intera cosa (a la 'dplyr :: mutate') non un'opzione? – shadowtalker
Sarebbe utile se fornissi il codice per [creare un sample data.table] (http://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example) che potremmo utilizzare per verificare i nostri risultati e per aiutare con il benchmarking. – GSee