Algoritmo di rilevamento dei picchi per Python / SciPy


136

Posso scrivere qualcosa da solo trovando zero-incroci della prima derivata o qualcosa del genere, ma sembra una funzione abbastanza comune da essere inclusa nelle librerie standard. Qualcuno ne conosce uno?

La mia particolare applicazione è un array 2D, ma di solito sarebbe usato per trovare picchi negli FFT, ecc.

In particolare, in questi tipi di problemi, ci sono più picchi forti, e quindi molti "picchi" più piccoli che sono solo causati dal rumore che dovrebbe essere ignorato. Questi sono solo esempi; non i miei dati effettivi:

Picchi monodimensionali:

Uscita FFT con picchi

Picchi bidimensionali:

Uscita di trasformazione del radon con picco cerchiato

L'algoritmo di ricerca del picco troverebbe la posizione di questi picchi (non solo i loro valori) e idealmente troverebbe il vero picco tra campioni, non solo l'indice con il valore massimo, probabilmente usando l' interpolazione quadratica o qualcosa del genere.

In genere ti preoccupi solo di alcuni picchi forti, quindi potrebbero essere scelti perché sono al di sopra di una certa soglia o perché sono i primi n picchi di un elenco ordinato, classificati in base all'ampiezza.

Come ho detto, so come scrivere qualcosa del genere da solo. Sto solo chiedendo se esiste una funzione o un pacchetto preesistenti che funzionano bene.

Aggiornare:

Ho tradotto uno script MATLAB e funziona decentemente per il caso 1-D, ma potrebbe essere migliore.

Aggiornamento aggiornato:

sixtenbe ha creato una versione migliore per il caso 1-D.


@endolith Hai il file MATLAB originale che hai tradotto in python per questo? Grazie!
Spacey,



1
@endolith So che questa domanda è piuttosto vecchia, ma è piuttosto utile;) Ho trascorso qualche ora stamattina find_peaks, quindi ho aggiunto questa risposta che potrebbe essere utile per riferimento futuro. (Sono sicuro che lo hai già trovato dal 2009, ma è per altre persone + me stesso quando mi farò di nuovo la domanda tra qualche anno!)
Basj

Risposte:


74

La funzione scipy.signal.find_peaks, come suggerisce il nome, è utile per questo. Ma è importante capire bene i suoi parametri width, threshold, distance e, soprattutto,prominence per ottenere una buona estrazione di picco.

Secondo i miei test e la documentazione, il concetto di prominenza è "il concetto utile" per mantenere le buone vette e scartare le vette rumorose.

Cos'è la prominenza (topografica) ? È "l'altezza minima necessaria per scendere dalla vetta a qualsiasi terreno più elevato" , come si può vedere qui:

inserisci qui la descrizione dell'immagine

L'idea è:

Maggiore è la prominenza, più "importante" è il picco.

Test:

inserisci qui la descrizione dell'immagine

Ho usato apposta una sinusoide (rumorosa) che varia di frequenza perché mostra molte difficoltà. Possiamo vedere che il widthparametro non è molto utile qui perché se si imposta un minimo widthtroppo alto, allora non sarà in grado di tracciare picchi molto vicini nella parte ad alta frequenza. Se si imposta widthtroppo basso, si avrebbero molti picchi indesiderati nella parte sinistra del segnale. Lo stesso problema con distance. thresholdconfronta solo con i vicini diretti, il che non è utile qui. prominenceè quello che offre la soluzione migliore. Nota che puoi combinare molti di questi parametri!

Codice:

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

Questo è quello che sto cercando. Ma conosci qualche implementazione che trova risalto nell'array 2D?
Jason

43

Sto osservando un problema simile e ho scoperto che alcuni dei migliori riferimenti provengono dalla chimica (dai picchi trovati nei dati delle specifiche di massa). Per una buona revisione approfondita degli algoritmi di individuazione dei picchi, leggi questo . Questa è una delle recensioni più chiare sulle tecniche di ricerca del picco che ho incontrato. (Le wavelet sono le migliori per trovare picchi di questo tipo in dati rumorosi.).

Sembra che i tuoi picchi siano chiaramente definiti e non siano nascosti nel rumore. Stando così le cose, consiglierei di usare derivati ​​savtizky-golay fluidi per trovare i picchi (se differenzi i dati sopra, avrai un casino di falsi positivi). Questa è una tecnica molto efficace ed è abbastanza facile da implementare (è necessaria una classe di matrici con operazioni di base). Se trovi semplicemente l'attraversamento zero della prima derivata SG penso che sarai felice.


2
Stavo cercando una soluzione di uso generale, non una che funzioni solo su quelle immagini particolari. Ho adattato uno script MATLAB a Python e funziona decentemente.
endolith il

1
Va bene. Matlab è una buona fonte di algoritmi. Quale tecnica utilizza lo script? (A proposito, SG è una tecnica molto generica).
Paul,

