Algorithme de recherche de pics pour Python / SciPy

136

Je peux écrire quelque chose moi-même en trouvant des passages par zéro du premier dérivé ou quelque chose du genre, mais cela semble être une fonction assez courante pour être incluse dans les bibliothèques standard. Quelqu'un en connait un?

Mon application particulière est un tableau 2D, mais il serait généralement utilisé pour trouver des pics dans les FFT, etc.

Plus précisément, dans ces types de problèmes, il y a plusieurs pics forts, puis beaucoup de "pics" plus petits qui sont juste causés par du bruit qui doit être ignoré. Ce ne sont que des exemples; pas mes données réelles:

Pics à une dimension:

Sortie FFT avec pics

Pics bidimensionnels:

Sortie de transformée de radon avec pic encerclé

L'algorithme de recherche de pics trouverait l'emplacement de ces pics (pas seulement leurs valeurs) et, idéalement, trouverait le véritable pic inter-échantillon, pas seulement l'indice avec la valeur maximale, probablement en utilisant une interpolation quadratique ou quelque chose du genre.

En règle générale, vous ne vous souciez que de quelques pics forts, ils seraient donc soit choisis parce qu'ils sont au-dessus d'un certain seuil, soit parce qu'ils sont les n premiers pics d'une liste ordonnée, classés par amplitude.

Comme je l'ai dit, je sais écrire moi-même quelque chose comme ça. Je demande simplement s'il existe une fonction ou un package préexistant qui fonctionne bien.

Mettre à jour:

J'ai traduit un script MATLAB et cela fonctionne décemment pour le cas 1-D, mais pourrait être mieux.

Mise à jour mise à jour:

sixtenbe a créé une meilleure version pour le cas 1-D.

endolithe
la source
@endolith Avez-vous le fichier MATLAB original que vous avez traduit en python pour cela? Merci!
Spacey
2
Qu'en est-il de ceci: docs.scipy.org/doc/scipy/reference/generated/…
dashesy
1
@endolith Je sais que cette question est assez ancienne, mais c'est assez utile;) J'ai passé quelques heures ce matin dessus find_peaks, j'ai donc ajouté cette réponse qui pourrait être utile pour référence future. (Je suis sûr que vous l'avez déjà trouvé depuis 2009 mais c'est pour d'autres personnes + moi-même quand je me poserai la question à nouveau dans quelques années!)
Basj

Réponses:

74

La fonction scipy.signal.find_peaks, comme son nom l'indique, est utile pour cela. Mais il est important de bien comprendre ses paramètres width, threshold, distance et surtoutprominence d'obtenir une bonne extraction de pointe.

D'après mes tests et la documentation, le concept de proéminence est "le concept utile" pour garder les bons pics, et écarter les pics bruyants.

Qu'est-ce que la proéminence (topographique) ? C'est "la hauteur minimale nécessaire pour descendre pour aller du sommet à tout terrain plus élevé" , comme on peut le voir ici:

entrez la description de l'image ici

L'idée est:

Plus la proéminence est élevée, plus le pic est "important".

Tester:

entrez la description de l'image ici

J'ai utilisé une sinusoïde (bruyante) à fréquence variable car elle présente de nombreuses difficultés. Nous pouvons voir que le widthparamètre n'est pas très utile ici car si vous définissez un minimum widthtrop élevé, il ne pourra pas suivre les pics très proches dans la partie haute fréquence. Si vous réglez widthtrop bas, vous auriez de nombreux pics indésirables dans la partie gauche du signal. Même problème avec distance. thresholdse compare uniquement avec les voisins directs, ce qui n'est pas utile ici. prominenceest celui qui donne la meilleure solution. Notez que vous pouvez combiner plusieurs de ces paramètres!

Code:

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
la source
C'est ce que je recherche. Mais connaissez-vous une implémentation qui trouve sa place dans le tableau 2D?
Jason
43

Je regarde un problème similaire et j'ai trouvé que certaines des meilleures références proviennent de la chimie (des pics trouvés dans les données de spécification de masse). Pour un bon examen approfondi des algorithmes de recherche de pics, lisez ceci . C'est l'une des meilleures critiques les plus claires des techniques de recherche de pics que j'ai rencontrées. (Les ondelettes sont les meilleures pour trouver des pics de ce type dans des données bruyantes.).

