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

非均匀X网格下scipy.signal.find_peaks峰值检测问题及方案问询

问题描述

我用scipy.signal.find_peaks检测峰值的坐标和特征(峰宽、突出度等),数据满足:

Yi = f(Xi)

其中i是数据点编号,Xi是X坐标,Yi是对应信号值。

处理均匀X网格(相邻点间距恒定)时,这个函数表现很好,能把实际X轴宽度转换成样本数来计算;但处理非均匀X网格(相邻点间距不等,即x(i)-x(i-1)≠x(i+1)-x(i))时,检测结果不理想。

我试过用width参数(要求以样本数指定峰的最小/最大宽度):

all_peaks_indexes = signal.find_peaks(signal_y, prominence=search_peak_prominence, width=1, height=1)

但width的单位是样本数,对非均匀X网格不适用。根据参数说明:

WIDTH - Required width of peaks in samples.
Either a number, None, an array matching x, or a 2-element sequence of the former.
The first element is always interpreted as the minimal and the second, if supplied, as
the maximal required width.

核心问题是find_peaks只使用索引(样本编号),不考虑实际X坐标,没法直接用实际宽度来筛选。

请问有没有支持基于实际信号维度(比如时间)设置峰宽的非均匀X网格峰值检测工具?或者有什么解决办法?


解决方法

1. 用scipy.signal.peak_widths配合实际X坐标后过滤

先调用find_peaks找到候选峰值,再用peak_widths传入实际X数组计算真实宽度,最后过滤不符合要求的峰值。示例代码:

import scipy.signal as signal

# 先获取候选峰值
peaks, props = signal.find_peaks(signal_y, prominence=search_peak_prominence, height=1)

# 传入实际X坐标,计算半高宽(可通过rel_height调整高度阈值)
widths, _, _, _ = signal.peak_widths(signal_y, peaks, rel_height=0.5, x=signal_x)

# 按实际宽度范围筛选峰值
min_width = 2.0  # 实际X维度的最小宽度
max_width = 10.0 # 实际X维度的最大宽度
valid_peaks = peaks[(widths >= min_width) & (widths <= max_width)]
valid_props = {k: v[(widths >= min_width) & (widths <= max_width)] for k, v in props.items()}

这种方法利用scipy原生工具,既保留find_peaks的检测能力,又能基于实际X维度筛选宽度。

2. 自定义峰值宽度判断逻辑

如果需要更灵活的宽度计算规则(比如用特定高度阈值),可以手动遍历候选峰值计算实际宽度:

peaks, props = signal.find_peaks(signal_y, prominence=search_peak_prominence, height=1)
valid_peaks = []

min_width = 2.0
max_width = 10.0
half_height_ratio = 0.5  # 用峰值的半高计算宽度

for peak_idx in peaks:
    peak_height = signal_y[peak_idx]
    threshold = peak_height * half_height_ratio
    
    # 找左侧第一个低于阈值的点
    left_idx = peak_idx
    while left_idx > 0 and signal_y[left_idx] >= threshold:
        left_idx -= 1
    # 找右侧第一个低于阈值的点
    right_idx = peak_idx
    while right_idx < len(signal_y)-1 and signal_y[right_idx] >= threshold:
        right_idx += 1
    
    # 计算实际X宽度并判断
    actual_width = signal_x[right_idx] - signal_x[left_idx]
    if min_width <= actual_width <= max_width:
        valid_peaks.append(peak_idx)

这种方法完全自定义规则,适合特殊场景,但效率略低于原生工具。

3. 重采样为均匀X网格

如果数据允许损失部分细节,可以把非均匀数据插值成均匀网格,再用find_peaks的width参数:

import numpy as np

# 生成均匀X网格,num控制分辨率
uniform_x = np.linspace(signal_x.min(), signal_x.max(), num=1000)
# 插值得到对应Y值
uniform_y = np.interp(uniform_x, signal_x, signal_y)

# 转换实际宽度为样本数
grid_spacing = uniform_x[1] - uniform_x[0]
width_samples = int(min_width / grid_spacing)

# 用find_peaks检测
peaks_uniform, props_uniform = signal.find_peaks(uniform_y, prominence=search_peak_prominence, width=width_samples, height=1)
# 转换回原X坐标
actual_peaks_x = uniform_x[peaks_uniform]

注意:重采样可能丢失峰值附近的精细结构,需要根据数据情况调整分辨率。

4. 其他替代方案

  • 用pandas结合滚动窗口实现自定义峰值检测,但需要自己编写峰值判断和宽度计算逻辑。
  • 完全自定义峰值检测函数:遍历数据点判断峰值,同时基于实际X坐标计算宽度,适合高度定制化的场景。

内容的提问来源于stack exchange,提问作者twistfire

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 13:35:41