【问题标题】:Count number of days with date range in one table and days criteria in another table计算一个表中的日期范围和另一个表中的天数标准的天数
【发布时间】:2020-09-03 12:14:20
【问题描述】:

我的一张表的数据在最后两列中提到了日期:

dat<- data.frame(a = c(rep("x",3)),
                 date1=c(seq(as.Date("2018-01-01"), as.Date("2018-01-3"), 1)), 
                 date2=c(seq(as.Date("2018-01-08"), as.Date("2018-01-10"), 1)))

日期1 日期2

1 x 2018-01-01 2018-01-08

2 x 2018-01-02 2018-01-09

3 x 2018-01-03 2018-01-10

我的另一张桌子每天是什么日子

cal <- data.frame(dt = c(seq(as.Date("2018-01-01"), as.Date("2018-01-10"),1)),
                  day = c(rep("workday",5), rep("holiday",1), rep("weekend",4)))

如何将表 1(dat) 中的天数作为新列获取,使其仅计算第 2 列和第 3 列中提到的范围内的工作日?

4 列的示例输出。最后一列是前两列中日期范围的工作日数

  a      date1      date2     countdown
1 x 2018-01-01   2018-01-08           5
2 x 2018-01-02   2018-01-09           4
3 x 2018-01-03   2018-01-10           3

【问题讨论】:

    标签: r data.table


    【解决方案1】:

    data.table解决方案

    library( data.table )
    #set data to data.table format
    setDT(dat); setDT(cal)
    setkey(dat, date1, date2 )
    dat[dat, 
        N := { val = cal[ day == "workday" & dt >= i.date1 & dt <= i.date2 ]
               list( nrow( val ) ) }, 
        by = .EACHI ]
    
    #    a      date1      date2 N
    # 1: x 2018-01-01 2018-01-08 5
    # 2: x 2018-01-02 2018-01-09 4
    # 3: x 2018-01-03 2018-01-10 3
    

    更新 data.table::foverlaps() 解决方案

    library( data.table )
    #set data to data.table format
    setDT(dat); setDT(cal)
    #create dummy date
    cal[,dt2 := dt]
    #set keys
    setkey( dat, date1, date2 )
    setkey( cal, dt, dt2 )
    #overlap join
    ans <- foverlaps( dat, cal )
    #summarise
    ans[, .( countdown = uniqueN( dt[day == "workday"] ) ), by = .(a, date1, date2)][]
    #    a      date1      date2 countdown
    # 1: x 2018-01-01 2018-01-08         5
    # 2: x 2018-01-02 2018-01-09         4
    # 3: x 2018-01-03 2018-01-10         3
    

    【讨论】:

    • 但是在更大的数据中这非常慢。无论如何,这可以更快吗?
    • @Deb 用data.table::foverlaps()-approach 更新答案...应该工作得很快
    • 如果列中有 NA 值,foverlaps 不起作用。有什么办法解决吗?
    • 您的样本数据中没有 NA?如果您的生产数据中有,请填写它们,或者将它们从加入过程中过滤出来
    【解决方案2】:

    一种使用tidyverse函数的方式:

    1. date1date2 之间创建一个天序列

    2. 获取长格式数据

    3. 将上述数据与cal数据框左连接

    4. 计算每一行的工作日数。

    library(dplyr)
    
    dat %>%
      mutate(row = row_number(),
             dt = purrr::map2(date1, date2, seq, by = '1 day')) %>%
      tidyr::unnest(dt) %>%
      left_join(cal, by = 'dt') %>%
      group_by(row, a, date1, date2) %>%
      summarise(countdown = sum(day == 'workday')) %>%
      ungroup() %>%
      select(-row)
    
    #   a     date1      date2      countdown
    #  <chr> <date>     <date>         <int>
    #1 x     2018-01-01 2018-01-08         5
    #2 x     2018-01-02 2018-01-09         4
    #3 x     2018-01-03 2018-01-10         3
    

    【讨论】:

    • 任何 data.table 解决方案?
    【解决方案3】:

    基本 R 选项

    within(
      dat,
      countdown <- sapply(
        1:nrow(dat),
        function(k) sum(cal$day == "workday" & !is.na(cut(cal$dt, c(date1[k], date2[k]))))
      )
    )
    

    给予

      a      date1      date2 countdown
    1 x 2018-01-01 2018-01-08         5
    2 x 2018-01-02 2018-01-09         4
    3 x 2018-01-03 2018-01-10         3
    

    【讨论】:

      【解决方案4】:

      其他解决方案

      # v1
      df %>% 
        rowwise() %>% 
        mutate(int_date = list(seq(date1, date2, "1 day"))) %>% 
        unnest(int_date) %>% 
        left_join(cal, by = c("int_date" = "dt")) %>% 
        filter(day == "workday") %>% 
        group_by(a, date1, date2) %>% 
        count
      
      # v2
      df %>% 
        rowwise() %>% 
        mutate(int_date = list(seq(date1, date2, "1 day")),
               out = sum(unlist(int_date) %in% cal$dt[cal$day == "workday"])) %>% 
        select(-int_date)
      
      # v3 (using @Ronak Shah hint with a `map` )
      df %>% 
        mutate(int_date = map2(date1, date2, seq, "1 day"),
               out = map_dbl(int_date, ~ sum(.x %in% cal$dt[cal$day == "workday"]))) %>% 
        select(-int_date)
      
      
      # A tibble: 3 x 4
      # Rowwise: 
        a     date1      date2        out
        <chr> <date>     <date>     <int>
      1 x     2018-01-01 2018-01-08     5
      2 x     2018-01-02 2018-01-09     4
      3 x     2018-01-03 2018-01-10     3
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-02-05
        • 1970-01-01
        • 2014-03-08
        • 2020-08-05
        • 2013-10-03
        相关资源
        最近更新 更多