You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何标记包含指定日期的不规则时间区间(R语言)

问题:标记时间区间是否包含指定日期

我有一个存储不规则时间区间的dataframe(df1)和一个日期向量(df2),需要为df1新增一列dateoccurs,标记该区间是否包含df2中的任意日期(后续会删除这些包含日期的区间)。现有2000+个区间和2000+个日期,之前尝试的方案要么无法匹配归属区间,要么在大数据集上出现值重复、区间信息被修改的情况,甚至出现日期误判。

示例数据

原始数据df1

> df1
 diveno               start                 fin
1      1 2018-08-01 08:20:40 2018-08-01 08:39:20
2      2 2018-08-01 08:40:50 2018-08-01 08:53:40
3      3 2018-08-01 10:01:00 2018-08-01 10:16:30
4      4 2018-08-01 15:45:30 2018-08-01 15:58:20
5      5 2018-08-01 17:06:00 2018-08-01 17:18:20

日期向量df2

> df2
                 date
1 2018-08-01 08:30:00
2 2018-08-01 15:47:00
3 2018-08-02 17:10:00

期望输出df3

> df3
  diveno               start                 fin dateoccurs
1      1 2018-08-01 08:20:40 2018-08-01 08:39:20          Y
2      2 2018-08-01 08:40:50 2018-08-01 08:53:40          N
3      3 2018-08-01 10:01:00 2018-08-01 10:16:30          N
4      4 2018-08-01 15:45:30 2018-08-01 15:58:20          Y
5      5 2018-08-01 17:06:00 2018-08-01 17:18:20          N

数据构造代码

# 构造df1
df1 <- data.frame(diveno=c(1,2,3,4,5), 
                  start=c("2018-08-01 08:20:40","2018-08-01 08:40:50", "2018-08-01 10:01:00","2018-08-01 15:45:30","2018-08-01 17:06:00"),
                  fin=c("2018-08-01 08:39:20","2018-08-01 08:53:40","2018-08-01 10:16:30","2018-08-01 15:58:20", "2018-08-01 17:18:20"))

df1$start <- as.POSIXct(df1$start,format="%Y-%m-%d %H:%M:%S",tz="CET")
df1$fin <- as.POSIXct(df1$fin,format="%Y-%m-%d %H:%M:%S",tz="CET")

# 构造df2
df2 <- data.frame(date=c("2018-08-01 08:30:00", "2018-08-01 15:47:00", "2018-08-02 17:10:00"))
df2$date <- as.POSIXct(df2$date,format="%Y-%m-%d %H:%M:%S",tz="CET")

# 构造期望输出df3
df3 <- data.frame(diveno=c(1,2,3,4,5), 
                  start=c("2018-08-01 08:20:40","2018-08-01 08:40:50", "2018-08-01 10:01:00","2018-08-01 15:45:30","2018-08-01 17:06:00"),
                  fin=c("2018-08-01 08:39:20","2018-08-01 08:53:40","2018-08-01 10:16:30","2018-08-01 15:58:20", "2018-08-01 17:18:20"),
                  dateoccurs=c("Y","N","N","Y","N"))

已尝试的方法及问题

曾尝试用data.table做重叠连接,但返回结果中fin时间被修改,大数据集上还出现值重复、diveno数量改变的问题:

intervals <- df1
elements <- df2[,1]

library(data.table) #v1.10.0
j <- setDT(intervals)[data.table(elements), on = .(start <= elements, fin >= elements)]
j2 <- as.data.frame(j)
na.omit(j2)

误判问题

更新df2数据后,出现明显误判:df2中2018-08-01 06:25:00、2018-08-01 06:30:00、2018-08-01 06:35:00这些早于区间开始时间的日期,被错误识别为落在diveno=1和diveno=2的区间内。

单独用lubridate的%within%验证时,逻辑是正确的:

s <- df1[1,2] 
f <- df1[1,3]     
int <- interval(s,f)

df2[,1] %within% int

解决方案

原因分析

误判的核心是时区不匹配:df1的时间时区是CET,而更新后的df2时间时区是UTC,两者转换时出现偏差,导致时间比较逻辑出错。

正确实现方法

方法1:用lubridate+dplyr实现

确保时区统一后,对每个区间检查是否包含任意df2的日期:

library(lubridate)
library(dplyr)

# 统一时区:将df2的日期转换为CET时区
df2$date <- with_tz(df2$date, tzone = "CET")

# 为df1添加dateoccurs列
df1 <- df1 %>%
  rowwise() %>%
  mutate(dateoccurs = ifelse(any(df2$date %within% interval(start, fin)), "Y", "N")) %>%
  ungroup()

# 查看结果
df1

方法2:用data.table高效实现(适合大数据集)

同样先统一时区,再用非等连接标记匹配情况:

library(data.table)

# 统一时区
df2$date <- with_tz(df2$date, tzone = "CET")

# 转换为data.table
setDT(df1)
setDT(df2)

# 非等连接,标记是否有匹配
df1[, dateoccurs := "N"]
matches <- df1[df2, on = .(start <= date, fin >= date), nomatch = 0]
df1[diveno %in% matches$diveno, dateoccurs := "Y"]

# 查看结果
df1

两种方法都能得到符合预期的结果,且不会出现误判或数据篡改问题,其中data.table方法在2000+数据量下效率更高。


内容的提问来源于stack exchange,提问作者Jess

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 21:20:26