2
L'ho collegato sopra. Fondamentalmente cerca solo i massimi locali che sono più grandi di una certa soglia al di sopra dei loro vicini. Esistono sicuramente metodi migliori.
endolith,

1
@Paul Ho aggiunto la pagina ai preferiti. IYO e in sintesi, quale specifica tecnica pensavi funzionasse meglio per questa attività di picco picking?
Spacey,

perché gli zeri di derivata sono migliori del semplice test se un centro su tre punti è più grande o più piccolo degli altri due. ho già applicato sg transfor, sembra un costo aggiuntivo.
kirill_igum,

20

C'è una funzione in scipy scipy.signal.find_peaks_cwtche sembra adatta alle tue esigenze, tuttavia non ho esperienza con essa, quindi non posso raccomandare ..

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


12
Sì, questo non esisteva quando l'ho chiesto, e non sono ancora sicuro di come usarlo
endolith,

1
L'hai aggiunto qualche tempo fa, ma ha funzionato benissimo. Usarlo è semplice come una torta. Basta passare l'array e un altro array (es. Np.arange (1,10)) che elenca tutte le larghezze dei picchi che si desidera; bel vantaggio di filtrare per picchi magri o larghi se uno ha bisogno. Grazie ancora!
Miglia

15

Per coloro che non sono sicuri su quali algoritmi di rilevamento dei picchi utilizzare in Python, ecco una rapida panoramica delle alternative: https://github.com/MonsieurV/py-findpeaks

Volendo me stesso un equivalente della findpeaksfunzione MatLab , ho scoperto che la funzione detect_peaks di Marcos Duarte è una buona cattura.

Abbastanza facile da usare:

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

Che ti darà:

risultati detect_peaks


1
Da quando questo post è stato scritto, la find_peaksfunzione è stata aggiunta a scipy.
onewhaleid,

6

Il rilevamento di picchi in uno spettro in modo affidabile è stato studiato un po ', ad esempio tutto il lavoro sulla modellazione sinusoidale per i segnali musicali / audio negli anni '80. Cerca "Modellazione sinusoidale" in letteratura.

Se i tuoi segnali sono puliti come nell'esempio, un semplice "dammi qualcosa con un'ampiezza superiore a N vicini" dovrebbe funzionare ragionevolmente bene. Se hai segnali rumorosi, un modo semplice ma efficace è guardare i tuoi picchi nel tempo, per seguirli: poi rilevi le linee spettrali anziché i picchi spettrali. IOW, calcoli la FFT su una finestra scorrevole del tuo segnale, per ottenere un set di spettro nel tempo (chiamato anche spettrogramma). Quindi si osserva l'evoluzione del picco spettrale nel tempo (cioè in finestre consecutive).


Guarda picchi nel tempo? Rileva linee spettrali? Non sono sicuro di cosa significhi. Funzionerebbe per le onde quadrate?
endolito il

Oh, stai parlando di usare STFT invece di FFT. Questa domanda non riguarda in particolare le FFT; questo è solo un esempio. Si tratta di trovare i picchi in qualsiasi array 1D o 2D generale.
endolith

4

Non credo che ciò che stai cercando sia fornito da SciPy. Scriverei il codice da solo, in questa situazione.

L'interpolazione della spline e il livellamento da scipy.interpolate sono piuttosto belli e potrebbero essere molto utili per adattare i picchi e quindi trovare la posizione del loro massimo.


16
Mi scuso, ma penso che questo dovrebbe essere un commento, non una risposta. Suggerisce solo di scriverlo da soli, con un vago suggerimento per funzioni che potrebbero essere utili (quelle nella risposta di Paolo sono molto più rilevanti, per inciso).
Ami Tavory,

1

Esistono funzioni e metodi statistici standard per trovare valori anomali nei dati, che è probabilmente ciò di cui hai bisogno nel primo caso. L'uso di derivati ​​risolverebbe il tuo secondo. Non sono sicuro di un metodo che risolva sia funzioni continue che dati campionati, tuttavia.


0

Per prima cosa, la definizione di "picco" è vaga se senza ulteriori specifiche. Ad esempio, per le seguenti serie, chiameresti 5-4-5 un picco o due?

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

In questo caso, avrai bisogno di almeno due soglie: 1) una soglia alta solo al di sopra della quale un valore estremo può essere registrato come picco; e 2) una soglia bassa in modo che i valori estremi separati da piccoli valori al di sotto diventino due picchi.

Il rilevamento dei picchi è un argomento ben studiato nella letteratura sulla teoria dei valori estremi, noto anche come "declusterizzazione di valori estremi". Le sue applicazioni tipiche includono l'identificazione di eventi pericolosi sulla base di letture continue di variabili ambientali, ad esempio l'analisi della velocità del vento per rilevare eventi di tempesta.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.