【问题标题】:How to check if several dates lie within a interval using data.table and lubridate?如何使用 data.table 和 lubridate 检查多个日期是否在一个区间内?
【发布时间】:2021-06-14 03:33:22
【问题描述】:

我有一个 data.table 有几列日期, 和两列定义一个区间。 我想检查是否有任何日期在该间隔内。

这是一个只有三列日期的玩具示例。

DT <- data.table(d1=dmy(c("1-1-2019", "2-2-2019")), 
    d2=dmy(c("1-3-2019", "2-2-2022")), 
    d3=dmy(c("1-1-2020", "2-2-2021")),
    initial=dmy(c("1-1-2020","5-5-2022")), 
    final=dmy(c("1-3-2020","1-1-2023")))

         d1         d2         d3    initial      final
 2019-01-01 2019-03-01 2020-01-01 2020-01-01 2020-03-01
 2019-02-02 2022-02-02 2021-02-02 2022-05-05 2023-01-01

我可以这样做:(它也可以用于过滤而不是创建新列)

DT[,new:=(d1 >= initial & d1 <= final) | (d2 >= initial & d2 <= final) | 
   (d3 >= initial & d3 <= final)]

但这很长,特别是如果我有超过三列。

我试过这个精简版

DT[,any( c(d1,d2,d3 ) %within% interval(initial, final))]

但它似乎不起作用。

我能做到的最简单的方法是什么?
也欢迎使用其他软件包的任何解决方案

【问题讨论】:

    标签: r dplyr data.table lubridate


    【解决方案1】:

    它不是矢量化的。所以,我们可以使用 group by 与行序列

    DT[, new := any( c(d1,d2,d3 ) %within% interval(initial, final)),
          by = 1:nrow(DT)]
    

    -输出

    DT
    #           d1         d2         d3    initial      final   new
    #1: 2019-01-01 2019-03-01 2020-01-01 2020-01-01 2020-03-01  TRUE
    #2: 2019-02-02 2022-02-02 2021-02-02 2022-05-05 2023-01-01 FALSE
    

    或向量化函数

    DT[, new := Reduce(`|`, lapply(.SD, function(x) x >= initial & 
        x <= final)),.SDcols = patterns('^d\\d+$')]
    DT
    #          d1         d2         d3    initial      final   new
    #1: 2019-01-01 2019-03-01 2020-01-01 2020-01-01 2020-03-01  TRUE
    #2: 2019-02-02 2022-02-02 2021-02-02 2022-05-05 2023-01-01 FALSE
    

    【讨论】:

    • 什么不是向量化的“any”或%within%?
    • 您的解决方案更具原创性,但我选择了 r2evans 的,因为它更快,而且我认为使用 na.rm 选项更容易处理缺失。
    • @skan 你能删除你的 cmets
    【解决方案2】:

    另一种选择:

    DT[, new := rowSums(sapply(.SD, between, initial, final)) > 0,
       .SDcols = c("d1", "d2", "d3")]
    DT
    #            d1         d2         d3    initial      final    new
    #        <Date>     <Date>     <Date>     <Date>     <Date> <lgcl>
    # 1: 2019-01-01 2019-03-01 2020-01-01 2020-01-01 2020-03-01   TRUE
    # 2: 2019-02-02 2022-02-02 2021-02-02 2022-05-05 2023-01-01  FALSE
    

    确保你使用的是data.table::between ...如果你和dplyr::between有冲突,后者会抱怨(因为它要求它的下限/上限长度为1)。

    这个答案既是矢量化的,又与任意数量的列一样有效。也就是说,它会为每个(矢量化)调用一次between,而不管列数如何,只调用一次rowSums。 (另外,rowSums(.) 通常比 apply(., 1, any) 或类似的规范 R 方法更快。)

    【讨论】:

    • 好的,我选择了您的解决方案,因为它更快。而且因为他已经有很多积分了:)我仍然需要决定如何处理这里的缺失。
    • 我并没有卑躬屈膝(他做得非常好,但我没有受伤;-),但缺失是一个重要问题。您在哪些变量中有NA 值?
    • 这就是重点,如果 d1、d2 或 d3 中的任何一个是 NA,我会忽略它们并继续使用另一个。但是,如果定义日期的任何间隔为 NA,则必须仔细决定。
    • 好吧,听起来你必须首先想出你想要做什么的“口头逻辑”。当日期范围的一端未定义时,直观地是什么意思?您应该假设-InfInf,还是假设Sys.Date() 更合理?还是您认为您想根据其他观察结果进行估算?很多好问题......而且我认为这对我来说真的太上下文敏感了,无法流畅地提出建议。祝你好运:-)
    • @r2evans 我在阅读您的回答后投了反对票。
    猜你喜欢
    • 2022-11-26
    • 1970-01-01
    • 2013-04-11
    • 1970-01-01
    • 1970-01-01
    • 2021-02-24
    • 2019-11-06
    • 2019-04-26
    • 1970-01-01
    相关资源
    最近更新 更多