R语言基于两列序列值为宽表透视分配正确分组ID
问题说明
- 现有长格式
data.frame,每条记录对应一个action的start/end日期:部分action可能仅存在start日期,同一类action可多次触发start、end流程。 - 需求为将长表转为宽表,单次
start对应单独一行,需要给每组start-end配对分配唯一分组ID以完成透视。 - 此前尝试用
rle方法实现,但无法结合两列信息生成正确分组ID,测试代码、当前输出、预期输出如下:
library(tidyverse) library(data.table) x <- c("start", "end") foo <- data.frame(time = c(rep(x, 3), "start", x, "start"), action = rep(letters[1:4], times = c(4,2,3,1)), day = 1:10) # 尝试用rle生成分组字段,无法得到正确ID foo %>% mutate(rle_time = rleid(time), rle_action = rleid(action)) #> time action day rle_time rle_action #> 1 start a 1 1 1 #> 2 end a 2 2 1 #> 3 start a 3 3 1 #> 4 end a 4 4 1 #> 5 start b 5 5 2 #> 6 end b 6 6 2 #> 7 start c 7 7 3 #> 8 start c 8 7 3 #> 9 end c 9 8 3 #> 10 start d 10 9 4 # 预期输出 data.frame(action = rep(letters[1:4], times = c(2,1,2,1)), start = c(seq(1,7,2),8,10), end = c(seq(2,6,2), NA,9,NA) ) #> action start end #> 1 a 1 2 #> 2 a 3 4 #> 3 b 5 6 #> 4 c 7 NA #> 5 c 8 9 #> 6 d 10 NA
实现方法
不需要复杂的rle逻辑,按action分组后,每遇到一次start就将分组计数加1,即可得到正确的配对ID,后续直接透视即可:
foo %>% # 按action分组,每个action类别内单独计数 group_by(action) %>% # 累计start出现的次数,作为同类别下的配对ID mutate(pair_id = cumsum(time == "start")) %>% # 转为宽表 pivot_wider(id_cols = c(action, pair_id), names_from = time, values_from = day) %>% # 移除辅助ID列,整理输出 ungroup() %>% select(-pair_id)
运行结果和预期完全一致:
# A tibble: 6 × 3 action start end <chr> <int> <int> 1 a 1 2 2 a 3 4 3 b 5 6 4 c 7 NA 5 c 8 9 6 d 10 NA
逻辑说明:每个end必然归属于它之前最近的一个未闭合的start,按action分组后累计start的出现次数,正好可以给每个start和后续对应的end分配相同的ID;连续出现多个start时,每个start会单独生成一个ID,靠前的start如果没有对应的end,透视后end字段自动为NA,完全匹配需求。
内容的提问来源于stack exchange,提问作者tjebo
相关产品推荐
相关产品推荐

