如何使用 data.table 和 lubridate 检查多个日期是否在一个区间内?
How to check if several dates lie within a interval using data.table and lubridate?
我有一个 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))]
但是好像不行。
最简单的方法是什么?
也欢迎使用其他软件包的任何解决方案
它没有矢量化。所以,我们可以使用 group by 和 rows
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
备选方案:
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
和 dplyr::between
之间有冲突,后者会抱怨(因为它要求它的 lower/upper 边界是长度-1).
这个答案既是向量化的,又是任意列数的高效答案。也就是说,它会在每个 列 (向量化)中调用一次 between
,而无论列数如何,它只会调用一次 rowSums
。 (此外,rowSums(.)
通常比 apply(., 1, any)
或类似的规范 R 方法更快。)
我有一个 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))]
但是好像不行。
最简单的方法是什么?
也欢迎使用其他软件包的任何解决方案
它没有矢量化。所以,我们可以使用 group by 和 rows
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
备选方案:
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
和 dplyr::between
之间有冲突,后者会抱怨(因为它要求它的 lower/upper 边界是长度-1).
这个答案既是向量化的,又是任意列数的高效答案。也就是说,它会在每个 列 (向量化)中调用一次 between
,而无论列数如何,它只会调用一次 rowSums
。 (此外,rowSums(.)
通常比 apply(., 1, any)
或类似的规范 R 方法更快。)