使用 R 测试日期是否出现在多个日期范围内
Test if date occurs in multiple date ranges with R
我有一个包含多个日期范围(准确地说是 45 个)的数据框:
Range Start End
1 2014-01-01 2014-02-30
2 2015-01-10 2015-03-30
3 2016-04-20 2016-10-12
... ... ...
它们永远不会重叠
我还有一个包含各种事件日期 (200K+) 的数据框:
Event Date
1 2014-01-02
2 2014-03-20
3 2015-04-01
4 2016-08-18
... ...
我想测试这些日期是否在这些范围内:
Event Date InRange
1 2014-01-02 TRUE
2 2014-03-20 FALSE
3 2015-04-01 FALSE
4 2016-08-18 TRUE
...
执行此测试的最佳方法是什么?我查看了 lubridate 的 between 和 interval 函数以及各种 Whosebug 问题,但找不到好的解决方案。
自己编写 function
来检查日期列表是否在多个间隔中的任何一个。
date.in <- function(x){
m <- NULL
for (i in 1:NROW(df)){m <- c(m, ifelse(x>=df[i,1] & x<=df[i,2], TRUE, FALSE))}
any(m)}
数据:
df <- data.frame(start=c("2014-01-01", "2015-01-10", "2016-04-20"),
end=c("2014-02-30", "2015-03-30", "2016-10-12"))
df[] <- lapply(df, as.character)
s <- c("2014-01-02", "2014-03-20", "2015-04-01", "2016-08-18")
使用字符串 s
.
进行测试
as.character(lapply(s, date.in))#TRUE FALSE FALSE TRUE
您可以从第一个数据框创建日期范围的向量,然后使用 %in%
运算符检查事件的每个日期是否在此日期范围内。假设您的第一个数据框是 dateRange
,第二个数据框是 events
,将上述逻辑放在一行中将是:
events$InRange <- events$Date %in% unlist(Map(`:`, dateRange$Start, dateRange$End))
events
Event Date InRange
1 1 2014-01-02 TRUE
2 2 2014-03-20 FALSE
3 3 2015-04-01 FALSE
4 4 2016-08-18 TRUE
我们使用 Map
创建日期范围向量的地方。 Map
结合 :
运算符创建一个日期范围从 Start
到 End
的列表。在接近 list(2014-01-01 : 2014-02-30, 2015-01-10 : 2015-03-30, 2016-04-20 : 2016-10-12 ...)
(象征性地,无效)的某个地方,使用 unlist
,我们将其展平为日期范围的向量,然后可以方便地与 %in%
一起使用。
在您的第一个 "data.frame" 中安排了有序的非重叠间隔,您可以测试 - 对于每个事件日期 - 它是否高于 $Start
及其各自的 $End
。使用 findInterval
减少关系比较和内存需求。
findInterval(events$Date, ranges$Start) > findInterval(events$Date, ranges$End)
#[1] TRUE FALSE FALSE TRUE
含数据(修改为“2014-02-30”):
ranges = structure(list(Range = 1:3, Start = structure(c(16071, 16445,
16911), class = "Date"), End = structure(c(16129, 16524, 17086
), class = "Date")), .Names = c("Range", "Start", "End"), row.names = c(NA,
-3L), class = "data.frame")
events = structure(list(Event = 1:4, Date = structure(c(16072, 16149,
16526, 17031), class = "Date")), .Names = c("Event", "Date"), row.names = c(NA,
-4L), class = "data.frame")
我有一个包含多个日期范围(准确地说是 45 个)的数据框:
Range Start End
1 2014-01-01 2014-02-30
2 2015-01-10 2015-03-30
3 2016-04-20 2016-10-12
... ... ...
它们永远不会重叠
我还有一个包含各种事件日期 (200K+) 的数据框:
Event Date
1 2014-01-02
2 2014-03-20
3 2015-04-01
4 2016-08-18
... ...
我想测试这些日期是否在这些范围内:
Event Date InRange
1 2014-01-02 TRUE
2 2014-03-20 FALSE
3 2015-04-01 FALSE
4 2016-08-18 TRUE
...
执行此测试的最佳方法是什么?我查看了 lubridate 的 between 和 interval 函数以及各种 Whosebug 问题,但找不到好的解决方案。
自己编写 function
来检查日期列表是否在多个间隔中的任何一个。
date.in <- function(x){
m <- NULL
for (i in 1:NROW(df)){m <- c(m, ifelse(x>=df[i,1] & x<=df[i,2], TRUE, FALSE))}
any(m)}
数据:
df <- data.frame(start=c("2014-01-01", "2015-01-10", "2016-04-20"),
end=c("2014-02-30", "2015-03-30", "2016-10-12"))
df[] <- lapply(df, as.character)
s <- c("2014-01-02", "2014-03-20", "2015-04-01", "2016-08-18")
使用字符串 s
.
as.character(lapply(s, date.in))#TRUE FALSE FALSE TRUE
您可以从第一个数据框创建日期范围的向量,然后使用 %in%
运算符检查事件的每个日期是否在此日期范围内。假设您的第一个数据框是 dateRange
,第二个数据框是 events
,将上述逻辑放在一行中将是:
events$InRange <- events$Date %in% unlist(Map(`:`, dateRange$Start, dateRange$End))
events
Event Date InRange
1 1 2014-01-02 TRUE
2 2 2014-03-20 FALSE
3 3 2015-04-01 FALSE
4 4 2016-08-18 TRUE
我们使用 Map
创建日期范围向量的地方。 Map
结合 :
运算符创建一个日期范围从 Start
到 End
的列表。在接近 list(2014-01-01 : 2014-02-30, 2015-01-10 : 2015-03-30, 2016-04-20 : 2016-10-12 ...)
(象征性地,无效)的某个地方,使用 unlist
,我们将其展平为日期范围的向量,然后可以方便地与 %in%
一起使用。
在您的第一个 "data.frame" 中安排了有序的非重叠间隔,您可以测试 - 对于每个事件日期 - 它是否高于 $Start
及其各自的 $End
。使用 findInterval
减少关系比较和内存需求。
findInterval(events$Date, ranges$Start) > findInterval(events$Date, ranges$End)
#[1] TRUE FALSE FALSE TRUE
含数据(修改为“2014-02-30”):
ranges = structure(list(Range = 1:3, Start = structure(c(16071, 16445,
16911), class = "Date"), End = structure(c(16129, 16524, 17086
), class = "Date")), .Names = c("Range", "Start", "End"), row.names = c(NA,
-3L), class = "data.frame")
events = structure(list(Event = 1:4, Date = structure(c(16072, 16149,
16526, 17031), class = "Date")), .Names = c("Event", "Date"), row.names = c(NA,
-4L), class = "data.frame")