R用组内另一个列值的下一个值替换NA值
R Replacing NA values with the next value of another column value within groups
我有一个数据框如下
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 用户来说可能是简单的问题,但对我来说,它已经让我兴奋了几个小时,但没有找到好的解决方案。请帮我!!谢谢。
试试这个,
首先使用字符向量而不是因子构建 df。
然后将所有 NA 值放入 sapply
。
sapply
中的函数查找同一天发生的 NA 之后的下一个 time.IN。并将它们从原始 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>
这是一个可能的 dplyr
解决方案。这是ifelse
和lead
的组合,而由于使用ifelse
[=16导致信息丢失,最终产品应该再次转换为as.POSIXct
=]
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>
我们可以使用data.table
来做到这一点。在将 'data.frame' 转换为 'data.table' (setDT(tmpdf)
) 之后,我们将 factor
'time' 列转换为 character
class。在这里,我假设 NA
是真实的 NA
而不是字符串。按 'spaceNum' 分组,我们使用 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"
之外,我们还为 'time' 列设置了多种格式。即 2015 09-05 16:48
和 2015-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>
我有一个数据框如下
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 用户来说可能是简单的问题,但对我来说,它已经让我兴奋了几个小时,但没有找到好的解决方案。请帮我!!谢谢。
试试这个,
首先使用字符向量而不是因子构建 df。
然后将所有 NA 值放入 sapply
。
sapply
中的函数查找同一天发生的 NA 之后的下一个 time.IN。并将它们从原始 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>
这是一个可能的 dplyr
解决方案。这是ifelse
和lead
的组合,而由于使用ifelse
[=16导致信息丢失,最终产品应该再次转换为as.POSIXct
=]
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>
我们可以使用data.table
来做到这一点。在将 'data.frame' 转换为 'data.table' (setDT(tmpdf)
) 之后,我们将 factor
'time' 列转换为 character
class。在这里,我假设 NA
是真实的 NA
而不是字符串。按 'spaceNum' 分组,我们使用 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"
之外,我们还为 'time' 列设置了多种格式。即 2015 09-05 16:48
和 2015-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>