如何用dplyr基于通勤标签为动物移动数据划分昼夜时段?
问题描述
现有不同动物(id)的移动数据,动物每日往返中心地点:早晨出发标记为am_commute,傍晚返回标记为pm_commute,非通勤状态标记为stationary,部分通勤包含多个连续数据点。
示例数据集代码:
id <- rep.int(c("A"), times = c(22)) datetimes <- c(seq(lubridate::ymd_hms('2021-05-21T06:00:00'), lubridate::ymd_hms('2021-05-23T23:00:00'), by = "3 hours")) commute <- c('am_commute', 'am_commute', 'stationary', 'stationary', 'pm_commute', 'stationary', 'stationary', 'stationary', 'am_commute', 'stationary', 'stationary', 'stationary', 'stationary', 'pm_commute', 'pm_commute', 'stationary', 'am_commute', 'am_commute', 'stationary', 'stationary', 'pm_commute', 'pm_commute') data <- data.frame(id, datetimes, commute)
需求:创建新变量time_of_day,规则如下:
- 通勤数据(
am_commute/pm_commute)对应的time_of_day为NA "day":当日首次早通勤点到首次晚通勤点之间的stationary数据"night":首次晚通勤点到次日首次早通勤点之间的stationary数据
期望输出示例:
id datetimes commute time_of_day 1 A 2021-05-21 06:00:00 am_commute <NA> 2 A 2021-05-21 09:00:00 am_commute <NA> 3 A 2021-05-21 12:00:00 stationary day 4 A 2021-05-21 15:00:00 stationary day 5 A 2021-05-21 18:00:00 pm_commute <NA> 6 A 2021-05-21 21:00:00 stationary night 7 A 2021-05-22 00:00:00 stationary night 8 A 2021-05-22 03:00:00 stationary night 9 A 2021-05-22 06:00:00 am_commute <NA> 10 A 2021-05-22 09:00:00 stationary day 11 A 2021-05-22 12:00:00 stationary day 12 A 2021-05-22 15:00:00 stationary day 13 A 2021-05-22 18:00:00 stationary day 14 A 2021-05-22 21:00:00 pm_commute <NA> 15 A 2021-05-23 00:00:00 pm_commute <NA> 16 A 2021-05-23 03:00:00 stationary night 17 A 2021-05-23 06:00:00 am_commute <NA> 18 A 2021-05-23 09:00:00 am_commute <NA> 19 A 2021-05-23 12:00:00 stationary day 20 A 2021-05-23 15:00:00 stationary day 21 A 2021-05-23 18:00:00 pm_commute <NA> 22 A 2021-05-23 21:00:00 pm_commute <NA>
尝试用dplyr结合mutate()、case_when()、first()和lead()实现,但无法引用次日的commute列值,询问能否通过dplyr管道完成该需求。
解决方案
可以通过dplyr管道结合lubridate处理日期时间,完整实现步骤如下:
library(dplyr) library(lubridate) result <- data %>% # 提取日期字段,用于按天分组识别通勤节点 mutate(date = date(datetimes)) %>% # 按动物ID和日期分组,获取当日首次早/晚通勤的时间点 group_by(id, date) %>% mutate( first_am = min(datetimes[commute == "am_commute"], na.rm = TRUE), first_pm = min(datetimes[commute == "pm_commute"], na.rm = TRUE) ) %>% ungroup() %>% # 按ID和时间排序,确保数据顺序正确 arrange(id, datetimes) %>% # 按ID分组,超前获取次日的首次早通勤时间 group_by(id) %>% mutate(next_first_am = lead(first_am, order_by = datetimes)) %>% ungroup() %>% # 根据时间区间生成time_of_day变量 mutate( time_of_day = case_when( commute != "stationary" ~ NA_character_, datetimes > first_am & datetimes < first_pm ~ "day", datetimes > first_pm & (is.na(next_first_am) | datetimes < next_first_am) ~ "night", TRUE ~ NA_character_ ) ) %>% # 移除中间辅助字段 select(-date, -first_am, -first_pm, -next_first_am)
代码说明
- 提取日期字段:用
date(datetimes)将时间戳转换为纯日期,方便按天分组定位每日的通勤节点 - 标记通勤节点:按
id和date分组,用min()获取当日最早的am_commute和pm_commute时间,自动处理多个连续通勤点的情况 - 获取次日早通勤时间:按
id分组后,用lead()获取下一行的first_am,即次日的首次早通勤时间 - 区间分类判断:通过
case_when逐一匹配规则:- 非
stationary数据直接赋值为NA - 时间在当日首次早通勤和首次晚通勤之间的
stationary标记为day - 时间在当日首次晚通勤和次日首次早通勤之间的
stationary标记为night
- 非
运行上述代码后,输出结果与期望示例完全一致。
内容的提问来源于stack exchange,提问作者m.hand
相关产品推荐
相关产品推荐

