Алгоритм поиска пика для Python / SciPy - PullRequest
115 голосов
/ 11 ноября 2009

Я могу написать что-то сам, найдя пересечения нуля первой производной или что-то в этом роде, но кажется, что это достаточно общая функция, которая должна быть включена в стандартные библиотеки. Кто-нибудь знает один?

Мое конкретное приложение - это двумерный массив, но обычно его используют для поиска пиков в БПФ и т. Д.

В частности, в подобных проблемах существует множество сильных пиков, а затем множество мелких "пиков", которые просто вызваны шумом, который следует игнорировать. Это всего лишь примеры; не мои фактические данные:

1-мерные пики:

FFT output with peaks

2-мерные пики:

Radon transform output with circled peak

Алгоритм поиска пиков найдет местоположение этих пиков (а не только их значения) и в идеале найдет истинный пик между выборками, а не только индекс с максимальным значением, возможно, используя квадратичную интерполяцию или что-то.

Как правило, вы заботитесь только о нескольких сильных пиках, поэтому они будут выбраны либо потому, что они выше определенного порога, либо потому, что они являются первыми n пиками упорядоченного списка, ранжированного по амплитуде.

Как я уже сказал, я знаю, как сам написать что-то подобное. Я просто спрашиваю, есть ли уже существующая функция или пакет, которые, как известно, хорошо работают.

Обновление:

Я перевел скрипт MATLAB , и он хорошо работает для одномерного случая, но может быть и лучше.

Обновленное обновление:

sixtenbe создал лучшую версию для 1-D случая.

Ответы [ 8 ]

43 голосов
/ 17 декабря 2009

Я смотрю на похожую проблему, и я обнаружил, что некоторые из лучших ссылок получены из химии (из пиков, найденных в данных масс-спецификации). Для хорошего тщательного обзора алгоритмов поиска пиков прочитайте this . Это один из самых ярких обзоров техник нахождения пиков, с которыми я сталкивался. (Вейвлеты являются лучшими для обнаружения пиков такого рода в шумных данных.).

Похоже, ваши пики четко определены и не скрыты в шуме. В таком случае я бы порекомендовал использовать плавные производные савтизки-Голея для нахождения пиков (если вы просто дифференцируете данные выше, у вас будет куча ложных срабатываний). Это очень эффективный метод, и его довольно легко реализовать (вам нужен класс матрицы с основными операциями). Если вы просто найдете пересечение нуля первой производной S-G, я думаю, вы будете счастливы.

25 голосов
/ 02 октября 2018

Функция scipy.signal.find_peaks, как следует из названия, полезна для этого. Но важно хорошо понимать его параметры width, threshold, distance и прежде всего prominence, чтобы получить хороший пик извлечения.

Согласно моим тестам и документации, концепция проточности является «полезной концепцией» для сохранения хороших пиков и устранения шумовых пиков.

Что такое (топографическая) заметность ? Это «минимальная высота, необходимая для спуска, чтобы добраться от вершины к любой более высокой местности» , как это можно увидеть здесь:

enter image description here

Идея такова:

Чем выше выпуклость, тем «важнее» пик.

Тест:

enter image description here

Я специально использовал (шумную) изменяющую частоту синусоиду, потому что это показывает много трудностей. Мы можем видеть, что параметр width здесь не очень полезен, потому что, если вы установите минимальный width слишком высокий, он не сможет отслеживать очень близкие пики в высокочастотной части. Если вы установите width слишком низко, у вас будет много нежелательных пиков в левой части сигнала. Та же проблема с distance. threshold сравнивается только с прямыми соседями, что здесь бесполезно. prominence - это то, что дает лучшее решение. Обратите внимание, что вы можете комбинировать многие из этих параметров!

Код:

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

