R语言实时时间序列数据条件式峰谷信号检测方法优化
带显著性阈值的自适应时序峰谷检测优化方案
针对动态噪声、动态峰宽峰高的时序数据,在原实时峰值检测算法基础上做定向优化,支持自定义幅度阈值过滤微小波动,同时识别显著峰值与谷值。
- 保留原算法滑动窗口计算局部基线、局部标准差的自适应逻辑,适配背景噪声、峰谷形态动态变化的场景
- 新增可配置的峰/谷最小幅度参数,自动过滤基线附近不达标的微小震荡
- 输出结果包含峰谷位置、信号值、相对基线幅度三类信息,可直接用于后续上报逻辑
优化后实现代码
# 优化版带显著性阈值的实时峰谷检测函数 detect_significant_peaks_valleys <- function( signal, lag = 10, threshold = 3, influence = 0.2, min_peak_amp = 40, # 可配置:峰值最小显著幅度 min_valley_amp = 40 # 可配置:谷值最小显著幅度 ) { # 初始化结果存储向量 signals <- rep(0, length(signal)) # 0=无异常, 1=峰值, -1=谷值 filtered_y <- c(signal[1:lag], rep(NA, length(signal)-lag)) avg_filter <- rep(NA, length(signal)) std_filter <- rep(NA, length(signal)) avg_filter[lag] <- mean(signal[1:lag]) std_filter[lag] <- sd(signal[1:lag]) # 逐点遍历检测 for (i in (lag+1):length(signal)) { local_base <- avg_filter[i-1] local_std <- std_filter[i-1] # 峰值判定:z分达标 + 幅度超过最小峰值阈值 if (signal[i] - local_base > threshold * local_std && signal[i] - local_base >= min_peak_amp) { signals[i] <- 1 filtered_y[i] <- influence * signal[i] + (1-influence) * filtered_y[i-1] } # 谷值判定:z分达标 + 幅度超过最小谷值阈值 else if (local_base - signal[i] > threshold * local_std && local_base - signal[i] >= min_valley_amp) { signals[i] <- -1 filtered_y[i] <- influence * signal[i] + (1-influence) * filtered_y[i-1] } # 非显著点,纳入基线计算 else { signals[i] <- 0 filtered_y[i] <- signal[i] } # 更新滑动窗口的均值、标准差 avg_filter[i] <- mean(filtered_y[(i-lag+1):i]) std_filter[i] <- sd(filtered_y[(i-lag+1):i]) } # 提取峰谷位置、对应值、相对幅度 peak_idx <- which(signals == 1) valley_idx <- which(signals == -1) return(list( peaks = data.frame( index = peak_idx, value = signal[peak_idx], amp_above_base = signal[peak_idx] - avg_filter[peak_idx-1] ), valleys = data.frame( index = valley_idx, value = signal[valley_idx], amp_below_base = avg_filter[valley_idx-1] - signal[valley_idx] ), detection_flag = signals )) } # 传入示例信号测试 signal <- c(659, 613, 586, 642, 685, 695, 691, 733, 638, 708, 706, 691, 703, 712, 715, 700, 693, 682, 717, 711, 722, 700, 704, 704, 715, 691, 670, 684, 689, 711, 680, 692, 686, 710, 702, 699, 702, 715, 691, 670, 684, 689, 711, 673, 688, 699, 677, 701, 680, 692, 686, 710, 702, 699, 717, 691, 703, 712, 715, 700, 693, 682, 717, 711, 722, 700, 704, 704, 715, 691, 670, 684, 689, 711, 680, 692, 686, 710, 702, 699, 702, 706, 651, 712, 722, 734, 705, 714, 691, 704, 704, 669, 712, 715, 689, 715, 691, 670, 684, 689, 711, 651, 712, 722, 734, 705, 714, 691, 686, 676, 690, 693, 702, 694, 682, 693, 724, 693, 707, 684, 687, 705, 680, 680, 705, 680, 693, 700, 704, 704, 669, 712, 715, 689, 715, 691, 670, 684, 689, 711, 673, 678, 699, 677, 680, 682, 676, 690, 658, 675, 663, 667, 682, 673, 675, 656, 652, 563, 544, 542, 540, 532, 538, 505, 526, 565, 629, 720, 713, 720, 720, 773, 732, 740, 695, 689, 723, 685, 726, 710, 684, 693, 715, 692, 683, 712, 707, 693, 699, 717, 703, 687, 682, 690, 716, 708, 713, 700, 676, 708, 691, 717, 711, 722, 688, 695, 641, 666, 638, 639, 600, 635, 609, 653, 671, 649, 716, 708, 713, 700, 676, 708, 691, 717, 711, 722, 700, 704, 704, 669, 712, 715, 689, 715, 691, 670, 684, 689, 711, 673, 688, 699, 677, 701, 680, 692, 686, 710, 702, 699, 717, 691, 703, 712, 715, 700, 693, 682, 717, 711, 722, 700, 704, 704, 715, 691, 670, 684, 689, 711, 680, 692, 686, 710, 702, 699, 702, 715, 691, 670, 684, 689, 711, 673, 688, 699, 677, 701, 680, 692, 686, 710, 702, 699, 717, 691, 703, 712, 715, 700, 693, 682, 717, 711, 722, 700, 704, 704, 715, 691, 670, 684, 769, 767, 740, 752, 686, 710, 702, 699, 702, 706, 651, 712, 722, 734, 705, 714, 691, 704, 704, 669, 712, 715, 689, 715, 691, 670, 684, 689, 711, 704, 669, 712, 715, 689, 715, 691, 670, 684, 689, 711, 673, 688, 699, 677, 701, 680, 692, 686, 710, 702, 699, 717, 691, 703, 712, 715, 700, 693, 682, 717, 711, 722, 700, 704, 704, 715, 691, 670, 684, 689, 711, 680, 692, 686, 710, 702, 699, 702, 715, 691, 670, 684, 689, 711, 673, 688, 699, 677, 701, 680, 692, 686, 710, 702, 699, 665, 630, 808, 686, 787, 781, 796, 815, 786, 793, 664, 717, 691, 703, 712, 715, 700, 693, 682, 717, 711, 722, 700, 704, 704, 715, 691, 670, 684, 689, 711, 680, 692, 686, 710, 702, 699, 702, 706, 651, 712, 722, 734, 705, 714, 691, 704, 704, 669, 712, 715, 689, 715, 691, 670, 684, 689, 711) # 运行检测,可根据实际噪声调整幅度阈值过滤小幅波动 result <- detect_significant_peaks_valleys( signal = signal, lag = 12, threshold = 2.8, influence = 0.15, min_peak_amp = 45, min_valley_amp = 45 ) # 查看输出的显著峰值、谷值 print("检测到的显著峰值:") print(result$peaks) print("检测到的显著谷值:") print(result$valleys)
参数说明
lag:滑动窗口长度,用于计算局部基线均值与标准差,建议设置为信号中最小峰谷宽度的2~3倍threshold:z-score判定阈值,取值越大对波动的判定越严格,常规场景取2.5~3.5即可influence:已检测到的峰谷对基线计算的影响权重,取值范围01,实时流场景建议取00.3,避免基线被异常峰谷带偏min_peak_amp:峰值最小显著幅度,峰值点高出局部基线的差值低于该值时直接过滤min_valley_amp:谷值最小显著幅度,谷值点低于局部基线的差值低于该值时直接过滤
效果参考
运行代码后将自动过滤参考图中未标注的小幅波动,仅保留红圈标记的显著峰谷:
调参参考
- 杂峰过多时,适当调高
min_peak_amp、min_valley_amp的取值 - 漏检窄峰时适当减小
lag值;宽峰被误拆为多个峰时适当增大lag值 - 高噪声场景下适当调高
threshold取值,减少噪声误判
内容的提问来源于stack exchange,提问作者ramen
相关产品推荐
相关产品推荐

