You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Python的find_peaks函数精准识别数据中前7个高突出度峰值?

如何使用Python的find_peaks函数精准识别数据中前7个高突出度峰值?

看起来你遇到的问题是用scipy的find_peaks找高突出度峰时,总是混进一些不符合预期的点,而用滚动百分位平滑数据的方法虽然能拿到正确结果,但速度太慢,对吧?我来帮你梳理一下这个问题,还有你后来想到的那个候选峰筛选方法到底靠不靠谱~

直接用find_peaks的问题

你一开始直接调用:

find_peaks(series, prominence=np.max(series) * 0.1, distance=48)

再取前7个突出度最高的峰,结果混进了一些 undesired 的点,这大概率是因为原始数据里的局部小波动刚好达到了prominence的阈值(最大值得10%),被find_peaks当成了有效峰。尤其是当数据本身有不少小凸起的时候,这种情况很常见。

你的“笨方法”的局限

用滚动窗口计算80分位数再做差的平滑方式,确实能过滤掉那些小波动,把真正的高突出峰凸显出来,但滚动窗口的apply操作(尤其是自定义lambda计算百分位)效率很低——因为它是逐窗口的纯Python循环,对大数据量来说速度会很慢,确实不是最优解。

你后来想到的候选峰筛选方法:非常靠谱!

你自己写的find_significant_peaks函数思路特别好,本质是先捞全候选峰,再基于局部突出度做二次筛选,这个方法比平滑数据快很多,而且能精准拿到你要的峰:

方法的核心逻辑

  1. 先找全候选峰:用很低的prominence阈值(最大值得1%)和小的distance(3),把所有可能的峰都先找出来,确保不会漏掉真正的大峰;
  2. 局部突出度双筛选:
    • 先检查每个峰是不是局部范围内(比如前后10个候选峰)的突出度最大值,把那些被周围更高突出度峰掩盖的小峰直接去掉;
    • 再检查这个峰和相邻候选峰的突出度差异,只有当差异超过自身突出度的10%时才保留,过滤掉那些和旁边峰“差不多高”的伪峰;
  3. 最后从筛选后的峰里取前7个突出度最高的,就是你要的结果。

这个方法的效率比平滑数据高太多:find_peaks本身是Scipy优化过的C实现,后续的筛选都是用Numpy的向量/局部操作,比滚动窗口的apply快N倍。

给你的小优化建议

不过这个方法还有可以微调的地方,能让它更快更省内存:

  • 缩小局部对比范围:你设置的compare_num=10可以根据数据的实际峰间隔调整,比如你之前用的distance=48,如果平均每48个点才会有一个真正的峰,那compare_num=5左右就足够,不用到10,能减少计算量;
  • 避免全量计算突出度差异矩阵:你原来用np.subtract.outer计算所有峰之间的突出度差异,会浪费不少内存(尤其是候选峰多的时候),可以改成在循环里只计算当前峰和局部窗口内峰的差异,不用提前算全量矩阵:

修改后的核心筛选逻辑如下:

def find_significant_peaks(x, prominence_diff_ratio=0.1, initial_distance=3):
    # Step 1: 先找所有候选峰
    peaks, properties = find_peaks(
        x, distance=initial_distance, prominence=np.max(x) * 0.01
    )

    if len(peaks) == 0:
        return peaks, properties

    prominences = properties["prominences"]
    valid_peaks_mask = np.ones(len(peaks), dtype=bool)
    compare_num = 10  # 可以根据实际数据调整

    for i in range(len(peaks)):
        # 取当前峰前后compare_num个候选峰的范围
        start_idx = max(0, i - compare_num)
        end_idx = min(len(peaks), i + 1 + compare_num)
        local_prominences = prominences[start_idx:end_idx]
        current_prominence = prominences[i]

        # 条件1:是不是局部范围内的突出度最大值
        if current_prominence < np.max(local_prominences):
            valid_peaks_mask[i] = False
            continue

        # 条件2:和相邻峰的突出度差异是否达标
        # 只计算当前峰和局部窗口内其他峰的差异
        neighbor_diffs = np.abs(current_prominence - local_prominences)
        neighbor_diffs = neighbor_diffs[neighbor_diffs != 0]  # 去掉和自己的差异
        threshold = current_prominence * prominence_diff_ratio
        
        if np.any(neighbor_diffs <= threshold):
            valid_peaks_mask[i] = False

    # 过滤有效峰和对应的属性
    valid_peaks = peaks[valid_peaks_mask]
    valid_properties = {key: properties[key][valid_peaks_mask] for key in properties}

    return valid_peaks, valid_properties

最终效果

用这个优化后的函数处理数据,再取前7个突出度最高的峰,就能拿到你肉眼可见的那些正确峰值,而且速度比平滑数据的方法快很多,完美解决你的问题~

附上你之前的错误检测结果图:
错误的检测结果

备注:内容来源于stack exchange,提问作者wander

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 12:54:31