如何计算数值超出指定范围的持续时长?
问题描述
我在处理一个看似简单的任务时卡壳了:手里有一个数值向量values和一个时间向量time,需要计算数值落在指定范围之外的总时长。
示例数据如下:
df <- data.frame(time= c(1,3:10), values= c(7,3:10))
针对这份数据,数值超出3.5到6.5范围的总时长是4.5小时。
可视化说明:
图表X轴为时间,Y轴为数值,点代表实际测量值,虚线是范围边界3.5和6.5,实线用于辅助判断穿越边界的时间点。
有没有我没注意到的简便方法来计算这个时长?
图表生成代码:
threshold_low <- 3.5 threshold_high <- 6.5 ggplot(data= df, mapping= aes(time, values)) + geom_point() + geom_line() + geom_hline(yintercept= c(threshold_low, threshold_high), linetype= "dashed") + scale_x_continuous(breaks=seq(0, 10, 1)) + scale_y_continuous(breaks=seq(0, 10, 1))
解决方案
核心逻辑是通过线性插值找到数值穿越阈值的时间点,再分段计算超出范围的时长并累加,这里提供两种简洁实现方式:
方法一:基础R实现
# 定义阈值 low <- 3.5 high <- 6.5 # 标记每个时间点的数值是否在范围内 df$in_range <- df$values >= low & df$values <= high # 初始化总时长 total_out <- 0 # 遍历相邻时间点对计算时长 for(i in 2:nrow(df)) { t_prev <- df$time[i-1] t_curr <- df$time[i] val_prev <- df$values[i-1] val_curr <- df$values[i] in_prev <- df$in_range[i-1] in_curr <- df$in_range[i] # 前后都在范围外,直接累加时间差 if(!in_prev & !in_curr) { total_out <- total_out + (t_curr - t_prev) } # 穿越边界,计算穿越点时间后累加对应时长 else if(xor(in_prev, in_curr)) { # 处理穿越低阈值的情况 if((val_prev < low & val_curr > low) || (val_prev > low & val_curr < low)) { t_cross <- t_prev + (low - val_prev) * (t_curr - t_prev) / (val_curr - val_prev) total_out <- total_out + ifelse(!in_prev, t_cross - t_prev, t_curr - t_cross) } # 处理穿越高阈值的情况 if((val_prev > high & val_curr < high) || (val_prev < high & val_curr > high)) { t_cross <- t_prev + (high - val_prev) * (t_curr - t_prev) / (val_curr - val_prev) total_out <- total_out + ifelse(!in_prev, t_cross - t_prev, t_curr - t_cross) } } } # 输出结果 total_out # [1] 4.5
方法二:tidyverse工具链简化
如果习惯用dplyr和purrr,可以更简洁地实现:
library(dplyr) low <- 3.5 high <- 6.5 df %>% mutate( in_range = between(values, low, high), time_diff = time - lag(time), prev_val = lag(values), prev_in = lag(in_range) ) %>% slice(-1) %>% mutate( out_duration = case_when( !prev_in & !in_range ~ time_diff, !prev_in & in_range ~ { t_cross <- lag(time) + ifelse(prev_val > high, (high - prev_val) * time_diff / (values - prev_val), (low - prev_val) * time_diff / (values - prev_val)) t_cross - lag(time) }, prev_in & !in_range ~ { t_cross <- lag(time) + ifelse(values > high, (high - prev_val) * time_diff / (values - prev_val), (low - prev_val) * time_diff / (values - prev_val)) time - t_cross }, TRUE ~ 0 ) ) %>% pull(out_duration) %>% sum() # [1] 4.5
内容的提问来源于stack exchange,提问作者LulY
相关产品推荐
相关产品推荐

