En un data.frame (o data.table), me gustaría "completar" NA con el valor anterior que no sea NA más cercano. Un ejemplo simple, el uso de vectores (en lugar de a data.frame
) es el siguiente:
> y <- c(NA, 2, 2, NA, NA, 3, NA, 4, NA, NA)
Me gustaría una función fill.NAs()
que me permita construir de yy
manera que:
> yy
[1] NA NA NA 2 2 2 2 3 3 3 4 4
Necesito repetir esta operación para muchos (total ~ 1 Tb) de tamaño pequeño data.frame
s (~ 30-50 Mb), donde una fila es NA donde están todas sus entradas. ¿Cuál es una buena manera de abordar el problema?
La solución fea que preparé usa esta función:
last <- function (x){
x[length(x)]
}
fill.NAs <- function(isNA){
if (isNA[1] == 1) {
isNA[1:max({which(isNA==0)[1]-1},1)] <- 0 # first is NAs
# can't be forward filled
}
isNA.neg <- isNA.pos <- isNA.diff <- diff(isNA)
isNA.pos[isNA.diff < 0] <- 0
isNA.neg[isNA.diff > 0] <- 0
which.isNA.neg <- which(as.logical(isNA.neg))
if (length(which.isNA.neg)==0) return(NULL) # generates warnings later, but works
which.isNA.pos <- which(as.logical(isNA.pos))
which.isNA <- which(as.logical(isNA))
if (length(which.isNA.neg)==length(which.isNA.pos)){
replacement <- rep(which.isNA.pos[2:length(which.isNA.neg)],
which.isNA.neg[2:max(length(which.isNA.neg)-1,2)] -
which.isNA.pos[1:max(length(which.isNA.neg)-1,1)])
replacement <- c(replacement, rep(last(which.isNA.pos), last(which.isNA) - last(which.isNA.pos)))
} else {
replacement <- rep(which.isNA.pos[1:length(which.isNA.neg)], which.isNA.neg - which.isNA.pos[1:length(which.isNA.neg)])
replacement <- c(replacement, rep(last(which.isNA.pos), last(which.isNA) - last(which.isNA.pos)))
}
replacement
}
La función fill.NAs
se usa de la siguiente manera:
y <- c(NA, 2, 2, NA, NA, 3, NA, 4, NA, NA)
isNA <- as.numeric(is.na(y))
replacement <- fill.NAs(isNA)
if (length(replacement)){
which.isNA <- which(as.logical(isNA))
to.replace <- which.isNA[which(isNA==0)[1]:length(which.isNA)]
y[to.replace] <- y[replacement]
}
Salida
> y
[1] NA 2 2 2 2 3 3 3 4 4 4
... que parece funcionar. Pero, hombre, ¿es feo! ¿Alguna sugerencia?
r
data.table
zoo
r-faq
Ryogi
fuente
fuente
roll=TRUE
endata.table
.fill
enR
tidyr::fill()
.Respuestas:
Probablemente quiera usar la
na.locf()
función del paquete del zoológico para llevar la última observación hacia adelante para reemplazar sus valores de NA.Aquí está el comienzo de su ejemplo de uso de la página de ayuda:
fuente
na.locf
en cuenta que en zoo funciona con vectores ordinarios, así como con objetos de zoo. Suna.rm
argumento puede ser útil en algunas aplicaciones.na.locf(cz, na.rm=FALSE)
para seguir liderandoNA
.Perdón por desenterrar una vieja pregunta. No pude buscar la función para hacer este trabajo en el tren, así que escribí uno yo mismo.
Me enorgulleció descubrir que es un poco más rápido.
Sin embargo, es menos flexible.
Pero juega bien
ave
, que es lo que necesitaba.Editar
Como esta se convirtió en mi respuesta más votada, a menudo me recordó que no uso mi propia función, porque a menudo necesito el uso de zoológicos.
maxgap
argumento . Debido a que el zoológico tiene algunos problemas extraños en casos extremos cuando uso dplyr + fechas que no pude depurar, volví a esto hoy para mejorar mi antigua función.Comparé mi función mejorada y todas las demás entradas aquí. Para el conjunto básico de características,
tidyr::fill
es más rápido y no falla en los casos límite. La entrada Rcpp de @BrandonBertelsen es aún más rápida, pero es inflexible con respecto al tipo de entrada (probó los casos límite incorrectamente debido a un malentendidoall.equal
).Si necesitas
maxgap
, mi función a continuación es más rápida que la del zoológico (y no tiene los problemas raros con las fechas).Pongo la documentación de mis pruebas .
nueva función
También puse la función en mi paquete formr (solo Github).
fuente
df
con varias columnas.na.locf0
que ahora es similar en alcance y rendimiento a surepeat_last
función. La clave era usar endiff
lugar decumsum
evitarifelse
. La principalna.locf.default
función es todavía algo más lento, ya que hace algunos más controles y las manijas varias columnas, etcuna
data.table
solucion:este enfoque también podría funcionar con relleno hacia adelante ceros:
Este método se vuelve muy útil en los datos a escala y en los que desea realizar un relleno hacia adelante por grupo (s), lo cual es trivial
data.table
. simplemente agregue los grupos a laby
cláusula antes de lacumsum
lógica.fuente
Al tratar con un gran volumen de datos, para ser más eficientes, podemos usar el paquete data.table.
fuente
replaceNaWithLatest <- function( dfIn, nameColsNa = names(dfIn)[1] ){ dtTest <- data.table(dfIn) invisible(lapply(nameColsNa, function(nameColNa){ setnames(dtTest, nameColNa, "colNa") dtTest[, segment := cumsum(!is.na(colNa))] dtTest[, colNa := colNa[1], by = "segment"] dtTest[, segment := NULL] setnames(dtTest, "colNa", nameColNa) })) return(dtTest) }
Tirando mi sombrero
Configure una muestra básica y un punto de referencia:
Y ejecuta algunos puntos de referencia:
Por si acaso:
Actualizar
Para un vector numérico, la función es un poco diferente:
fuente
Esto me ha funcionado:
la velocidad también es razonable:
fuente
replace_na_with_last(c(NA,1:4,NA))
(es decir, se rellenan con el siguiente valor). Este es también el comportamiento predeterminado deimputeTS::na.locf(x, na.remaining = "rev")
.replace_na_with_last<-function(x,p=is.na,d=0)c(d,x)[cummax(seq_along(x)*(!p(x)))+1]
Prueba esta función. No requiere el paquete ZOO:
Ejemplo:
fuente
if (!anyNA(x)) return(x)
.Tener un inicio
NA
es un poco arrugado, pero encuentro una forma muy legible (y vectorizada) de hacer LOCF cuando no falta el término principal :na.omit(y)[cumsum(!is.na(y))]
Una modificación ligeramente menos legible funciona en general:
c(NA, na.omit(y))[cumsum(!is.na(y))+1]
da la salida deseada:
c(NA, 2, 2, 2, 2, 3, 3, 4, 4, 4)
fuente
Puede usar la
data.table
funciónnafill
, disponible endata.table >= 1.12.3
.Si su vector es una columna en a
data.table
, también puede actualizarlo por referencia consetnafill
:Si tienes
NA
en varias columnas ...... puedes llenarlos por referencia de una vez:
Tenga en cuenta que:
Lo más probable es que la funcionalidad se extienda pronto; vea el tema abierto nafill, setnafill para caracteres, factores y otros tipos , donde también encontrará una solución temporal .
fuente
El paquete tidyverse propone una forma simple de hacer eso:
fuente
Hay un montón de paquetes que ofrecen funciones
na.locf
(NA
última observación realizada):xts
-xts::na.locf
zoo
-zoo::na.locf
imputeTS
-imputeTS::na.locf
spacetime
-spacetime::na.locf
Y también otros paquetes donde esta función se nombra de manera diferente.
fuente
Seguimiento de las contribuciones Rcpp de Brandon Bertelsen Para mí, la versión NumericVector no funcionó: solo reemplazó la primera NA. Esto es porque el
ina
vector solo se evalúa una vez, al comienzo de la función.En cambio, uno puede adoptar el mismo enfoque exacto que para la función IntegerVector. Lo siguiente funcionó para mí:
En caso de que necesite una versión de CharacterVector, el mismo enfoque básico también funciona:
fuente
Aquí hay una modificación de la solución de @ AdamO. Este se ejecuta más rápido, ya que omite la
na.omit
función. Esto sobrescribirá losNA
valores en el vectory
(excepto losNA
s iniciales ).fuente
Intenté lo siguiente:
nullIdx obtiene el número de idx donde masterData $ RequiredColumn tiene un valor Nulo / NA. En la siguiente línea lo reemplazamos con el valor Idx-1 correspondiente, es decir, el último valor bueno antes de cada NULL / NA
fuente
1 NA NA
convierte en1 1 NA
. Además, creo queas.array()
es innecesario.Esto funcionó para mí, aunque no estoy seguro de si es más eficiente que otras sugerencias.
fuente
Reducir es un buen concepto de programación funcional que puede ser útil para tareas similares. Desafortunadamente en R es ~ 70 veces más lento que
repeat.before
en la respuesta anterior.fuente
Yo personalmente uso esta función. No sé qué tan rápido o lento es. Pero hace su trabajo sin tener que usar bibliotecas.
si desea aplicar esta función en un marco de datos, si su marco de datos se llama df, simplemente
fuente