Reemplace NA con el valor anterior o siguiente, por grupo, usando dplyr
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
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
El uso zoo::na.locf
directo en todo el marco de datos llenaría el NA independientemente de los userID
grupos. Desafortunadamente, la agrupación del paquete dplyr no tiene ningún efecto en na.locf
la 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 rbind
para volver a unir los marcos de datos. Tienes el resultado esperado.
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))
}