Algoritmo de búsqueda de picos para Python / SciPy

136

Puedo escribir algo yo mismo al encontrar cruces por cero de la primera derivada o algo así, pero parece una función lo suficientemente común como para incluirla en las bibliotecas estándar. Alguien sabe de uno?

Mi aplicación particular es una matriz 2D, pero generalmente se usaría para encontrar picos en FFT, etc.

Específicamente, en este tipo de problemas, hay múltiples picos fuertes, y luego muchos "picos" más pequeños que son causados ​​por el ruido que debe ignorarse. Estos son solo ejemplos; no mis datos reales:

Picos unidimensionales:

Salida FFT con picos

Picos bidimensionales:

Salida de transformación de radón con pico en círculo

El algoritmo de búsqueda de picos encontraría la ubicación de estos picos (no solo sus valores), e idealmente encontraría el verdadero pico entre muestras, no solo el índice con el valor máximo, probablemente usando interpolación cuadrática o algo así.

Por lo general, solo le interesan unos pocos picos fuertes, por lo que se elegirían porque están por encima de un cierto umbral o porque son los primeros n picos de una lista ordenada, clasificados por amplitud.

Como dije, sé cómo escribir algo como esto yo mismo. Solo pregunto si hay una función o paquete preexistente que funcione bien.

Actualizar:

Yo traduje una secuencia de comandos de MATLAB y funciona bastante bien para el caso 1-D, pero podría ser mejor.

Actualización actualizada:

sixtenbe creó una mejor versión para el caso 1-D.

endolito
fuente
@endolith ¿Tiene el archivo MATLAB original que tradujo a Python para esto? ¡Gracias!
Spacey
2
¿Qué pasa con esto: docs.scipy.org/doc/scipy/reference/generated/…
guiones
1
@endolith Sé que esta pregunta es bastante antigua, pero es bastante útil;) Pasé algunas horas esta mañana find_peaks, así que agregué esta respuesta que podría ser útil para referencia futura. (Estoy seguro de que ya ha encontrado esto desde 2009, ¡pero es para otras personas + yo mismo cuando me haga la pregunta nuevamente en unos años!)
Basj

Respuestas:

74

La función scipy.signal.find_peaks, como su nombre lo indica, es útil para esto. Pero es importante comprender bien sus parámetros widthy threshold, distance sobre todo,prominence obtener una buena extracción de picos.

Según mis pruebas y la documentación, el concepto de prominencia es "el concepto útil" para mantener los picos buenos y descartar los picos ruidosos.

¿Qué es la prominencia (topográfica) ? Es "la altura mínima necesaria para descender para llegar desde la cumbre a cualquier terreno más alto" , como se puede ver aquí:

ingrese la descripción de la imagen aquí

La idea es:

Cuanto mayor es la prominencia, más "importante" es el pico.

Prueba:

ingrese la descripción de la imagen aquí

Usé un sinusoide (ruidoso) que varía en frecuencia a propósito porque muestra muchas dificultades. Podemos ver que el widthparámetro no es muy útil aquí porque si establece un mínimo widthdemasiado alto, entonces no podrá rastrear picos muy cercanos en la parte de alta frecuencia. Si establece widthdemasiado bajo, tendría muchos picos no deseados en la parte izquierda de la señal. Mismo problema con distance. thresholdsolo se compara con los vecinos directos, lo que no es útil aquí. prominenceEs el que da la mejor solución. ¡Tenga en cuenta que puede combinar muchos de estos parámetros!

Código:

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()
Basj
fuente
Esto es lo que busco. ¿Pero conoces alguna implementación que encuentre prominencia en la matriz 2D?
Jason
43

Estoy viendo un problema similar, y he encontrado que algunas de las mejores referencias provienen de la química (de picos encontrados en datos de especificaciones de masa). Para una buena revisión exhaustiva de los algoritmos de búsqueda de picos, lea esto . Esta es una de las mejores revisiones más claras de las técnicas de búsqueda de picos que he encontrado. (Las wavelets son las mejores para encontrar picos de este tipo en datos ruidosos).

Parece que tus picos están claramente definidos y no están ocultos en el ruido. Siendo ese el caso, recomendaría usar derivados suaves de savtizky-golay para encontrar los picos (si solo diferencia los datos anteriores, tendrá un montón de falsos positivos). Esta es una técnica muy efectiva y es bastante fácil de implementar (necesita una clase de matriz con operaciones básicas). Si simplemente encuentra el cruce por cero de la primera derivada SG, creo que será feliz.