Il semble que vos pics sont clairement définis et ne sont pas cachés dans le bruit. Cela étant le cas, je recommanderais d'utiliser des dérivés savtizky-golay lisses pour trouver les pics (si vous différenciez simplement les données ci-dessus, vous aurez un désordre de faux positifs.). C'est une technique très efficace et assez facile à implémenter (vous avez besoin d'une classe de matrice avec des opérations de base). Si vous trouvez simplement le passage à zéro du premier dérivé SG, je pense que vous serez heureux.

Paul
la source
2
Je cherchais une solution à usage général, pas une solution qui ne fonctionne que sur ces images particulières. J'ai adapté un script MATLAB à Python et cela fonctionne décemment.
endolith
1
Droit dessus. Matlab est une bonne source d'algorithmes. Quelle technique le script utilise-t-il? (BTW, SG est une technique très générale).
Paul
2
Je l'ai lié ci-dessus. Il recherche simplement les maxima locaux supérieurs à un certain seuil au-dessus de leurs voisins. Il existe certainement de meilleures méthodes.
endolith
1
@Paul J'ai mis cette page en signet. IYO et en résumé, quelle technique spécifique pensez-vous avoir fonctionné le mieux pour cette activité de pointe?
Spacey
pourquoi les zéros de la dérivée sont-ils meilleurs que de simplement tester si un milieu sur trois points est plus grand ou plus petit des deux autres. J'ai déjà appliqué sg transfor, cela semble être un coût supplémentaire.
kirill_igum
20

Il y a une fonction dans scipy nommée scipy.signal.find_peaks_cwtqui semble convenir à vos besoins, mais je n'ai pas d'expérience avec elle, donc je ne peux pas recommander.

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

Hanan Shteingart
la source
12
Ouais, cela n'existait pas quand j'ai posé cette question, et je ne sais toujours pas comment l'utiliser
endolith
1
Vous l'avez ajouté il y a quelque temps, mais cela a fonctionné à merveille. Son utilisation est simple comme bonjour. Passez simplement le tableau, et un autre tableau (ie. Np.arange (1,10)) qui répertorie toutes les largeurs de pics que vous souhaitez; bel avantage à filtrer pour les pics maigres ou larges si on en a besoin. Merci encore!
Miles
15

Pour ceux qui ne savent pas quels algorithmes de recherche de pics utiliser en Python, voici un rapide aperçu des alternatives: https://github.com/MonsieurV/py-findpeaks

En me voulant un équivalent à la findpeaksfonction MatLab , j'ai trouvé que la fonction detect_peaks de Marcos Duarte est une bonne prise.

Assez facile à utiliser:

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

Ce qui vous donnera:

detect_peaks résultats

Tournade Yoan
la source
1
Depuis que ce message a été écrit, la find_peaksfonction a été ajoutée scipy.
onewhaleid
6

La détection fiable des pics d'un spectre a été assez étudiée, par exemple tous les travaux sur la modélisation sinusoïdale des signaux musicaux / audio dans les années 80. Recherchez «Modélisation sinusoïdale» dans la littérature.

Si vos signaux sont aussi propres que l'exemple, un simple "donnez-moi quelque chose avec une amplitude supérieure à N voisins" devrait fonctionner raisonnablement bien. Si vous avez des signaux bruyants, un moyen simple mais efficace est de regarder vos pics dans le temps, de les suivre: vous détectez alors des raies spectrales au lieu de pics spectraux. IOW, vous calculez la FFT sur une fenêtre glissante de votre signal, pour obtenir un ensemble de spectre dans le temps (également appelé spectrogramme). Vous regardez ensuite l'évolution du pic spectral dans le temps (ie dans des fenêtres consécutives).

David Cournapeau
la source
Regardez les pics dans le temps? Détecter les raies spectrales? Je ne sais pas ce que cela signifie. Cela fonctionnerait-il pour les ondes carrées?
endolith
Oh, vous parlez d'utiliser STFT au lieu de FFT. Cette question ne concerne pas spécifiquement les FFT; ce n'est qu'un exemple. Il s'agit de trouver les pics dans n'importe quel tableau 1D ou 2D général.
endolith
4

Je ne pense pas que ce que vous recherchez soit fourni par SciPy. J'écrirais le code moi-même, dans cette situation.

L'interpolation et le lissage des splines de scipy.interpolate sont assez agréables et pourraient être très utiles pour ajuster les pics et trouver ensuite l'emplacement de leur maximum.

Eric O Lebigot
la source
16
Mes excuses, mais je pense que cela devrait être un commentaire, pas une réponse. Cela suggère simplement de l'écrire par soi-même, avec une vague suggestion de fonctions qui pourraient être utiles (celles de la réponse de Paul sont beaucoup plus pertinentes, d'ailleurs).
Ami Tavory
1

Il existe des fonctions et des méthodes statistiques standard pour trouver des valeurs aberrantes aux données, ce dont vous avez probablement besoin dans le premier cas. L'utilisation de dérivés résoudrait votre deuxième. Je ne suis cependant pas sûr d'une méthode qui résout à la fois les fonctions continues et les données échantillonnées.

pointeur nul
la source
0

Tout d'abord, la définition de «pic» est vague si elle n'est pas spécifiée. Par exemple, pour la série suivante, appelleriez-vous 5-4-5 un pic ou deux?

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

Dans ce cas, vous aurez besoin d'au moins deux seuils: 1) un seuil haut seulement au-dessus duquel une valeur extrême peut enregistrer comme un pic; et 2) un seuil bas pour que les valeurs extrêmes séparées par de petites valeurs en dessous de lui deviennent deux pics.

La détection des pics est un sujet bien étudié dans la littérature sur la théorie des valeurs extrêmes, également connu sous le nom de «désintégration des valeurs extrêmes». Ses applications typiques comprennent l'identification des événements dangereux sur la base de lectures continues de variables environnementales, par exemple l'analyse de la vitesse du vent pour détecter les événements de tempête.

Ian Liu
la source