如何筛选局部峰值前的连续非负值(含峰值)?
解决方法:提取峰值前连续非负值区间
嘿,这个需求我刚好处理过类似的场景,用dplyr的分组逻辑就能轻松搞定,而且能完美适配多峰值的情况。
思路拆解
- 先标记所有负值的位置,以此为分界划分数据分组——每个分组代表「从某个负值结束后到下一个峰值」的连续区间
- 过滤掉没有峰值的纯负值分组,只保留包含峰值的有效分组
- 对每个有效分组,提取从第一个非负值到峰值的所有行
完整代码实现
先加载依赖包(如果没安装过,先运行install.packages("dplyr")):
library(dplyr)
然后处理你的数据:
test <- structure(list(year = 1996:2016, value = c(-0.5214506, -0.8037488, 0.1138524, 0.9939848, 1.7027944, 0.6448417, 0.1204489, -1.2254546, -0.6733273, -0.7457323, 0.4874829, 2.2080809, 2.0609055, -2.5291374, -1.5272201, 0.3057773, 0.1383523, -0.6455441, -0.8364883, -0.8907073, -0.7940878), peak = c(FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE)), class = c("tbl_df", "tbl", "data.frame"), row.names = c(NA, -21L)) result <- test %>% # 1. 标记负值并生成分组ID mutate( is_negative = value < 0, # 每次遇到负值就创建新分组,确保连续非负值在同一组 group_id = cumsum(is_negative) ) %>% # 2. 只保留包含峰值的分组 filter(group_id %in% group_id[peak]) %>% # 3. 对每个分组,提取从第一个非负值到峰值的所有行 group_by(group_id) %>% slice(which.max(is_negative == FALSE):n()) %>% ungroup() %>% # 清理临时列 select(-is_negative, -group_id) print(result)
运行结果
运行后会得到和你预期完全一致的输出:
# A tibble: 5 × 3 year value peak <int> <dbl> <lgl> 1 1998 0.114 FALSE 2 1999 0.994 FALSE 3 2000 1.70 TRUE 4 2006 0.487 FALSE 5 2007 2.21 TRUE
代码逻辑说明
is_negative = value < 0:标记所有value为负的行,作为分组的分界点group_id = cumsum(is_negative):利用累积求和,每次遇到负值就给分组ID加1,这样连续的非负值会被分到同一个组里filter(group_id %in% group_id[peak]):只保留包含峰值的分组,过滤掉没有峰值的纯负值分组slice(which.max(is_negative == FALSE):n()):在每个分组里找到第一个非负值的位置,然后从该位置取到分组最后一行(也就是峰值所在行)
这个方法可以轻松扩展到任意数量的峰值,不管你的数据里有多少个峰值,都能准确提取对应的连续非负值区间。
内容的提问来源于stack exchange,提问作者Miha Trošt
相关产品推荐
相关产品推荐

