Reemplace NA con el valor anterior o siguiente, por grupo, usando dplyr

Resuelto Tarak asked hace 54 años • 5 respuestas

Tengo un marco de datos que está organizado en orden descendente de fecha.

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') 
)

Deseo imputar (reemplazar) valores de NA con valores anteriores y agrupados por ID de usuario. En caso de que la primera fila de un ID de usuario tenga NA, reemplácela con el siguiente conjunto de valores para ese grupo de ID de usuario.

Estoy intentando utilizar paquetes dplyr y zoo algo como esto... pero no funciona

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

Necesito un resultado df como este:

                      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
Tarak avatar Jan 01 '70 08:01 Tarak
Aceptado
library(tidyr) #fill is part of tidyr

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

Lo que te da:

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
Rentrop avatar Oct 14 '2016 10:10 Rentrop

El uso zoo::na.locfdirecto en todo el marco de datos llenaría el NA independientemente de los userIDgrupos. Desafortunadamente, la agrupación del paquete dplyr no tiene ningún efecto en na.locfla función, por eso elegí una división:

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

Lo que hace es que primero divide los datos en 3 marcos de datos, luego aplico un primer paso de imputación (hacia abajo), luego hacia arriba con la función anónima en y lapply, finalmente, lo uso rbindpara volver a unir los marcos de datos. Tienes el resultado esperado.

agenis avatar Oct 14 '2016 10:10 agenis

Escribí esta función y definitivamente es más rápida que fill y probablemente más rápida 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))
}
Naja Bohanec avatar Mar 14 '2019 14:03 Naja Bohanec