如何在Scipy峰值检测中偏向负峰值(谷值)左侧?
偏向左侧的负峰值(谷值)检测方案
一、Scipy库的适配调整
Scipy的find_peaks本身不支持直接给prominence的左右侧加权,但可以通过以下两种方式实现偏向左侧的谷值检测:
1. 限制右侧评估窗口长度(wlen参数)
检测谷值时(通过取反数据调用find_peaks),可以设置wlen参数让右侧的评估窗口比左侧短,这样计算突出度时会更侧重左侧的高度差。该参数支持设置元组来分别指定左右窗口长度(需Scipy 1.7+版本),左侧窗口设大、右侧设小,就能让算法更倾向于认可左侧满足条件的谷值。
示例代码:
import matplotlib.pyplot as plt import scipy.signal as signal # 取反数据找谷值,设置wlen让右侧窗口更短,偏向左侧检测 peaks, properties = signal.find_peaks(-df['RSI_MA14'], prominence=10, wlen=(20, 5)) local_minima = df.iloc[peaks] # 筛选x=200附近的最后一个负峰值(这里设±20为"附近"范围,可自行调整) target_peaks = [p for p in peaks if abs(p - 200) <= 20] last_peak = max(target_peaks) if target_peaks else None # 绘图展示 plt.plot(df.index, df['RSI_MA14']) plt.scatter(peaks, df['RSI_MA14'].iloc[peaks], color='red', label='检测到的谷值') if last_peak: plt.scatter(last_peak, df['RSI_MA14'].iloc[last_peak], color='blue', label='接近200的最后谷值') plt.legend() plt.show()
2. 自定义左侧优先的筛选逻辑
先通过find_peaks获取所有候选谷值,再手动筛选出左侧突出度达标、右侧要求放宽的点,以此实现偏向左侧的效果。比如要求左侧的下降幅度≥10,右侧上升幅度≥5即可。
示例代码:
import matplotlib.pyplot as plt import scipy.signal as signal import numpy as np # 先获取所有候选谷值(放宽初始prominence阈值) peaks, properties = signal.find_peaks(-df['RSI_MA14'], prominence=5) rsi_data = df['RSI_MA14'].values # 自定义筛选规则:左侧深度≥10,右侧深度≥5 filtered_peaks = [] left_win = 15 # 左侧计算窗口长度 right_win = 5 # 右侧计算窗口长度 for p in peaks: # 计算左侧最大差值(谷值左侧最高点与谷值的差) left_start = max(0, p - left_win) left_max = np.max(rsi_data[left_start:p]) left_prom = left_max - rsi_data[p] # 计算右侧差值(谷值右侧最高点与谷值的差) right_end = min(len(rsi_data)-1, p + right_win) right_max = np.max(rsi_data[p+1:right_end+1]) right_prom = right_max - rsi_data[p] # 满足左侧优先的条件则保留 if left_prom >= 10 and right_prom >= 5: filtered_peaks.append(p) # 筛选x=200附近的最后一个谷值 target_peaks = [p for p in filtered_peaks if abs(p - 200) <= 20] last_peak = max(target_peaks) if target_peaks else None # 绘图展示 plt.plot(df.index, rsi_data) plt.scatter(filtered_peaks, rsi_data[filtered_peaks], color='red', label='偏向左侧的谷值') if last_peak: plt.scatter(last_peak, rsi_data[last_peak], color='blue', label='接近200的最后谷值') plt.legend() plt.show()
二、实时数据处理的专属策略
针对实时新增数据的场景,偏向左侧检测意味着不需要等待右侧足够多的数据就能判定谷值,推荐采用滑动窗口+阈值判定的方式:
1. 核心逻辑
- 维护一个滑动窗口,实时跟踪当前数据点和左侧窗口内的最大值
- 当当前数据点比左侧窗口最大值低出设定阈值(比如10),且后续连续几个点开始上升时,立即判定该点为谷值
- 这种方式无需等待右侧大量数据,完全适配实时数据流场景
2. 示例代码
import matplotlib.pyplot as plt import numpy as np rsi_data = df['RSI_MA14'].values left_win_size = 15 # 左侧参考窗口大小 prominence_thresh = 10 # 左侧深度阈值 post_rising_count = 3 # 连续上升的点数,用于确认谷值 realtime_minima = [] # 模拟实时数据流入的遍历过程 for i in range(left_win_size, len(rsi_data)): left_max = np.max(rsi_data[i-left_win_size:i]) # 检查当前点是否满足左侧深度要求 if left_max - rsi_data[i] >= prominence_thresh: # 检查后续点是否连续上升,确认谷值 if i + post_rising_count < len(rsi_data): post_data = rsi_data[i+1:i+post_rising_count+1] if all(post_data[j] > post_data[j-1] for j in range(1, len(post_data))): realtime_minima.append(i) # 跳过后续点,避免重复检测 i += post_rising_count # 筛选x=200附近的最后一个谷值 target_peaks = [p for p in realtime_minima if abs(p - 200) <= 20] last_peak = max(target_peaks) if target_peaks else None # 绘图展示 plt.plot(df.index, rsi_data) plt.scatter(realtime_minima, rsi_data[realtime_minima], color='red', label='实时检测的偏向左侧谷值') if last_peak: plt.scatter(last_peak, rsi_data[last_peak], color='blue', label='接近200的最后谷值') plt.legend() plt.show()
三、其他可选工具
如果不想用Scipy,还可以考虑:
- Pandas Rolling窗口:结合滚动窗口计算最大值,自定义谷值判定逻辑,适合DataFrame操作
- TA-Lib库:专门用于技术分析,内置谷值/峰值检测函数,部分参数可调整检测偏向性(需额外安装)
内容的提问来源于stack exchange,提问作者BAM
相关产品推荐
相关产品推荐

