如何在R中完成开关通断状态持续时长的后续计算?
问题描述
现有如下R语言数据框data:
data<-data.frame(ymdhms=c(20230301000000,20230301000010,20230301000020,20230301000030,20230301000040, 20230301000050,20230301000100,20230301000110,20230301000120,20230301000130, 20230301000140,20230301000150,20230301000200,20230301000210,20230301000220), switch=c(40,41,42,43,0,0,0,51,52,53,54,0,0,48,47))
其中:
ymdhms为年月日时分秒的拼接数值,例如20230301000000对应2023年3月1日00:00:00;switch值>0代表开关开启(on),=0代表关闭(off),所有值非负;- 每行数据的时间间隔固定为10秒。
需要统计开关各段状态的持续时长,预期输出数据框如下:
on_off<-data.frame(on=c(30,30,10),off=c(20,10,NA))
已完成辅助列的添加:
data$dt <- as.POSIXct(strptime(formatC(data$ymdhms, format='f'), format='%Y%m%d%H%M%S')) data$switchStatus_A <- ifelse(data$switch > 0, 1, 0) # 1标记开启,0标记关闭
解决方案
通过状态分组、时长计算、结果对齐三步即可实现目标:
1. 为连续相同状态分配分组ID
把连续的开启/关闭状态划分为独立分组,提供两种实现方式:
# 方式1:使用data.table包的rleid函数(简洁高效) library(data.table) data$group_id <- rleid(data$switchStatus_A) # 方式2:基础R实现,无需额外包 data$group_id <- cumsum(c(1, diff(data$switchStatus_A) != 0))
2. 计算各状态段的持续时长
结合时间列dt计算每个分组的实际持续时长,同时处理最后一个无后续状态的段:
library(dplyr) # 添加下一行的时间列,用于计算状态结束时间 data$next_dt <- lead(data$dt) # 按分组统计时长 duration_summary <- data %>% group_by(group_id, switchStatus_A) %>% summarise( # 状态段的结束时间:有后续状态则取下一段的开始时间,否则取当前段最后一行的时间 end_time = ifelse(!is.na(last(next_dt)), last(next_dt), last(dt)), start_time = first(dt), .groups = "drop" ) %>% mutate( duration = as.numeric(difftime(end_time, start_time, units = "secs")), # 最后一个状态段需额外加10秒(每行间隔10秒,最后一行的状态持续到下一个10秒节点) duration = ifelse(is.na(last(next_dt)) & switchStatus_A == 1, duration + 10, duration), status = ifelse(switchStatus_A == 1, "on", "off") ) %>% select(status, duration)
3. 对齐on/off时长,生成目标数据框
将开启和关闭时长按交替顺序配对,最后一个状态若无对应状态则补NA:
# 分离on和off的时长列表 on_list <- duration_summary$duration[duration_summary$status == "on"] off_list <- duration_summary$duration[duration_summary$status == "off"] # 对齐两个列表的长度,不足的位置补NA max_length <- max(length(on_list), length(off_list)) on_list <- c(on_list, rep(NA, max_length - length(on_list))) off_list <- c(off_list, rep(NA, max_length - length(off_list))) # 创建目标数据框 on_off <- data.frame(on = on_list, off = off_list)
运行后即可得到与预期一致的结果:
> on_off on off 1 30 20 2 30 10 3 10 NA
内容的提问来源于stack exchange,提问作者Lee
相关产品推荐
相关产品推荐

