Algoritmo de localização de pico para Python / SciPy


136

Eu mesmo posso escrever algo encontrando cruzamentos zero da primeira derivada ou algo assim, mas parece uma função suficientemente comum para ser incluída nas bibliotecas padrão. Alguém conhece um?

Minha aplicação em particular é uma matriz 2D, mas geralmente seria usada para encontrar picos em FFTs, etc.

Especificamente, nesses tipos de problemas, existem vários picos fortes e, em seguida, muitos "picos" menores que são causados ​​apenas por ruídos que devem ser ignorados. Estes são apenas exemplos; não meus dados reais:

Picos unidimensionais:

Saída FFT com picos

Picos bidimensionais:

Saída de transformação de radônio com pico circulado

O algoritmo de localização de pico encontraria a localização desses picos (não apenas seus valores) e, idealmente, encontraria o verdadeiro pico entre amostras, não apenas o índice com valor máximo, provavelmente usando interpolação quadrática ou algo assim.

Normalmente, você se preocupa apenas com alguns picos fortes, para que eles sejam escolhidos porque estão acima de um determinado limite ou porque são os primeiros n picos de uma lista ordenada, classificados por amplitude.

Como eu disse, sei escrever algo assim sozinho. Só estou perguntando se existe uma função ou pacote preexistente que funcione bem.

Atualizar:

I traduzido um script MATLAB e funciona decentemente para o caso 1-D, mas poderia ser melhor.

Atualização atualizada:

sixtenbe criou uma versão melhor para o gabinete 1-D.


@endolith Você tem o arquivo MATLAB original que você traduziu para python para isso? Obrigado!
Spacey



1
@ endolith Eu sei que esta pergunta é bastante antiga, mas é bastante útil;) Passei algumas horas esta manhã find_peaks, então adicionei esta resposta que pode ser útil para referência futura. (Eu tenho certeza que você já encontrou este desde 2009, mas é para outras pessoas + mim quando eu vou me fazer a pergunta novamente em alguns anos!)
Basj

Respostas:


74

A função scipy.signal.find_peaks, como o próprio nome sugere, é útil para isso. Mas é importante entender bem seus parâmetros widthe threshold, distance acima de tudo,prominence obter uma boa extração de pico.

De acordo com meus testes e a documentação, o conceito de destaque é "o conceito útil" para manter os bons picos e descartar os barulhentos.

O que é destaque (topográfico) ? É "a altura mínima necessária para descer do topo até qualquer terreno mais alto" , como pode ser visto aqui:

insira a descrição da imagem aqui

A ideia é:

Quanto maior a proeminência, mais "importante" é o pico.

Teste:

insira a descrição da imagem aqui

Eu usei um sinusóide (barulhento) com variação de frequência de propósito, porque mostra muitas dificuldades. Podemos ver que o widthparâmetro não é muito útil aqui, porque se você definir um mínimo widthmuito alto, ele não poderá rastrear picos muito próximos na parte de alta frequência. Se você definir widthmuito baixo, terá muitos picos indesejados na parte esquerda do sinal. Mesmo problema com distance. thresholdsó se compara aos vizinhos diretos, o que não é útil aqui. prominenceé o que dá a melhor solução. Observe que você pode combinar muitos desses 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()

É isso que eu estou procurando. Mas você conhece alguma implementação que encontra destaque na matriz 2D?
Jason

43

Estou procurando um problema semelhante e descobri que algumas das melhores referências vêm da química (de picos encontrados em dados de especificações de massa). Para uma boa revisão completa dos picos de algoritmos de localização, leia isto . Essa é uma das melhores análises mais claras das técnicas de detecção de pico que eu já deparei. (As wavelets são as melhores para encontrar picos desse tipo em dados ruidosos.).

Parece que seus picos estão claramente definidos e não estão escondidos no barulho. Sendo esse o caso, recomendo o uso de derivativos savtizky-golay suaves para encontrar os picos (se você apenas diferenciar os dados acima, terá uma bagunça de falsos positivos.). Essa é uma técnica muito eficaz e é muito fácil de implementar (você precisa de uma classe de matriz com operações básicas). Se você simplesmente encontrar o cruzamento zero do primeiro derivado SG, acho que ficará feliz.


2
Eu estava procurando uma solução de uso geral, não uma que funcionasse apenas nessas imagens em particular. Eu adaptei um script MATLAB para Python e ele funciona decentemente.
Endolith 17/12/2009

1
Pode apostar. O Matlab é uma boa fonte de algoritmos. Qual técnica o script usa? (BTW, SG é uma técnica de uso muito geral).
Paul

