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

基于R语言计算时间序列中状态持续时长与状态变更次数

问题:R语言计算状态持续时长与变更次数

数据背景

我有一个含两列的数据框:Time列存储图片的时间戳,State列存储图片显示的状态。数据覆盖数周时间,每日包含数百行,连续行的状态可能保持不变或发生变更。

示例数据

TimeState
20220526173731PORED
20220526173741PORED
20220526173746SJEDI
20220526175242PORED
20220526175246SJEDI
20220526175806SJEDI
20220526175810SJEDI
20220526175818NEMA
20220526175819SJEDI
20220526175819SJEDI
20220526175822SJEDI
20220526180013SJEDI
20220522071053NEMA
20220522071056NEMA

需要完成的计算

  • 整个周期内各状态的持续时长(单位:秒)
  • 每日各状态的持续时长(单位:秒)
  • 每次状态变更前该状态的持续时长(单位:秒)
  • 整个周期及每日的状态变更次数

已尝试的代码

library(dplyr)
library(data.table)

# 读取数据框
dat <- read.csv("Lala.csv")

dat$Time <- as.character(dat$Time)
dat$Time <- as.POSIXct(dat$Time, format="%Y%m%d%H%M%S", tz="CET")
dat$Time

dat$State <- as.factor(dat$State)

# 为连续相同状态的序列分配唯一编号
setDT(dat)
dat[, state_run := cumsum(c(TRUE, diff(as.integer(dat$State)) != 0L))]
head(dat,20)

# 计算各状态的持续时长
dat2 <- dat[, list(StartTime = min(Time),
                     State = State[1],
                     Duration = diff(range(Time))), by = state_run]

现有问题

上述代码计算的是同一状态序列首行到末行的时长,而非该状态结束(即下一个状态首行)的时长,无法得到正确结果。

预期输出

每日状态持续时长

DayStateDuration
2022-05-22NEMA60547
2022-05-22PORED0
2022-05-22SJEDI0
2022-05-26NEMA63452
2022-05-26PORED19
2022-05-26SJEDI1342

总状态持续时长

StateDuration
NEMA123999
PORED19
SJEDI1342

每日状态变更次数

Day# of State changes
2022-05-220
2022-05-266

解决方案

完整代码

library(data.table)

# 读取并预处理数据
dat <- fread("Lala.csv")
dat[, Time := as.POSIXct(Time, format = "%Y%m%d%H%M%S", tz = "CET")]
dat[, State := as.factor(State)]
# 按时间排序,确保数据时序正确
setorder(dat, Time)

# 生成连续状态段ID
dat[, state_run := rleid(State)]

# 获取每个状态段的开始时间、状态
state_segments <- dat[, .(StartTime = min(Time), State = first(State)), by = state_run]
# 添加每个状态段的结束时间:下一个状态段的开始时间,最后一个用当日23:59:59
state_segments[, EndTime := shift(StartTime, type = "lead", fill = as.POSIXct(paste0(as.Date(max(dat$Time)), " 23:59:59"), tz = "CET"))]

# 计算每个状态段的持续时长(秒)
state_segments[, Duration := as.numeric(difftime(EndTime, StartTime, units = "secs"))]

# 1. 整个周期各状态总时长
total_duration <- state_segments[, .(TotalDuration = sum(Duration)), by = State]
print("总状态持续时长:")
print(total_duration)

# 2. 每日各状态持续时长
# 先拆分状态段跨天的情况(如果有的话)
state_segments[, DayStart := as.POSIXct(as.Date(StartTime), tz = "CET")]
state_segments[, DayEnd := DayStart + 86399] # 当日23:59:59

# 处理跨天的状态段
split_segments <- state_segments[, {
  if (EndTime <= DayEnd) {
    .(Day = as.Date(StartTime), State = State, Duration = Duration)
  } else {
    # 第一段:从StartTime到DayEnd
    dur1 <- as.numeric(difftime(DayEnd, StartTime, units = "secs"))
    # 第二段:从下一天00:00:00到EndTime
    next_day_start <- DayStart + 86400
    dur2 <- as.numeric(difftime(EndTime, next_day_start, units = "secs"))
    .(Day = c(as.Date(StartTime), as.Date(next_day_start)), State = rep(State, 2), Duration = c(dur1, dur2))
  }
}, by = state_run]

# 聚合每日各状态时长,同时补全所有状态的0值
all_states <- unique(dat$State)
all_days <- unique(as.Date(dat$Time))
full_grid <- expand.grid(Day = all_days, State = all_states)

daily_duration <- merge(full_grid, split_segments[, .(Duration = sum(Duration)), by = .(Day, State)], 
                        by = c("Day", "State"), all.x = TRUE)
daily_duration[is.na(Duration), Duration := 0]
print("每日状态持续时长:")
print(daily_duration)

# 3. 每次状态变更前的状态持续时长
# 排除最后一个状态段(没有后续变更)
change_durations <- state_segments[-.N, .(PrevState = State, DurationBeforeChange = Duration)]
print("每次状态变更前的状态持续时长:")
print(change_durations)

# 4. 状态变更次数
# 整个周期变更次数
total_changes <- nrow(state_segments) - 1
print(paste("整个周期状态变更次数:", total_changes))

# 每日变更次数
# 先找出所有变更发生的时间点(每个状态段的EndTime,除了最后一个)
change_times <- state_segments[-.N, .(ChangeTime = EndTime)]
change_times[, Day := as.Date(ChangeTime)]
daily_changes <- change_times[, .(ChangeCount = .N), by = Day]
# 补全没有变更的日期
daily_changes <- merge(data.table(Day = all_days), daily_changes, by = "Day", all.x = TRUE)
daily_changes[is.na(ChangeCount), ChangeCount := 0]
setnames(daily_changes, "ChangeCount", "# of State changes")
print("每日状态变更次数:")
print(daily_changes)

关键说明

  • rleid(State):data.table内置函数,高效生成连续相同状态的分组ID,替代手动cumsum逻辑更简洁可靠。
  • 跨天状态段处理:如果一个状态持续到次日,会拆分为两段分别统计当日时长,保证每日数据准确。
  • 补全0值:通过expand.grid生成所有日期与状态的组合,确保每个日期下所有状态都有记录,无数据的状态时长设为0,匹配预期输出格式。
  • 变更次数计算:状态变更次数等于状态段总数减1;每日变更次数统计该日期内发生的状态切换点数量,无变更的日期补0。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 06:15:10