在R中,我如何向下填充匹配一个变量但不匹配另一个变量的所有行

时间:2018-10-26 15:05:30

标签: r

我找不到其他答案,如果可以的话,我很抱歉,请指出正确的方向

我想测试一个大型数据集(因此没有循环),对于匹配一个变量的所有行(例如ID),然后测试第二个变量(例如Time)是否在2小时内。我想通过结合ID和时间来制作一个URN。

如果时间在2小时以内(小于或等于),我想使用与原始第一行相同的ID。

对于时差大于2小时的所有行,我想从此点开始创建新的URN。

使用数据可能更有意义:

ID      Time      URN             URN_whichIwanttomake  Index
hawk    09:05     hawk_09         hawk_09               1
hawk    09:10     hawk_09         hawk_09               2
hawk    10:00     hawk_10         hawk_09               3
hawk    11:00     hawk_11         hawk_09               4
hawk    15:00     hawk_15         hawk_15               5
hawk    16:00     hawk_16         hawk_15               6
eagle   12:00     eagle_12        eagle_12              7
eagle   12:20     eagle_12        eagle_12              8
eagle   12:45     eagle_12        eagle_12              9
eagle   13:50     eagle_13        eagle_12              10
eagle   14:00     eagle_14        eagle_12              11
eagle   14:30     eagle_14        eagle_14              12
eagle   15:15     eagle_15        eagle_14              13

我尝试在if语句中将向量与逻辑语句一起使用,我可以使我的逻辑起作用并返回正确的TRUE / FALSE向量,但是我不能用它来覆盖URN

到目前为止,我的代码:

IndexShiftedBy1 <- dt$Index + 1               # ie a vector which starts at 2 and goes up to 14

if ((dt$ID[dt$Index] == dt$ID[IndexShiftedBy1]) # ie if the two IDs are the same
&  (dt$URN[dt$Index] != dt$URN[IndexShiftedBy1])) { # URN2 (ie time dependent) is NOT the same
dt$URN[IndexShiftedBy1] <- dt$URN[Index] } # overwrite lower row with upper row's value

现在这首先不起作用,其次,如果这样做了,我将不得不多次运行它,因为它只是将问题向下移动了!

非常感谢任何帮助,我显然缺少一个有用的功能/需要自己编写一个功能,但这超出了我的知识水平

1 个答案:

答案 0 :(得分:1)

这是一个整洁的解决方案。关键组成部分是zoo::na.locf(不是tidyverse),它用先前的非NA值填充NA值。

library(dplyr)
# library(zoo)
dat %>%
  group_by(ID) %>%
  mutate(
    URN_new = if_else(c(TRUE, `units<-`(diff(Time), "hours") > 2), URN_original, NA_character_),
    URN_new = zoo::na.locf(URN_new)
  ) %>%
  ungroup()
# # A tibble: 13 x 5
#    ID    Time                URN_original URN_whichIwanttomake URN_new 
#    <chr> <dttm>              <chr>        <chr>                <chr>   
#  1 hawk  2018-10-26 09:05:00 hawk_09      hawk_09              hawk_09 
#  2 hawk  2018-10-26 09:10:00 hawk_09      hawk_09              hawk_09 
#  3 hawk  2018-10-26 10:00:00 hawk_10      hawk_09              hawk_09 
#  4 hawk  2018-10-26 11:00:00 hawk_11      hawk_09              hawk_09 
#  5 hawk  2018-10-26 15:00:00 hawk_15      hawk_15              hawk_15 
#  6 hawk  2018-10-26 16:00:00 hawk_16      hawk_15              hawk_15 
#  7 eagle 2018-10-26 12:00:00 eagle_12     eagle_12             eagle_12
#  8 eagle 2018-10-26 12:20:00 eagle_12     eagle_12             eagle_12
#  9 eagle 2018-10-26 12:45:00 eagle_12     eagle_12             eagle_12
# 10 eagle 2018-10-26 13:50:00 eagle_13     eagle_12             eagle_12
# 11 eagle 2018-10-26 14:00:00 eagle_14     eagle_12             eagle_12
# 12 eagle 2018-10-26 14:30:00 eagle_14     eagle_14             eagle_12
# 13 eagle 2018-10-26 15:15:00 eagle_15     eagle_14             eagle_12

data.table替代:

library(data.table)
datdt <- as.data.table(dat)
datdt[,
      URN_newdt := zoo::na.locf(
        ifelse(c(TRUE, `units<-`(diff(Time), "hours") > 2), URN_original, NA_character_)
      ),
      by = "ID"]

基本R:

ave(dat, dat$ID, FUN = function(d) {
  d$URN_newave <- zoo::na.locf(
    ifelse(c(TRUE, `units<-`(diff(d$Time), "hours") > 2), d$URN_original, NA_character_)
  )
  d
})

简要说明:zoo::na.locf用最近的非NA值填充NA

zoo::na.locf(c("hawk_09", NA, NA, NA, "hawk_15", NA))
# [1] "hawk_09" "hawk_09" "hawk_09" "hawk_09" "hawk_15" "hawk_15"

知道,下一步是弄清楚当时间差小于两个小时时如何将NA分配给新的URN。 diff(dat$Time)足够直接,尽管因为它可以返回不同的单位而不会发出警告,所以我们需要将其封装在units<-(..., "hours")中,以确保获得所需的东西。

下一步,diff返回向量长度减去1,因此我们需要确定是否需要添加前缀或追加元素,并且该附加值应为TRUEFALSE。在这种情况下,我们总是希望组中的第一个是原始的,因此在TRUE前面加注最有意义。


数据:

dat <- read.table(header=TRUE, stringsAsFactors=FALSE, text="
ID      Time      URN_original    URN_whichIwanttomake
hawk    09:05     hawk_09         hawk_09
hawk    09:10     hawk_09         hawk_09
hawk    10:00     hawk_10         hawk_09
hawk    11:00     hawk_11         hawk_09 
hawk    15:00     hawk_15         hawk_15
hawk    16:00     hawk_16         hawk_15
eagle   12:00     eagle_12        eagle_12
eagle   12:20     eagle_12        eagle_12
eagle   12:45     eagle_12        eagle_12
eagle   13:50     eagle_13        eagle_12
eagle   14:00     eagle_14        eagle_12
eagle   14:30     eagle_14        eagle_14
eagle   15:15     eagle_15        eagle_14")
dat$Time <- as.POSIXct(paste(Sys.Date(), dat$Time))

为了方便起见,我在POSIXt中使用“今天”。我建议您使用类似POSIXt之类的东西,但要由您决定时间的差异。