2
Eu o vinculei acima. Basicamente, apenas procura por máximos locais maiores que um determinado limite acima de seus vizinhos. Certamente existem métodos melhores.
Endolith 18/12/2009

1
@ Paul eu marquei essa página como favorita. IYO e, em resumo, qual técnica específica você achou que funcionou melhor para esse negócio de pico de picking?
Spacey

por que os zeros da derivada são melhores do que apenas testar se um dos três pontos médios é maior ou menor que os outros dois? Eu já apliquei sg transfor, parece um custo extra.
Kirill_igum

20

Existe uma função no scipy chamada scipy.signal.find_peaks_cwtque parece adequada às suas necessidades, no entanto, não tenho experiência com ela, portanto não posso recomendar.

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


12
Sim, que não existia quando eu perguntei isso, e eu ainda não tenho certeza de como usá-lo
endolith

1
Você adicionou isso há um tempo atrás, mas funcionou demais. Usá-lo é simples como torta. Basta passar a matriz e outra matriz (por exemplo, np.arange (1,10)) que lista todas as larguras de picos que você deseja; bom benefício para filtrar picos finos ou largos, se necessário. Obrigado novamente!
Miles

15

Para quem não tem certeza sobre quais algoritmos de localização de pico usar no Python, aqui está uma rápida visão geral das alternativas: https://github.com/MonsieurV/py-findpeaks

Querendo um equivalente à findpeaksfunção MatLab , descobri que a função detect_peaks de Marcos Duarte é uma boa solução .

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

O que lhe dará:

resultados detect_peaks


1
Desde que esta postagem foi escrita, a find_peaksfunção foi adicionada a scipy.
Onewhaleid

6

A detecção de picos em um espectro de maneira confiável tem sido bastante estudada, por exemplo, todo o trabalho em modelagem sinusoidal para sinais de música / áudio nos anos 80. Procure por "Modelagem Sinusoidal" na literatura.

Se seus sinais são tão limpos quanto o exemplo, um simples "me dê algo com uma amplitude maior que N vizinhos" deve funcionar razoavelmente bem. Se você possui sinais ruidosos, uma maneira simples, porém eficaz, é observar seus picos no tempo e segui-los: você detecta linhas espectrais em vez de picos espectrais. Como você calcula a FFT em uma janela deslizante do seu sinal, para obter um conjunto de espectro no tempo (também chamado espectrograma). Você observa a evolução do pico espectral no tempo (ou seja, em janelas consecutivas).


Olhe para os picos a tempo? Detectar linhas espectrais? Não tenho certeza do que isso significa. Funcionaria para ondas quadradas?
Endolith 27/11/2009

Ah, você está falando sobre o uso de STFT em vez de FFT. Esta pergunta não é sobre FFTs especificamente; isso é apenas um exemplo. Trata-se de encontrar os picos em qualquer matriz 1D ou 2D geral.
Endolith 30/11/2009

4

Não acho que o que você procura seja fornecido pelo SciPy. Eu mesmo escreveria o código nessa situação.

A interpolação e suavização da spline do scipy.interpolate são bastante agradáveis ​​e podem ser bastante úteis para ajustar picos e, em seguida, encontrar a localização do seu máximo.


16
Peço desculpas, mas acho que isso deve ser um comentário, não uma resposta. Apenas sugere escrevê-lo por si próprio, com uma vaga sugestão de funções que podem ser úteis (as da resposta de Paulo são muito mais relevantes, aliás).
Ami Tavory

1

Existem funções e métodos estatísticos padrão para localizar dados discrepantes, o que provavelmente é o que você precisa no primeiro caso. Usar derivativos resolveria o seu segundo. Não tenho certeza de um método que resolva funções contínuas e dados amostrados, no entanto.


0

Para começar, a definição de "pico" é vaga, se não houver especificações adicionais. Por exemplo, para a série a seguir, você chamaria 5-4-5 de um ou dois picos?

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

Nesse caso, você precisará de pelo menos dois limites: 1) um limite alto apenas acima do qual um valor extremo possa ser registrado como um pico; e 2) um limite baixo para que valores extremos separados por pequenos valores abaixo dele se tornem dois picos.

A detecção de pico é um tópico bem estudado na literatura da teoria dos valores extremos, também conhecido como "decomposição de valores extremos". Suas aplicações típicas incluem a identificação de eventos de risco com base em leituras contínuas de variáveis ​​ambientais, por exemplo, análise da velocidade do vento para detectar eventos de tempestade.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.