HOWTO · Python
在 Python 中查詢峰值
本教程演示了 Python 中的尋峰演算法。
本頁內容
區域最大值是大於附近樣本的數值,但不一定是整個序列中的最大值。對一維序列而言,若只需要簡單的結果,可以比較每個內部樣本與相鄰值;若需要篩選候選峰值,則可使用 scipy.signal.find_peaks()。這些函式回傳的是原始資料中以 0 為起始的索引,可用索引取得峰值。
使用 NumPy 尋找簡單的區域最大值
這個精簡範例使用一維陣列,並列出每個偵測到的索引及其值。
import numpy as np
data = np.array([1, 3, 7, 1, 2, 6, 0, 5, 0, 4])
local_maxima = (data[1:-1] > data[:-2]) & (data[1:-1] > data[2:])
indices = np.flatnonzero(local_maxima) + 1
print("indices:", indices.tolist())
print("values:", data[indices].tolist())
indices: [2, 5, 7]
values: [7, 6, 5]
這兩項比較會要求樣本嚴格大於左右兩側緊鄰的值。切片只比較內部樣本,因此第一個和最後一個元素不會列入候選;值相同的平台區段也會被排除。索引位移會將較短的比較陣列對應回 data 中的位置。當這項精確規則符合資料特性時,NumPy 方法就很實用。對這筆輸入資料,偵測到的索引是 [2, 5, 7],對應的值是 [7, 6, 5]。
使用 scipy.signal.find_peaks() 篩選峰值
若要一般性地搜尋一維資料中的峰值,SciPy 的 scipy.signal.find_peaks() 會回傳區域最大值的索引,也能套用選用條件。以下範例會選出高度至少為 3 的峰值,並印出索引及其值:
import numpy as np
from scipy.signal import find_peaks
data = np.array([1, 3, 7, 1, 2, 6, 0, 5, 0, 4])
indices, properties = find_peaks(data, height=3, distance=2, prominence=1)
print("indices:", indices.tolist())
print("values:", data[indices].tolist())
indices: [2, 5, 7]
values: [7, 6, 5]
height 會依峰值的數值進行篩選;distance 會要求保留的峰值之間至少相隔指定的樣本數;prominence 則衡量峰值相較於周圍區域有多突出。distance 會影響哪些峰值得以保留,但不會作為屬性回傳。其他選用條件還包括 width 和 threshold。SciPy 會回傳索引,以及一個屬性字典;字典包含所要求且適用條件所產生的屬性。對這筆輸入資料和這些引數,回傳的索引是 [2, 5, 7],對應的值是 [7, 6, 5]。請選擇能描述訊號特性的條件,不要假設每個區域最大值都有意義。
使用 scipy.signal.argrelextrema() 尋找相對極值
若要比較指定鄰域內的樣本,請使用 scipy.signal.argrelextrema()。傳入 np.greater 等比較器即可尋找最大值,並將 order 設為比較時納入的左右兩側樣本數。此函式會回傳索引陣列組成的 tuple,每個輸入維度各有一個索引陣列;若輸入是一維陣列,請使用 [0] 取得索引。
import numpy as np
from scipy.signal import argrelextrema
data = np.array([1, 3, 7, 1, 2, 6, 0, 5, 0, 4])
indices = argrelextrema(data, np.greater, order=1)[0]
print("indices:", indices.tolist())
print("values:", data[indices].tolist())
indices: [2, 5, 7]
values: [7, 6, 5]
設定 order=1 時,每個候選值會與左右各一個樣本比較。較大的 order 會使用更寬的鄰域,因此可能回傳不同的相對極值。對這筆輸入資料並設定 order=1 時,回傳的索引是 [2, 5, 7],對應的值是 [7, 6, 5]。
使用 detecta.detect_peaks() 作為替代方案
detecta.detect_peaks() 是另一種適用於一維序列的第三方方案。請在專案環境中安裝相依套件 python -m pip install detecta,再將樣本資料傳給 detect_peaks():
data = [1, 3, 7, 1, 2, 6, 0, 5, 0, 4]
try:
from detecta import detect_peaks
except ImportError:
print("Install detecta with: python -m pip install detecta")
else:
indices = detect_peaks(data)
values = [data[index] for index in indices]
print("indices:", list(indices))
print("values:", values)
對這組樣本序列,預期的峰值索引(以 0 為起始)是 [2, 5, 7],對應的值是 [7, 6, 5]。提供的執行證據只確認了缺少相依套件時顯示的訊息,因此這裡的 detecta 結果是預期值,用來比較方法,並非實際執行所得。請保留匯入防護:若無法使用 detecta,程式會印出安裝指令,而不會嘗試偵測。
選擇峰值偵測方法
若你只需要依據緊鄰樣本嚴格比較來判定最大值,而且希望直接掌握運作邏輯,請使用 NumPy 比較法。若需要依高度、間距、突出度或寬度等條件篩選,請選擇 scipy.signal.find_peaks()。若極值的定義取決於比較鄰域和比較器,請使用 scipy.signal.argrelextrema()。如果第三方 API 符合專案需求,而且你願意新增相依套件,也可以考慮 detecta.detect_peaks()。
所有回傳位置都是輸入序列中以 0 為起始的索引。解讀結果前,請先確認方法的定義是否符合資料特性:端點處理方式、平台區段處理方式和篩選條件都可能不同。以上範例適用於一維數值資料中的區域最大值;多維峰值偵測則需要使用適合該資料形狀的方法。