如何修正状态时间线数据:优先保留a、b,消除c的时间重叠
解决状态时长重叠问题:调整C状态适配A/B优先级
嘿,我刚好能帮你搞定这个状态重叠修正的需求——毕竟A、B优先级高于C,只能动C的起止时间,甚至如果A/B把C的时间全占了就直接删掉C记录对吧?我给你写了一套基于R的实现方案,完美匹配你给出的示例数据场景。
首先先把你的示例数据加载进来
# 加载示例数据 df <- structure(list(id = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L), period = c("a", "b", "c", "a", "b", "c", "a", "a", "b", "c", "a", "b"), start = c(3L, 11L, 8L, 18L, 20L, 26L, 31L, 1L, 11L, 13L, 15L, 16L), end = c(10L, 12L, 20L, 19L, 25L, 32L, 35L, 10L, 14L, 17L, 15L, 20L)), class = "data.frame", row.names = c(NA, -12L))
核心实现思路
我这套方案的逻辑很清晰:
- 按个体(
id)分组,先把每个个体下的A、B状态区间提取出来(题目说A/B本身无重叠,所以直接用就行) - 对每个C状态的区间,计算它和A/B区间的非重叠部分,只保留这些有效片段;如果C的区间被A/B完全覆盖,就直接删掉这条C记录
- 最后把原有的A/B记录和修正后的C记录合并,按时间排序得到最终结果
完整代码实现
我用了dplyr和purrr来简化分组和循环处理,代码可读性很高:
library(dplyr) library(purrr) # 定义工具函数:计算单个C区间与A/B区间的非重叠有效部分 get_valid_c_intervals <- function(c_start, c_end, exclude_intervals) { # 先把A/B区间整理成有序的不重叠区间(即使题目说A/B无重叠,这步也能让代码更鲁棒) exclude_intervals <- exclude_intervals %>% arrange(start) %>% mutate( group = cumsum(ifelse(start > lag(end, default = -Inf), 1, 0)) ) %>% group_by(group) %>% summarise(start = min(start), end = max(end)) %>% ungroup() %>% select(start, end) current_start <- c_start valid_segments <- list() # 逐个检查A/B区间,切割C区间 for (i in 1:nrow(exclude_intervals)) { ex_start <- exclude_intervals$start[i] ex_end <- exclude_intervals$end[i] # 如果当前C区间的起点在A/B区间之前,这段就是有效部分 if (current_start < ex_start) { valid_segments <- append(valid_segments, list(data.frame(start = current_start, end = ex_start - 1))) } # 更新C区间的起点到A/B区间的终点之后 current_start <- max(current_start, ex_end + 1) if (current_start > c_end) break } # 检查循环结束后是否还有剩余的有效C区间 if (current_start <= c_end) { valid_segments <- append(valid_segments, list(data.frame(start = current_start, end = c_end))) } # 返回有效区间,没有的话返回空数据框 if (length(valid_segments) == 0) { data.frame(start = numeric(0), end = numeric(0)) } else { bind_rows(valid_segments) } } # 主流程处理数据 result <- df %>% group_by(id) %>% group_split() %>% map_dfr(function(group_df) { # 分离A/B记录和C记录 ab_df <- group_df %>% filter(period %in% c("a", "b")) c_df <- group_df %>% filter(period == "c") # 处理每个C记录 processed_c <- if (nrow(c_df) > 0) { c_df %>% rowwise() %>% mutate(valid = list(get_valid_c_intervals(start, end, ab_df))) %>% unnest(valid) %>% select(id, period, start, end) %>% filter(start <= end) # 过滤掉无效的空区间 } else { data.frame(id = integer(0), period = character(0), start = integer(0), end = integer(0)) } # 合并A/B和处理后的C记录,按时间排序 bind_rows(ab_df, processed_c) %>% arrange(start) }) # 查看最终结果 print(result)
效果验证
- 对于
id=1的个体:
原C区间8-20会被A(3-10)、B(11-12)、A(18-19)切割,变成两段有效区间:13-17和20-20;原C区间26-32会被A(31-35)切割,变成26-30。 - 对于
id=2的个体:
A/B的区间(1-10、11-14、15-15、16-20)完全覆盖了C的13-17区间,所以处理后没有C记录,完全符合你的期望。
内容的提问来源于stack exchange,提问作者Dan Lewer
相关产品推荐
相关产品推荐

