R使用组中另一列值的下一个值替换NA值

时间:2015-09-07 11:45:02

标签: r data.table dplyr

我有一个数据框如下

tmpdf <- data.frame(spaceNum=c(1,1,1,2,2,2,2), time.IN=c("2015-09-04 16:30", "2015-09-04 19:50", "2015-09-04 21:00", "2015-09-05 12:00", "2015 09-05 13:00", "2015 09-05 16:00", "2015 09-05 17:00"), time.OUT= c("2015-09-04 18:00", "NA", "NA","NA", "2015-09-05 13:21", "2015 09-05 16:48", "NA"))

> tmpdf
  spaceNum          time.IN         time.OUT
1        1 2015-09-04 16:30 2015-09-04 18:00
2        1 2015-09-04 19:50               NA
3        1 2015-09-04 21:00               NA
4        2 2015-09-05 12:00               NA
5        2 2015 09-05 13:00 2015-09-05 13:21
6        2 2015 09-05 16:00 2015 09-05 16:48
7        2 2015 09-05 17:00               NA
> 

我想将time.OUT的NA值替换为time.IN的下一行值,该值不是NULL且位于同一spaceNum组内。即,以下是我期望的结果。

 spaceNum          time.IN         time.OUT
    1        1 2015-09-04 16:30 2015-09-04 18:00
    2        1 2015-09-04 19:50 2015-09-04 21:00
    3        1 2015-09-04 21:00               NA
    4        2 2015-09-05 12:00 2015-09-05 13:00
    5        2 2015 09-05 13:00 2015-09-05 13:21
    6        2 2015 09-05 16:00 2015 09-05 16:48
    7        2 2015 09-05 17:00               NA

似乎dplyr或data.table可以做到这一点,我已经搜索过以前的问题但是找不到合适的方法。

对于专家R用户来说,这可能是一个简单的问题,但对我而言,它已经让我好几个小时了,并没有找到好的解决方案。请帮我!!感谢。

3 个答案:

答案 0 :(得分:4)

这是一个可能的dplyr解决方案。这是ifelselead的组合,而由于使用as.POSIXct ifelse >

library(dplyr)
tmpdf %>%
  group_by(spaceNum) %>%
  mutate(time.OUT = as.POSIXct(ifelse(is.na(time.OUT), lead(time.IN), time.OUT), origin = "1970-01-01"))
# Source: local data frame [7 x 3]
# Groups: spaceNum
# 
#   spaceNum             time.IN            time.OUT
# 1        1 2015-09-04 16:30:00 2015-09-04 18:00:00
# 2        1 2015-09-04 19:50:00 2015-09-04 21:00:00
# 3        1 2015-09-04 21:00:00                <NA>
# 4        2 2015-09-05 12:00:00 2015-09-05 13:00:00
# 5        2 2015-09-05 13:00:00 2015-09-05 13:21:00
# 6        2 2015-09-05 16:00:00 2015-09-05 16:48:00
# 7        2 2015-09-05 17:00:00                <NA>

答案 1 :(得分:2)

我们可以使用data.table来执行此操作。我们将'data.frame'转换为'data.table'(factor)后,将character'时间'列转换为setDT(tmpdf)类。在这里,我假设NA s是真实的NA而不是字符串。通过'spaceNum'分组,我们使用devel版本的data.table中的shift创建一个新列'v1'。将'time.OUT'值分配给'time.out'中与NA元素对应的'v1'。我们还可以将不需要的列分配给“NULL”,即“v1”

library(data.table)#v1.9.5+
setDT(tmpdf)[, (2:3) :=lapply(.SD, as.character), .SDcols=2:3]
tmpdf[, v1:=shift(time.IN, type='lead'), spaceNum][is.na(time.OUT), 
                   time.OUT:= v1][, v1:= NULL]
tmpdf
#   spaceNum          time.IN         time.OUT
#1:        1 2015-09-04 16:30 2015-09-04 18:00
#2:        1 2015-09-04 19:50 2015-09-04 21:00
#3:        1 2015-09-04 21:00               NA
#4:        2 2015-09-05 12:00 2015 09-05 13:00
#5:        2 2015 09-05 13:00 2015-09-05 13:21
#6:        2 2015 09-05 16:00 2015 09-05 16:48
#7:        2 2015 09-05 17:00               NA

注意到在示例数据集中,除了字符"NA"之外,我们还有“时间”列的多种格式。即2015 09-05 16:482015-09-05 13:21。如果我们需要转换为POSIXct,我们可以使用library(lubridate),因为它可以采用多种格式。

library(lubridate)
tmpdf[, (2:3) := lapply(.SD, ymd_hm), .SDcols=2:3]
tmpdf
#  spaceNum             time.IN            time.OUT
#1:        1 2015-09-04 16:30:00 2015-09-04 18:00:00
#2:        1 2015-09-04 19:50:00 2015-09-04 21:00:00
#3:        1 2015-09-04 21:00:00                <NA>
#4:        2 2015-09-05 12:00:00 2015-09-05 13:00:00
#5:        2 2015-09-05 13:00:00 2015-09-05 13:21:00
#6:        2 2015-09-05 16:00:00 2015-09-05 16:48:00
#7:        2 2015-09-05 17:00:00                <NA>

答案 2 :(得分:1)

试试这个,

首先使用字符向量而不是因子构建df。 然后将所有NA值放在sapply中。 sapply中的函数找到NA之后的同一天发生的下一次。并从原来的df拉出它们。最后将它们分配给df的NA值。

tmpdf <- data.frame(spaceNum=c(1,1,1,2,2,2,2), 
time.IN=c("2015-09-04 16:30", "2015-09-04 19:50", "2015-09-04 21:00", "2015-09-05 12:00", "2015 09-05 13:00", "2015 09-05 16:00", "2015 09-05 17:00"), 
time.OUT= c("2015-09-04 18:00", NA, NA,NA, "2015-09-05 13:21", "2015 09-05 16:48", NA),stringsAsFactors = F)

tmp<-tmpdf[unlist(
             sapply(which(is.na(tmpdf[,3])),function(x){
               if(tmpdf[x,1]==tmpdf[x+1,1] && !is.na(tmpdf[x,1]==tmpdf[x+1,1])) x+1 
               else NA
               })), 2]

tmpdf[which(is.na(tmpdf[,3])),3]<-tmp

> tmpdf
  spaceNum          time.IN         time.OUT
1        1 2015-09-04 16:30 2015-09-04 18:00
2        1 2015-09-04 19:50 2015-09-04 21:00
3        1 2015-09-04 21:00             <NA>
4        2 2015-09-05 12:00 2015 09-05 13:00
5        2 2015 09-05 13:00 2015-09-05 13:21
6        2 2015 09-05 16:00 2015 09-05 16:48
7        2 2015 09-05 17:00             <NA>