web-dev-qa-db-fra.com

Remplacez NA par la valeur précédente ou suivante, par groupe, en utilisant dplyr

J'ai une trame de données qui est organisée par ordre décroissant de date.

ps1 = data.frame(userID = c(21,21,21,22,22,22,23,23,23), 
             color = c(NA,'blue','red','blue',NA,NA,'red',NA,'gold'), 
             age = c('3yrs','2yrs',NA,NA,'3yrs',NA,NA,'4yrs',NA), 
             gender = c('F',NA,'M',NA,NA,'F','F',NA,'F') 
)

Je souhaite imputer (remplacer) les valeurs NA par les valeurs précédentes et regroupées par userID. Dans le cas où la première ligne d'un userID a NA, remplacer par l'ensemble de valeurs suivant pour ce groupe userid.

J'essaie d'utiliser des paquets dplyr et Zoo quelque chose comme ça ... mais ça ne fonctionne pas

cleanedFUG <- filteredUserGroup %>%
 group_by(UserID) %>%
 mutate(Age1 = na.locf(Age), 
     Color1 = na.locf(Color), 
     Gender1 = na.locf(Gender) ) 

J'ai besoin d'un résultat df comme ceci:

                      userID color  age gender
                1     21  blue 3yrs      F
                2     21  blue 2yrs      F
                3     21   red 2yrs      M
                4     22  blue 3yrs      F
                5     22  blue 3yrs      F
                6     22  blue 3yrs      F
                7     23   red 4yrs      F
                8     23   red 4yrs      F
                9     23  gold 4yrs      F
23
Tarak
require(tidyverse) #fill is part of tidyr

ps1 %>% 
  group_by(userID) %>% 
  fill(color, age, gender) %>% #default direction down
  fill(color, age, gender, .direction = "up")

Ce qui vous donne:

Source: local data frame [9 x 4]
Groups: userID [3]

  userID  color    age gender
   <dbl> <fctr> <fctr> <fctr>
1     21   blue   3yrs      F
2     21   blue   2yrs      F
3     21    red   2yrs      M
4     22   blue   3yrs      F
5     22   blue   3yrs      F
6     22   blue   3yrs      F
7     23    red   4yrs      F
8     23    red   4yrs      F
9     23   gold   4yrs      F
38
Rentrop

En utilisant Zoo::na.locf directement sur l'ensemble de data.frame remplirait l'AN indépendamment des groupes userID. Le regroupement du paquet dplyr n'a malheureusement aucun effet sur na.locf fonction, c'est pourquoi j'ai opté pour un split:

library(dplyr); library(Zoo)
ps1 %>% split(ps1$userID) %>% 
  lapply(function(x) {na.locf(na.locf(x), fromLast=T)}) %>% 
  do.call(rbind, .)
####      userID color  age gender
#### 21.1     21  blue 3yrs      F
#### 21.2     21  blue 2yrs      F
#### 21.3     21   red 2yrs      M
#### 22.4     22  blue 3yrs      F
#### 22.5     22  blue 3yrs      F
#### 22.6     22  blue 3yrs      F
#### 23.7     23   red 4yrs      F
#### 23.8     23   red 4yrs      F
#### 23.9     23  gold 4yrs      F

Ce qu'il fait, c'est qu'il divise d'abord les données en 3 data.frames, puis j'applique un premier passage d'imputation (vers le bas), puis vers le haut avec la fonction anonyme dans lapply, et finalement j'utilise rbind pour rassembler les data.frames. Vous avez la sortie attendue.

4
agenis

En utilisant la méthode @agenis avec na.locf() combinée avec purrr, vous pouvez faire:

library(purrr)
library(Zoo)

ps1 %>% 
  slice_rows("userID") %>% 
  by_slice(function(x) { 
    na.locf(na.locf(x), fromLast=T) }, 
    .collate = "rows") 
3
Steven Beaupré

J'ai écrit cette fonction et elle est définitivement plus rapide que fill et probablement plus rapide que na.locf:

fill_NA <- function(x) {
  which.na <- c(which(!is.na(x)), length(x) + 1)
  values <- na.omit(x)

  if (which.na[1] != 1) {
    which.na <- c(1, which.na)
    values <- c(values[1], values)
  }

  diffs <- diff(which.na)
  return(rep(values, times = diffs))
}
0
Naja Bohanec