如何基于睡眠时长占比将DateTime睡眠数据的mixed类别转为day/night?
解决睡眠数据跨时段分类的问题
我来帮你搞定这个把"mixed"睡眠时段按占比重新分类的需求!咱们一步步来:
首先,核心思路是计算每个睡眠时段在白天(8:00-20:00)和夜间(20:00-次日8:00)的实际时长占比,然后把原来标记为"mixed"的记录替换为占比更高的类别。
第一步:定义计算重叠时长的函数
我们需要一个函数来计算单个睡眠时段和白天/夜间区间的重叠时长,这里用lubridate的时间区间工具来实现:
library(tidyverse) library(lubridate) # 计算睡眠时段在白天和夜间的时长(分钟) calc_day_night_duration <- function(start_time, end_time) { # 获取睡眠覆盖的所有日期 dates <- seq.Date(as.Date(start_time), as.Date(end_time), by = "day") day_total <- 0 night_total <- 0 for (date in dates) { # 当天白天区间:8:00 - 20:00 day_int <- interval(ymd_hms(paste(date, "08:00:00")), ymd_hms(paste(date, "20:00:00"))) # 当天夜间区间:20:00 - 次日8:00 night_int <- interval(ymd_hms(paste(date, "20:00:00")), ymd_hms(paste(date + 1, "08:00:00"))) # 计算和白天区间的重叠时长 day_overlap <- intersect(interval(start_time, end_time), day_int) if (!is.na(day_overlap)) day_total <- day_total + as.numeric(as.duration(day_overlap), "minutes") # 计算和夜间区间的重叠时长 night_overlap <- intersect(interval(start_time, end_time), night_int) if (!is.na(night_overlap)) night_total <- night_total + as.numeric(as.duration(night_overlap), "minutes") } tibble(day_minutes = day_total, night_minutes = night_total) }
第二步:整合到数据处理流程里
接下来把这个函数和你原来的分类逻辑结合,替换"mixed"类别:
# 你的原始数据 mydata <- data.frame( id = c("m1","m1","m1","m2","m2","m2","m3","m4","m4"), start = strptime(c("2020-01-03 10:00:00","2020-01-03 16:00:00","2020-01-03 19:20:00","2020-01-05 10:00:00","2020-01-05 15:20:00","2020-01-05 20:50:00","2020-01-06 06:30:00","2020-01-08 06:30:00","2020-01-08 07:50:00"), "%Y-%m-%d %H:%M:%S"), end = strptime(c("2020-01-03 16:00:00","2020-01-03 19:20:00","2020-01-03 20:50:00","2020-01-05 15:20:00","2020-01-05 20:50:00","2020-01-05 22:00:00","2020-01-06 07:40:00","2020-01-08 07:50:00","2020-01-08 08:55:00"), "%Y-%m-%d %H:%M:%S") ) # 最终数据处理流程 df_final <- mydata %>% # 转换时间格式为ymd_hms mutate_at(vars(start, end), ymd_hms) %>% # 先标记初始的day/night/mixed类别 mutate( start_hour = hour(start), end_hour = hour(end), day.night_initial = case_when( start_hour >= 8 & end_hour >= 8 & end_hour < 20 ~ "day", start_hour >= 20 & (end_hour < 8 | end_hour <= 23) | (start_hour < 8 & end_hour < 8) ~ "night", TRUE ~ "mixed" ) ) %>% # 逐行计算白天/夜间时长 rowwise() %>% mutate(calc_day_night_duration(start, end)) %>% ungroup() %>% # 根据时长占比替换mixed类别 mutate( day.night = case_when( day.night_initial != "mixed" ~ day.night_initial, day_minutes > night_minutes ~ "day", night_minutes > day_minutes ~ "night", # 如果时长相等,这里默认设为night,你可以根据需求调整 TRUE ~ "night" ) ) %>% # 清理中间列(可选) select(-start_hour, -end_hour, -day.night_initial)
第三步:验证结果
运行后你会看到:
- 第3行(19:20-20:50):白天40分钟,夜间50分钟 → 转为
night - 第5行(15:20-20:50):白天280分钟,夜间50分钟 → 转为
day - 第9行(07:50-08:55):白天55分钟,夜间10分钟 → 转为
day
完全符合你的需求!如果需要调整时长相等时的规则,直接修改最后一个TRUE ~ "night"即可。
内容的提问来源于stack exchange,提问作者Sparky
相关产品推荐
相关产品推荐

