Алгоритм поиска пика для Python / SciPy


136

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

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

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

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

БПФ выход с пиками

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

Выход преобразования радона с обведенным пиком

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

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

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

Обновить:

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

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

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


@endolith У вас есть исходный файл MATLAB, который вы для этого перевели на python? Спасибо!
Спейси


2
Что насчет этого: docs.scipy.org/doc/scipy/reference/generated/…
dashesy

1
@endolith Я знаю, что этот вопрос довольно старый, но он довольно полезен;) Я потратил несколько часов сегодня утром find_peaks, поэтому я добавил этот ответ, который может быть полезен для справок в будущем. (Я уверен, что вы уже нашли это с 2009 года, но это для других людей + меня, когда я снова задам себе этот вопрос через несколько лет!)
Basj

Ответы:


74

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

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

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

введите описание изображения здесь

Идея заключается в следующем:

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

Тест:

введите описание изображения здесь

Я специально использовал (зашумленную) частотно-изменяющуюся синусоиду, потому что она показывает много трудностей. Мы видим, что 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()

Это то, что мне нужно. Но знаете ли вы какую-либо реализацию, которая находит выдающееся положение в 2D массиве?
Джейсон

43

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

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


2
Я искал решение общего назначения, а не то, которое работает только на этих конкретных изображениях. Я адаптировал сценарий MATLAB для Python, и он работает прилично.
эндолит

1
Право на. Matlab - хороший источник для алгоритмов. Какую технику использует сценарий? (Кстати, SG - метод очень общего назначения).
Пол

2
Я связал это выше. В основном он просто ищет локальные максимумы, которые превышают определенный порог над их соседями. Конечно, есть методы получше.
эндолит

1
@Paul Я добавил эту страницу в закладки. IYO и в итоге, какой конкретный метод, по вашему мнению, лучше всего работал для этого бизнеса по сбору пиков?
Спейси

почему нули производной лучше, чем просто проверить, больше ли средняя из трех точек двух других. Я уже применил SG Transfor, кажется, это дополнительная плата.
kirill_igum

20

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

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


12
Да, этого не было, когда я спросил об этом, и я все еще не уверен, как его использовать
эндолит

1
Вы добавили это некоторое время назад, но это сработало. Используя это просто как пирог. Просто передайте массив и другой массив (т.е. np.arange (1,10)), в котором перечислены все значения ширины пиков, которые вы бы хотели; Хорошая выгода для фильтрации на тощие или широкие пики, если это необходимо. Еще раз спасибо!
Майлз

15

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

Желая себе эквивалента findpeaksфункции MatLab , я обнаружил, что функция 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 результаты


1
Поскольку этот пост был написан, find_peaksфункция была добавлена ​​в scipy.
onewhaleid

6

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

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


Посмотрите на пики во времени? Обнаружить спектральные линии? Я не уверен, что это значит. Подойдет ли это для прямоугольных волн?
Эндолит

О, вы говорите об использовании STFT вместо FFT. Этот вопрос не о БПФ конкретно; это просто пример. Речь идет о поиске пиков в любом общем 1D или 2D массиве.
эндолит

4

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

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


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

1

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


0

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

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

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.