x = np.sin(2*np.pi*(2**np.linspace(2,10,1000))*np.arange(1000)/48000) + np.random.normal(0, 1, 1000) * 0.15
peaks, _ = find_peaks(x, distance=20)
peaks2, _ = find_peaks(x, prominence=1)      # BEST!
peaks3, _ = find_peaks(x, width=20)
peaks4, _ = find_peaks(x, threshold=0.4)     # Required vertical distance to its direct neighbouring samples, pretty useless
plt.subplot(2, 2, 1)
plt.plot(peaks, x[peaks], "xr"); plt.plot(x); plt.legend(['distance'])
plt.subplot(2, 2, 2)
plt.plot(peaks2, x[peaks2], "ob"); plt.plot(x); plt.legend(['prominence'])
plt.subplot(2, 2, 3)
plt.plot(peaks3, x[peaks3], "vg"); plt.plot(x); plt.legend(['width'])
plt.subplot(2, 2, 4)
plt.plot(peaks4, x[peaks4], "xk"); plt.plot(x); plt.legend(['threshold'])
plt.show()
19 голосов
/ 17 сентября 2013

В scipy есть функция с именем scipy.signal.find_peaks_cwt, которая звучит так, как будто она подходит для ваших нужд, но у меня нет опыта, поэтому я не могу рекомендовать ..

http://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.find_peaks_cwt.html

14 голосов
/ 18 июля 2015

Для тех, кто не уверен, какие алгоритмы пикового поиска использовать в Python, здесь краткий обзор альтернатив: https://github.com/MonsieurV/py-findpeaks

Желая себе эквивалента функции MatLab findpeaks, я обнаружил, что функция detect_peaks от Marcos Duarte - хороший улов.

Довольно прост в использовании:

import numpy as np
from vector import vector, plot_peaks
from libs import detect_peaks
print('Detect peaks with minimum height and distance filters.')
indexes = detect_peaks.detect_peaks(vector, mph=7, mpd=2)
print('Peaks are: %s' % (indexes))

Что даст вам:

detect_peaks results

6 голосов
/ 27 ноября 2009

Обнаружение пиков в спектре надежным способом было изучено довольно мало, например, все работы по синусоидальному моделированию для музыкальных / звуковых сигналов в 80-х годах. Ищите «Синусоидальное моделирование» в литературе.

Если ваши сигналы такие же чистые, как в примере, простое «дайте мне что-нибудь с амплитудой, превышающей N соседей», должно работать достаточно хорошо. Если у вас есть зашумленные сигналы, простой, но эффективный способ - смотреть на свои пики во времени, чтобы отслеживать их: затем вы обнаруживаете спектральные линии вместо спектральных пиков. Итак, вы вычисляете БПФ на скользящем окне вашего сигнала, чтобы получить набор спектра во времени (также называемый спектрограммой). Затем вы смотрите на эволюцию спектрального пика во времени (то есть в последовательных окнах).

4 голосов
/ 12 ноября 2009

Я не думаю, что то, что вы ищете, предоставлено SciPy. Я бы написал код сам, в этой ситуации.

Сплайн-интерполяция и сглаживание из scipy.interpolate довольно хороши и могут быть весьма полезны при подборе пиков и затем определении местоположения их максимума.

1 голос
/ 12 ноября 2009

Существуют стандартные статистические функции и методы для нахождения выбросов в данных, что, вероятно, то, что вам нужно в первом случае. Использование деривативов решит вашу вторую проблему. Однако я не уверен в методе, который решает как непрерывные функции, так и выборочные данные.

0 голосов
/ 03 мая 2016

Перво-наперво, определение «пик» является расплывчатым, если без дальнейших уточнений. Например, для следующих серий вы бы назвали 5-4-5 один пик или два?

1-2-1-2-1-1-5-4-5-1-1-5-1

В этом случае вам понадобятся как минимум два порога: 1) только верхний порог, выше которого может регистрироваться экстремальное значение в качестве пика; и 2) низкий порог, так что крайние значения, разделенные маленькими значениями ниже, станут двумя пиками.

Обнаружение пиков является хорошо изученной темой в литературе по теории экстремальных значений, также известной как "декластеризация экстремальных значений". Его типичные применения включают в себя идентификацию опасных событий на основе непрерывных показаний переменных среды, например, анализ скорости ветра для обнаружения штормовых явлений.

...