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

Scipy.signal peak_widths无法适配图表的问题及替代方案咨询

解决scipy.signal.peak_widths图表适配问题及替代方案

一、修复peak_widths的图表适配问题

find_peaks能正常识别峰位,但peak_widths的结果错位,大多是因为对返回值的单位理解有误,或者没有做坐标转换。

1. 明确返回值的含义

scipy.signal.peak_widths默认返回的宽度、左右边界都是样本点的索引值(浮点型,包含插值结果),如果你的x轴是自定义坐标(比如时间、频率等非连续整数),直接用这些索引值绘图必然错位。

2. 转换为实际x轴坐标的代码示例

import numpy as np
from scipy.signal import find_peaks, peak_widths
import matplotlib.pyplot as plt

# 示例数据(自定义x轴)
x = np.linspace(0, 15, 150)
y = np.sin(x) + np.random.normal(0, 0.15, 150)
peaks, _ = find_peaks(y, height=0.6)

# 获取半高宽数据(rel_height=0.5指定半高位置)
widths, width_heights, left_ips, right_ips = peak_widths(y, peaks, rel_height=0.5)

# 将浮点索引转换为实际x轴坐标(适配均匀/非均匀x轴)
left_x = np.interp(left_ips, np.arange(len(x)), x)
right_x = np.interp(right_ips, np.arange(len(x)), x)
peak_x = x[peaks]

# 绘图验证
plt.plot(x, y, label="原始数据")
plt.plot(peak_x, y[peaks], "x", color="orange", label="峰位")
# 绘制半高宽线段
for lx, rx, h in zip(left_x, right_x, width_heights):
    plt.hlines(h, lx, rx, color="red", label="半高宽" if lx == left_x[0] else "")
plt.legend()
plt.show()

3. 常见错误排查

  • 未指定rel_height:默认值为1.0(峰值高度),此时宽度为0,无法显示,必须指定合理值(如0.5对应半高宽)
  • 直接用整数索引取x值:忽略了left_ips/right_ips的浮点插值特性,导致峰宽位置偏移
  • x轴非均匀间隔:必须用np.interp做坐标映射,不能直接用步长计算

二、替代peak_widths的峰宽计算方法

如果peak_widths仍不符合需求,可以尝试以下几种方式:

1. 手动计算半高宽(适用于简单峰型)

def get_half_width(x, y, peak_idx):
    peak_y = y[peak_idx]
    half_y = peak_y * 0.5
    # 找峰值左侧第一个低于半高的点
    left_idx = np.argmax(y[:peak_idx] < half_y)
    # 找峰值右侧第一个低于半高的点
    right_idx = peak_idx + np.argmax(y[peak_idx:] < half_y)
    # 线性插值得到精确半高位置
    left_x = np.interp(half_y, y[left_idx:left_idx+2][::-1], x[left_idx:left_idx+2][::-1])
    right_x = np.interp(half_y, y[right_idx-1:right_idx+1], x[right_idx-1:right_idx+1])
    return right_x - left_x, left_x, right_x

# 使用示例
width, lx, rx = get_half_width(x, y, peaks[0])
plt.hlines(y[peaks[0]]*0.5, lx, rx, color="green", label="手动计算半高宽")

2. 结合峰突出度计算宽度

利用peak_prominences获取峰的基座边界,以此计算宽度:

from scipy.signal import peak_prominences

prominences, left_bases, right_bases = peak_prominences(y, peaks)
# 基座宽度(从左基座到右基座的x轴距离)
base_widths = x[right_bases] - x[left_bases]

# 绘图
for peak, lb, rb in zip(peaks, left_bases, right_bases):
    plt.hlines(y[peak]-prominences[np.where(peaks==peak)[0][0]], x[lb], x[rb], color="blue", linestyle="--")

3. 曲线拟合计算宽度(适用于已知峰型)

如果峰符合高斯、洛伦兹等分布,拟合后直接从参数推导宽度:

from scipy.optimize import curve_fit

def gaussian(x, amp, mean, std):
    return amp * np.exp(-(x - mean)**2 / (2*std**2))

# 拟合单个峰
target_peak_idx = peaks[0]
fit_x = x[target_peak_idx-15:target_peak_idx+15]
fit_y = y[target_peak_idx-15:target_peak_idx+15]
# 初始参数猜测(峰值幅度、峰位、标准差)
popt, _ = curve_fit(gaussian, fit_x, fit_y, p0=[y[target_peak_idx], x[target_peak_idx], 1])
# 高斯峰半高宽≈2.3548*标准差
half_width = 2.3548 * popt[2]

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 00:10:20