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

R语言中高效判断DateTime是否处于任意时间区间的方法

高效判断心率时间是否处于睡眠区间的优化方案

我有一个含约38000条观测的hr_clean数据框,每行是Fitbit采集的连续一个月每分钟平均心率对应的POSIXct类型时间。需要判断每行的dateTime是否处于睡眠时段,现有sleep_ranges数据框存储睡眠起始(startTime)和结束(endTime)时间(均为POSIXct类型)。原for循环实现逻辑但速度极慢,现寻求优化方案。

原低效代码:

# Set seed for reproducibility
set.seed(123)

# Define the start and end dates
start_date <- ymd_hms("2025-03-01 00:00:00")
end_date <- ymd_hms("2025-04-01 23:59:59")

hr_clean <- data.frame(dateTime = start_date + runif(10, 0, as.numeric(difftime(end_date, start_date, units = "secs"))))


sleep_ranges <- data.frame(startTime = ymd_hms(c("2025-03-15 00:58:00",
                                                 "2025-03-14 00:17:00",
                                                 "2025-03-12 23:38:00",
                                                 "2025-03-12 00:21:30")),
                           endTime = ymd_hms(c("2025-03-15 09:10:30",
                                               "2025-03-14 07:30:30",
                                               "2025-03-13 07:45:30",
                                               "2025-03-12 07:26:30")))


is_sleeping_df <- data.frame(dateTime = character(),
                             is_sleeping = character())

for(minute in hr_clean$dateTime){
  print(as_datetime(minute))
  sleepTF = (any(minute > sleep_ranges$startTime & minute < sleep_ranges$endTime))
  
  df <- data.frame(dateTime = as_datetime(minute),
                   is_sleeping = sleepTF)
  
  is_sleeping_df <- rbind(is_sleeping_df, df)
}

原代码低效原因

  • 循环中频繁使用rbind拼接数据框:每次rbind都会创建新数据框并复制全部数据,38000次操作会导致大量内存开销和时间浪费。
  • 逐行处理:R的循环本身对逐元素操作效率较低,数据量较大时劣势更明显。

优化方案

方案1:基础R向量化操作

利用R的向量化特性,直接对整个dateTime列进行判断,避免循环和频繁拼接:

library(lubridate)

# 对每个dateTime,检查是否存在任意睡眠区间包含它
hr_clean$is_sleeping <- sapply(hr_clean$dateTime, function(x) {
  any(x > sleep_ranges$startTime & x < sleep_ranges$endTime)
})

注:相比原for循环,该方案避免了rbind的重复内存复制,效率提升明显。

方案2:data.table非等连接(性能最优)

data.table的非等连接专门针对区间匹配场景,处理大数据量速度极快:

library(data.table)
library(lubridate)

# 转换为data.table格式
setDT(hr_clean)
setDT(sleep_ranges)

# 初始化标记列
hr_clean[, is_sleeping := FALSE]
# 非等连接匹配符合区间的记录
matches <- hr_clean[sleep_ranges, on = .(dateTime > startTime, dateTime < endTime), nomatch = 0]
# 标记匹配成功的时间点
hr_clean[matches$dateTime, is_sleeping := TRUE]

# 如需转回data.frame格式
# hr_clean <- as.data.frame(hr_clean)

方案3:fuzzyjoin包(语法简洁)

fuzzyjoin提供直观的模糊区间匹配函数,无需复杂操作:

library(fuzzyjoin)
library(lubridate)

# 模糊左连接,匹配dateTime处于睡眠区间的记录
result <- fuzzy_left_join(
  hr_clean,
  sleep_ranges,
  by = c("dateTime" = "startTime", "dateTime" = "endTime"),
  match_fun = list(`>`, `<`)
)

# 标记是否处于睡眠区间:存在匹配则为TRUE
result$is_sleeping <- !is.na(result$startTime)

# 保留原心率数据列和标记列
final_result <- result[, c("dateTime", "is_sleeping")]

内容的提问来源于stack exchange,提问作者Evan N.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 04:52:11