Dans un data.frame (ou data.table), je voudrais "remplir" les NA avec la valeur non NA précédente la plus proche. Un exemple simple, utilisant des vecteurs (au lieu de a data.frame
) est le suivant:
> y <- c(NA, 2, 2, NA, NA, 3, NA, 4, NA, NA)
Je voudrais une fonction fill.NAs()
qui me permette de construire yy
telle que:
> yy
[1] NA NA NA 2 2 2 2 3 3 3 4 4
Je dois répéter cette opération pour de nombreux s (au total ~ 1 To) de petite taille data.frame
(~ 30-50 Mb), où une ligne est NA est toutes ses entrées. Quelle est la bonne manière d'aborder le problème?
La solution laide que j'ai préparée utilise cette fonction:
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 fonction fill.NAs
est utilisée comme suit:
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]
}
Production
> y
[1] NA 2 2 2 2 3 3 3 4 4 4
... qui semble fonctionner. Mais, mec, c'est moche! Aucune suggestion?
r
data.table
zoo
r-faq
Ryogi
la source
la source
roll=TRUE
dansdata.table
.fill
dansR
tidyr::fill()
.Réponses:
Vous voudrez probablement utiliser la
na.locf()
fonction du paquet zoo pour reporter la dernière observation pour remplacer vos valeurs NA.Voici le début de son exemple d'utilisation à partir de la page d'aide:
la source
na.locf
dans le zoo fonctionne avec des vecteurs ordinaires ainsi que des objets de zoo. Sonna.rm
argument peut être utile dans certaines applications.na.locf(cz, na.rm=FALSE)
pour continuer à dirigerNA
.Désolé d'avoir déterré une vieille question. Je ne pouvais pas rechercher la fonction pour faire ce travail dans le train, alors j'en ai écrit une moi-même.
J'étais fier de découvrir que c'était un peu plus rapide.
C'est moins flexible cependant.
Mais ça joue bien avec
ave
, c'est ce dont j'avais besoin.Éditer
Comme cela est devenu ma réponse la plus positive, on m'a souvent rappelé que je n'utilise pas ma propre fonction, car j'ai souvent besoin de l'
maxgap
argument du zoo . Parce que zoo a des problèmes étranges dans les cas extrêmes lorsque j'utilise des dates dplyr + que je ne pouvais pas déboguer, j'y suis revenu aujourd'hui pour améliorer mon ancienne fonction.J'ai comparé ma fonction améliorée et toutes les autres entrées ici. Pour l'ensemble de base des fonctionnalités,
tidyr::fill
est le plus rapide tout en ne manquant pas non plus les cas extrêmes. L'entrée Rcpp de @BrandonBertelsen est encore plus rapide, mais elle est inflexible en ce qui concerne le type d'entrée (il a mal testé les cas de bord en raison d'un malentendu deall.equal
).Si vous avez besoin
maxgap
, ma fonction ci-dessous est plus rapide que zoo (et n'a pas les problèmes étranges avec les dates).J'ai mis en place la documentation de mes tests .
nouvelle fonction
J'ai également mis la fonction dans mon package formr (Github uniquement).
la source
df
avec plusieurs colonnes?na.locf0
qui est maintenant similaire en termes de portée et de performances à votrerepeat_last
fonction. L'indice était d'utiliserdiff
plutôt que d'cumsum
éviterifelse
. Lana.locf.default
fonction principale est encore un peu plus lente car elle effectue plus de vérifications et gère plusieurs colonnes, etc.une
data.table
solution:cette approche pourrait également fonctionner avec des zéros de remplissage avant:
cette méthode devient très utile sur les données à grande échelle et où vous voudriez effectuer un remplissage avant par groupe (s), ce qui est trivial avec
data.table
. ajoutez simplement le (s) groupe (s) à laby
clause avant lacumsum
logique.la source
Pour faire face à un gros volume de données, pour être plus efficace, nous pouvons utiliser le package data.table.
la source
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) }
Jeter mon chapeau dans:
Configurer un échantillon de base et un benchmark:
Et exécutez quelques benchmarks:
Au cas où:
Mettre à jour
Pour un vecteur numérique, la fonction est un peu différente:
la source
Cela a fonctionné pour moi:
la vitesse est également raisonnable:
la source
replace_na_with_last(c(NA,1:4,NA))
(c'est-à-dire qu'ils sont remplis avec la valeur suivante). C'est également le comportement par défaut 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]
Essayez cette fonction. Il ne nécessite pas le package ZOO:
Exemple:
la source
if (!anyNA(x)) return(x)
.Avoir un début
NA
est un peu ridicule, mais je trouve une façon très lisible (et vectorisée) de faire LOCF lorsque le terme principal n'est pas manquant est:na.omit(y)[cumsum(!is.na(y))]
Une modification légèrement moins lisible fonctionne en général:
c(NA, na.omit(y))[cumsum(!is.na(y))+1]
donne la sortie souhaitée:
c(NA, 2, 2, 2, 2, 3, 3, 4, 4, 4)
la source
Vous pouvez utiliser la
data.table
fonctionnafill
, disponible à partir dedata.table >= 1.12.3
.Si votre vecteur est une colonne dans a
data.table
, vous pouvez également le mettre à jour par référence avecsetnafill
:Si vous avez
NA
dans plusieurs colonnes ...... vous pouvez les remplir par référence en une seule fois:
Notez que:
La fonctionnalité sera très probablement bientôt étendue; voir le problème ouvert nafill, setnafill pour les caractères, les facteurs et d'autres types , où vous trouverez également une solution de contournement temporaire .
la source
Le package tidyverse propose un moyen simple de le faire:
la source
Il existe de nombreux packages offrant des fonctions
na.locf
(NA
Dernière observation reportée):xts
-xts::na.locf
zoo
-zoo::na.locf
imputeTS
-imputeTS::na.locf
spacetime
-spacetime::na.locf
Et aussi d'autres packages où cette fonction est nommée différemment.
la source
Suivi des contributions Rcpp de Brandon Bertelsen. Pour moi, la version NumericVector ne fonctionnait pas: elle ne remplaçait que le premier NA. En effet, le
ina
vecteur n'est évalué qu'une seule fois, au début de la fonction.Au lieu de cela, on peut adopter exactement la même approche que pour la fonction IntegerVector. Ce qui suit a fonctionné pour moi:
Si vous avez besoin d'une version CharacterVector, la même approche de base fonctionne également:
la source
Voici une modification de la solution de @ AdamO. Celui-ci fonctionne plus rapidement, car il contourne la
na.omit
fonction. Cela écrasera lesNA
valeurs du vecteury
(sauf pour lesNA
s en tête ).la source
J'ai essayé ci-dessous:
nullIdx obtient le numéro idx là où masterData $ RequiredColumn a une valeur Null / NA. Dans la ligne suivante, nous la remplaçons par la valeur Idx-1 correspondante, c'est-à-dire la dernière bonne valeur avant chaque NULL / NA
la source
1 NA NA
se transforme en1 1 NA
. De plus, je pense que ceas.array()
n'est pas nécessaire.Cela a fonctionné pour moi, même si je ne suis pas sûr que ce soit plus efficace que d'autres suggestions.
la source
Réduire est un joli concept de programmation fonctionnelle qui peut être utile pour des tâches similaires. Malheureusement, dans R, il est ~ 70 fois plus lent que
repeat.before
dans la réponse ci-dessus.la source
J'utilise personnellement cette fonction. Je ne sais pas à quel point c'est rapide ou lent. Mais il fait son travail sans avoir à utiliser de bibliothèques.
si vous souhaitez appliquer cette fonction dans un dataframe, si votre dataframe s'appelle df alors simplement
la source