Pablo
fuente
2
Estaba buscando una solución de uso general, no una que solo funcione en esas imágenes en particular. Adapte un script de MATLAB a Python y funciona de manera decente.
endolito
1
Tocar el asunto exacto. Matlab es una buena fuente de algoritmos. ¿Qué técnica usa el guión? (Por cierto, SG es una técnica de propósito muy general).
Paul
2
Lo vinculé arriba. Básicamente solo busca máximos locales que son más grandes que un cierto umbral por encima de sus vecinos. Ciertamente hay mejores métodos.
endolito
1
@Paul Marqué esa página como favorita. En resumen, ¿qué técnica específica crees que funcionó mejor para este negocio de picking pico?
Spacey
¿Por qué los ceros de derivada son mejores que simplemente probar si un punto medio de tres puntos es más grande o más pequeño de los otros dos? Ya he aplicado sg transfor, parece un costo adicional.
kirill_igum
20

Hay una función en scipy llamada scipy.signal.find_peaks_cwtque parece adecuada para sus necesidades, sin embargo, no tengo experiencia con ella, así que no puedo recomendarla.

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

Hanan Shteingart
fuente
12
Sí, eso no existía cuando pregunté esto, y todavía no estoy seguro de cómo usarlo
Endolith
1
Agregaste esto hace un tiempo, pero funcionó de maravilla. Usarlo es simple como un pastel. Simplemente pase la matriz y otra matriz (es decir, np.arange (1,10)) que enumera todos los anchos de picos que desea; Un buen beneficio para filtrar picos delgados o anchos si es necesario. ¡Gracias de nuevo!
Millas el
15

Para aquellos que no están seguros sobre qué algoritmos de búsqueda de picos usar en Python, aquí hay una descripción general rápida de las alternativas: https://github.com/MonsieurV/py-findpeaks

Al querer un equivalente a la findpeaksfunción MatLab , descubrí que la función detect_peaks de Marcos Duarte es una buena captura.

Bastante fácil de usar:

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))

Lo que te dará:

resultados de detect_peaks

Yoan Tournade
fuente
1
Desde que se escribió esta publicación, find_peaksse agregó la función a scipy.
onewhaleid
6

La detección de picos en un espectro de manera confiable se ha estudiado bastante, por ejemplo, todo el trabajo sobre modelado sinusoidal para señales de música / audio en los años 80. Busque "Modelado sinusoidal" en la literatura.

Si sus señales son tan claras como el ejemplo, un simple "dame algo con una amplitud mayor que N vecinos" debería funcionar razonablemente bien. Si tiene señales ruidosas, una forma simple pero efectiva es mirar sus picos a tiempo, rastrearlos: luego detecta líneas espectrales en lugar de picos espectrales. IOW, calcula la FFT en una ventana deslizante de su señal, para obtener un conjunto de espectro a tiempo (también llamado espectrograma). Luego observa la evolución del pico espectral en el tiempo (es decir, en ventanas consecutivas).

David Cournapeau
fuente
Mira los picos a tiempo? Detectar líneas espectrales? No estoy seguro de lo que esto significa. ¿Funcionaría para las ondas cuadradas?
endolito el
Oh, estás hablando de usar STFT en lugar de FFT. Esta pregunta no se trata específicamente de FFT; eso es solo un ejemplo. Se trata de encontrar los picos en cualquier matriz general 1D o 2D.
endolito el
4

No creo que lo que estás buscando sea provisto por SciPy. Escribiría el código yo mismo, en esta situación.

La interpolación de spline y el suavizado de scipy.interpolate son bastante agradables y pueden ser bastante útiles para ajustar picos y luego encontrar la ubicación de su máximo.

Eric O Lebigot
fuente
16
Mis disculpas, pero creo que esto debería ser un comentario, no una respuesta. Simplemente sugiere escribirlo uno mismo, con una sugerencia vaga para funciones que podrían ser útiles (las de la respuesta de Paul son mucho más relevantes, por cierto).
Ami Tavory
1

Existen funciones y métodos estadísticos estándar para encontrar valores atípicos en los datos, que probablemente sea lo que necesita en el primer caso. Usar derivados resolvería tu segundo. Sin embargo, no estoy seguro de un método que resuelva funciones continuas y datos muestreados.

puntero nulo
fuente
0

Lo primero es lo primero, la definición de "pico" es vaga si no hay más especificaciones. Por ejemplo, para la siguiente serie, ¿llamaría 5-4-5 un pico o dos?

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

En este caso, necesitará al menos dos umbrales: 1) un umbral alto solo por encima del cual puede registrarse un valor extremo como un pico; y 2) un umbral bajo para que los valores extremos separados por valores pequeños por debajo se conviertan en dos picos.

La detección de picos es un tema bien estudiado en la literatura de la Teoría del Valor Extremo, también conocido como "desenmascaramiento de valores extremos". Sus aplicaciones típicas incluyen identificar eventos de peligro basados ​​en lecturas continuas de variables ambientales, por ejemplo, analizar la velocidad del viento para detectar eventos de tormenta.

Ian Liu
fuente