如何用R语言向量化方法提取指定行块并计算状态转换时间
用向量化方法实现分组内的行块提取与状态转换时间计算
需求说明
在每个grp分组内:
- 行块起始条件满足任一即可:
val为负且time等于1val为负且前一行val为正
- 行块结束条件:
val为正且前一行val为负 - 最终需计算每个分组内,所有有效行块的「结束时间 - 起始时间」之和,即负到正的状态转换总时长
示例数据
library(tidyverse) df <- tribble( ~grp, ~time, ~val, "A", 1, 1, "A", 2, -0.3, "A", 3, -0.4, "A", 5, 0.5, "A", 7, -0.1, "A", 9, 0.9, "B", 1, -0.1, "B", 2, -0.4, "B", 3, 0.3, "B", 5, 0.1, "B", 7, 0.4, "B", 9, -0.1 )
向量化实现步骤
我们可以用dplyr的窗口函数(完全向量化,无需循环)完成所有操作:
1. 标记行块的起始、结束,并生成行块ID
df_with_flags <- df %>% group_by(grp) %>% mutate( # 获取前一行的val值,首行设为NA prev_val = lag(val, default = NA), # 标记行块起始 start_flag = case_when( val < 0 & time == 1 ~ TRUE, val < 0 & prev_val > 0 ~ TRUE, .default = FALSE ), # 标记行块结束 end_flag = case_when( val > 0 & prev_val < 0 ~ TRUE, .default = FALSE ), # 生成行块ID:每次遇到起始标记,ID递增 block_id = cumsum(start_flag) ) %>% ungroup()
2. 提取有效行块(包含起始和结束的行块)
筛选出所有属于存在结束标记的行块:
valid_blocks <- df_with_flags %>% group_by(grp, block_id) %>% filter(any(end_flag)) %>% ungroup() # 这一步得到需求中的result dataframe result <- valid_blocks %>% select(grp, time, val)
3. 计算负到正的转换总时长
按分组和行块ID计算单块时长,再求和得到分组总时长:
final <- valid_blocks %>% group_by(grp, block_id) %>% summarise( block_duration = last(time) - first(time), .groups = "drop_last" ) %>% summarise( tran_time = sum(block_duration), .groups = "drop" )
验证结果
查看生成的result:
print(result) #> # A tibble: 8 × 3 #> grp time val #> <chr> <dbl> <dbl> #> 1 A 2 -0.3 #> 2 A 3 -0.4 #> 3 A 5 0.5 #> 4 A 7 -0.1 #> 5 A 9 0.9 #> 6 B 1 -0.1 #> 7 B 2 -0.4 #> 8 B 3 0.3
查看最终的final:
print(final) #> # A tibble: 2 × 2 #> grp tran_time #> <chr> <dbl> #> 1 A 5 #> 2 B 2
和需求中的结果完全一致:A组(5-2)+(9-7)=5,B组3-1=2
内容的提问来源于stack exchange,提问作者wikichung
相关产品推荐
相关产品推荐

