使用dplyr按组将NA替换为上一个或下一个值

使用dplyr按组将NA替换为上一个或下一个值,r,dplyr,missing-data,zoo,R,Dplyr,Missing Data,Zoo,我有一个按日期降序排列的数据框 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,

我有一个按日期降序排列的数据框

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') 
)
我希望用以前的值插补(替换)NA值 并按用户ID分组 如果userID的第一行有NA,则替换为该userID组的下一组值

我正在尝试使用dplyr和zoo软件包之类的东西…但它不起作用

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

直接在整个data.frame上使用
zoo::na.locf
将填充na,而不考虑
userID
组。不幸的是,包dplyr的分组对
na.locf
函数没有影响,这就是我选择拆分的原因:

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

它首先将数据拆分为3个data.frames,然后应用第一次插补(向下),然后使用
lappy
中的匿名函数向上,最后使用
rbind
将data.frames重新组合在一起。您已经获得了预期的输出

直接在整个data.frame上使用
zoo::na.locf
将填充na,而不考虑
userID
组。不幸的是,包dplyr的分组对
na.locf
函数没有影响,这就是我选择拆分的原因:

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
require(tidyverse) #fill is part of tidyr

ps1 %>% 
  group_by(userID) %>% 
  fill(color, age, gender) %>% #default direction down
  fill(color, age, gender, .direction = "up")
它首先将数据拆分为3个data.frames,然后应用第一次插补(向下),然后使用
lappy
中的匿名函数向上,最后使用
rbind
将data.frames重新组合在一起。您已经获得了预期的输出

require(tidyverse) #fill is part of tidyr

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

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
来源:本地数据帧[9 x 4]
组:userID[3]
用户标识颜色年龄性别
1 21蓝色3年F
2 21蓝色2年F
3 21红色2岁
4 22蓝色3岁F
5 22蓝色3岁F
6 22蓝色3岁F
7 23红色4yrs F
8 23红色4yrs F
9 23黄金4年F
这给了你:

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
来源:本地数据帧[9 x 4]
组:userID[3]
用户标识颜色年龄性别
1 21蓝色3年F
2 21蓝色2年F
3 21红色2岁
4 22蓝色3岁F
5 22蓝色3岁F
6 22蓝色3岁F
7 23红色4yrs F
8 23红色4yrs F
9 23黄金4年F

使用@agenis方法与
na.locf()
结合使用
purr
,您可以执行以下操作:

library(purrr)
library(zoo)

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

将@agenis方法与
na.locf()
结合使用
purr
,可以执行以下操作:

library(purrr)
library(zoo)

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

我写了这个函数,它肯定比fill快,可能比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))
}

fill\u NA我编写了这个函数,它肯定比fill快,可能比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))
}

几年后,我发现事情发生了变化。 使用@Steven Beaupré的方法

1) 添加
na.rm=F
可确保不删除/排除任何行。 2) 可以在
purrlyr
软件包中找到
slide\u rows()
功能

library(purrrlyr)
library(zoo)

ps1 %>% 
  slice_rows("userID") %>% 
  by_slice(function(x) { 
    na.locf(na.locf(x, na.rm=F), fromLast=T, na.rm=F) }, 
    .collate = "rows") 

几年后,我发现事情发生了变化。 使用@Steven Beaupré的方法

1) 添加
na.rm=F
可确保不删除/排除任何行。 2) 可以在
purrlyr
软件包中找到
slide\u rows()
功能

library(purrrlyr)
library(zoo)

ps1 %>% 
  slice_rows("userID") %>% 
  by_slice(function(x) { 
    na.locf(na.locf(x, na.rm=F), fromLast=T, na.rm=F) }, 
    .collate = "rows") 

看看这是否有用。或者看看这是否有用。或者,您可以使用更惯用的
bind_rows()
split(ps1$userID)
替换
do.call()
,使用
purr
的另一种替代方法也可以是:
library(purr);ps1%>%slice_rows(“userID”)%%>%by_slice(函数(x){na.locf(na.locf(x),fromLast=T)}、.collate=“rows”)
@stevenbauprénice!这本身就应该有一个新的答案;-)您可以将
do.call()
替换为更惯用的
bind_rows()
split(ps1$userID)
替换为
split(.userID)
使用
purr
的另一种替代方法也可以是:
library(purr);ps1%>%slice_rows(“userID”)%%>%by_slice(函数(x){na.locf(na.locf(x),fromLast=T)}、.collate=“rows”)
@stevenbauprénice!这本身就应该有一个新的答案;-)此后,
fill()
函数被更新为允许同时向两个方向填充,而无需使用两次。
.direction
选项现在包括
downup
updown
。如果您的数据集很大,并且无法写入每个列的名称,该怎么办?
fill()
函数已经更新,允许同时在两个方向上填充,而不需要使用两次。
.direction
选项现在包括
downup
updown
。如果您的数据集很大,并且无法写入每个列的名称,该怎么办?感谢您共享独立于分组变量的代码(我的数据中没有分组变量)并且不需要安装zoo软件包。除了我正在使用tidyr之外,我发现tidyr::fill使用管道可以更优雅地完成工作,而不需要使用mutate()。感谢您共享独立于分组变量(我的数据中没有)的代码而且不需要安装zoo软件包。除了我正在使用tidyr之外,我发现tidyr::fill使用管道可以更优雅地完成工作,而不需要使用mutate()。