【问题标题】:How to add rows with time periods inbetween given time period?如何在给定时间段之间添加具有时间段的行?
【发布时间】:2019-09-25 03:43:23
【问题描述】:

我有一个包含时间段的数据集,可能会重叠,显示是否有人在场 (example_df)。我想获得一个数据集,将一个大的时间段(从 2014-01-01 到 2014-10-31)分成有人在场的更小的时间段(present = 1)和没有人在场的时间段(@987654323 @)。 结果应该类似于result_df

示例数据框

example_df <- data.frame(ID = 1, 
                     start = c(as.Date("2014-01-01"), as.Date("2014-03-05"), as.Date("2014-06-13"), as.Date("2014-08-15")), 
                     end = c(as.Date("2014-04-07"), as.Date("2014-04-12"), as.Date("2014-08-05"), as.Date("2014-10-02")), 
                     present = 1) 

结果应该是这样的

result_df <- data.frame(ID = 1, 
                     start = c(as.Date("2014-01-01"), as.Date("2014-04-12"), as.Date("2014-06-13"), as.Date("2014-08-05"), as.Date("2014-08-15"), as.Date("2014-10-02")), 
                     end = c(as.Date("2014-04-12"), as.Date("2014-06-13"), as.Date("2014-08-05"), as.Date("2014-08-15"), as.Date("2014-10-02"), as.Date("2014-10-31")), 
                     present = c(1, 0, 1, 0, 1, 0)) 

我不知道如何解决这个问题,因为它需要拆分时间段或添加行(或其他什么?)。非常感谢任何帮助!

【问题讨论】:

    标签: r dplyr time-series lubridate


    【解决方案1】:

    假设您想为每个 ID 单独执行此操作,您可以创建一个包含有人在场的所有日期的数据表,并将该数据表与该时间段内所有日期的表连接起来。结果并不完全相同,因为现在和不现在的时期不重叠。

    library(data.table)
    setDT(example_df)
    
    
    example_df[, {
      pres <- unique(unlist(Map(`:`, start, end)))
      class(pres) <- 'Date'
      all <- min(pres):max(pres)
      class(all) <- 'Date'
      pres <- data.table(day = pres)
      all <- data.table(day = all)
      out.full <- pres[all, on = .(day), .(day = i.day, present = +!is.na(x.day))]
      out.full[, .(start = min(day), end = max(day)), 
               by = .(present, rid = rleid(present))][, -'rid']
      }, by = ID]
    
    #    ID present      start        end
    # 1:  1       1 2014-01-01 2014-04-12
    # 2:  1       0 2014-04-13 2014-06-12
    # 3:  1       1 2014-06-13 2014-08-05
    # 4:  1       0 2014-08-06 2014-08-14
    # 5:  1       1 2014-08-15 2014-10-02
    

    【讨论】:

      【解决方案2】:

      我希望我能提供帮助,因为我也一直在努力解决这个问题。

      与 IceCreamToucan 的示例一样,这假设个人 ID 独立。这种方法使用 dplyr 查看日期范围的重叠,然后将它们展平。此方法的其他examples 已在stackoverflow 中描述并使用dplyr。最终结果包括人在场的时间范围。

      library(tidyr)
      library(dplyr)
      
      pres <- example_df %>%
        group_by(ID) %>%
        arrange(start) %>% 
        mutate(indx = c(0, cumsum(as.numeric(lead(start)) > cummax(as.numeric(end)))[-n()])) %>%
        group_by(ID, indx) %>%
        summarise(start = min(start), end = max(end), present = 1) %>%
        select(-indx)
      

      然后,可以添加额外的行来指示存在的时间段。在这些情况下,对于给定的 ID,它将确定较旧的结束日期和较新(较近的)开始日期之间的间隔。然后最后按 ID 和开始日期排序。

      result <- pres
      
      for (i in unique(pres$ID)) {
        pres_i <- subset(pres, ID == i)
        if (nrow(pres_i) > 1) {
          adding <- data.frame(ID = i, start = pres_i$end[-nrow(pres_i)]+1, end = pres_i$start[-1]-1, present = 0)
          adding <- adding[adding$start <= adding$end, ]
          result <- bind_rows(result, adding)
        }
      }
      result[order(result$ID, result$start), ]
      
      # A tibble: 5 x 4
      # Groups:   ID [1]
           ID start      end        present
        <dbl> <date>     <date>       <dbl>
      1     1 2014-01-01 2014-04-12       1
      2     1 2014-04-13 2014-06-12       0
      3     1 2014-06-13 2014-08-05       1
      4     1 2014-08-06 2014-08-14       0
      5     1 2014-08-15 2014-10-02       1
      

      【讨论】: