更新时间:2023-02-26 18:35:13
In case this is still relevant to you, you can use scipy.signal.peak_widths "as is" to achieve what you want by passing in modified prominence_data
. Based on your own answer:
import numpy as np
from scipy.signal import find_peaks, peak_prominences, peak_widths
# Create sample data
x = np.linspace(0, 6 * np.pi, 1000)
x = np.sin(x) + 0.6 * np.sin(2.6 * x)
# Find peaks
peaks, _ = find_peaks(x)
prominences, left_bases, right_bases = peak_prominences(x, peaks)
As stated in peak_widths
's documentation the height at which the width is measured is calculated as
h_eval = h_peak - prominence * relative_height
We can control the latter two variables through the parameters prominence_data
and rel_height
. So instead of passing in the calculated prominence
which differs for each peak we can create an array where all values are the same and use that to create an absolute height:
# Create constant offset as a replacement for prominences
offset = np.ones_like(prominences)
# Calculate widths at x[peaks] - offset * rel_height
widths, h_eval, left_ips, right_ips = peak_widths(
x, peaks,
rel_height=1,
prominence_data=(offset, left_bases, right_bases)
)
# Check that h_eval is 1 everywhere
np.testing.assert_equal(x[peaks] - h_eval, 1)
# Visualize result
import matplotlib.pyplot as plt
plt.plot(x)
plt.plot(peaks, x[peaks], "x")
plt.hlines(h_eval, left_ips, right_ips, color="C2")
plt.show()
As you can see the width is evaluated for each peak at the same constant offset of 1. By using the original left_bases
and right_bases
as provided by peak_prominences
we are limiting the maximal measured width (e.g. see peaks at 299 and 533). If you want to remove that limitation you must create these arrays yourself.