1B uyuşuk bir dizide Numpy ile yerel maksimum / minimum bulma


116

Numpy / scipy'den 1B numpy dizisinde yerel maksimum / minimumları bulabilen bir modül işlevi önerebilir misiniz? Açıkçası, şimdiye kadarki en basit yaklaşım, en yakın komşulara bir göz atmaktır, ancak hissiz dağıtımın bir parçası olan kabul edilmiş bir çözüme sahip olmak istiyorum.



1
Hayır, bu 2D'de (1D'den bahsediyorum) ve özel fonksiyonlar içeriyor. Kendi basit uygulamam var, ancak Numpy / Scipy modülleri ile gelen daha iyisi olup olmadığını merak ediyordum.
Navi

Belki soruyu (1) 1d diziniz olduğunu ve (2) ne tür bir yerel minimum aradığınızı içerecek şekilde güncelleyebilirsiniz. Sadece bitişik iki girişten daha küçük bir giriş mi?
Sven Marnach

1
Parazitli verilerden bahsediyorsanız scipy.signal.find_peaks_cwt'ye bir göz atabilirsiniz
Lakshay Garg

Yanıtlar:


66

1d dizisindeki tüm girişleri akomşularından daha küçük arıyorsanız, deneyebilirsiniz

numpy.r_[True, a[1:] < a[:-1]] & numpy.r_[a[:-1] < a[1:], True]

Bu adımdan önce dizinizi de düzeltebilirsiniznumpy.convolve() .

Bunun için özel bir işlev olduğunu sanmıyorum.


Hmm, neden düzeltmeye ihtiyacım var? Gürültüyü kaldırmak için? Bu ilginç geliyor. Bana öyle geliyor ki örnek kodunuzda 1 yerine başka bir tam sayı kullanabilirim. Ayrıca gradyanları hesaplamayı düşünüyordum. Her neyse, hiçbir işlevi yoksa çok kötü.
Navi

1
@Navi: Sorun şu ki, "yerel minimum" kavramı kullanım durumundan kullanım durumuna büyük ölçüde değişiyor, bu nedenle bu amaç için "standart" bir işlev sağlamak zor. Düzeltme, en yakın komşudan daha fazlasını hesaba katmaya yardımcı olur. 1, diyelim ki 3 yerine farklı bir tamsayı kullanmak, sadece üçüncü-sonraki öğeyi her iki yönde de dikkate alacağı, ancak doğrudan komşuları dikkate almayacağı için garip olacaktır.
Sven Marnach

1
@Sven Marnach: Bağladığınız tarif sinyali geciktirir. Bir var ikinci tarifi kullanan filtfilt scipy.signal den
bobrobbob

2
Sadece yerine onun uğruna, için <ile >yerine sana minimanın yerel maksimum verecektir
DarkCygnus

1
@SvenMarnach Burada yayınlanan problemimi çözmek için yukarıdaki çözümünüzü kullandım stackoverflow.com/questions/57403659/… ama çıktı aldım [False False]Buradaki sorun ne olabilir?
Msquare

221

SciPy'de> = 0.11

import numpy as np
from scipy.signal import argrelextrema

x = np.random.random(12)

# for local maxima
argrelextrema(x, np.greater)

# for local minima
argrelextrema(x, np.less)

üretir

>>> x
array([ 0.56660112,  0.76309473,  0.69597908,  0.38260156,  0.24346445,
    0.56021785,  0.24109326,  0.41884061,  0.35461957,  0.54398472,
    0.59572658,  0.92377974])
>>> argrelextrema(x, np.greater)
(array([1, 5, 7]),)
>>> argrelextrema(x, np.less)
(array([4, 6, 8]),)

Unutmayın, bunlar yerel maks / min olan x indisleridir. Değerleri almak için şunu deneyin:

>>> x[argrelextrema(x, np.greater)[0]]

scipy.signalAyrıca sağlar argrelmaxve argrelminsırasıyla maksimum ve minimum bulmak için.


1
12'nin önemi nedir?
hatmi

7
@marshmallow: np.random.random(12)12 rastgele değer üretir, bunlar işlevi göstermek için kullanılır argrelextrema.
sebix

2
girdi ise test02=np.array([10,4,4,4,5,6,7,6]), o zaman çalışmaz. Ardışık değerleri yerel minimumlar olarak tanımıyor.
Leos313

1
teşekkür ederim @Cleb. Başka sorunlara işaret etmek istiyorum: dizinin uç noktaları ne olacak? Dizinin son öğesi de yerel bir minimum olduğundan, ilk öğe de yerel bir maksimumdur. Ayrıca, ardışık kaç değerin kurulduğunu da döndürmez. Ancak ben burada bu sorunun kodunda bir çözüm önerdim . Teşekkür ederim!!
Leos313

1
Teşekkürler, bu şimdiye kadar bulduğum en iyi çözümlerden biri
Noufal E

37

Çok fazla gürültü içermeyen eğriler için aşağıdaki küçük kod parçacığını öneririm:

from numpy import *

# example data with some peaks:
x = linspace(0,4,1e3)
data = .2*sin(10*x)+ exp(-abs(2-x)**2)

# that's the line, you need:
a = diff(sign(diff(data))).nonzero()[0] + 1 # local min+max
b = (diff(sign(diff(data))) > 0).nonzero()[0] + 1 # local min
c = (diff(sign(diff(data))) < 0).nonzero()[0] + 1 # local max


# graphical output...
from pylab import *
plot(x,data)
plot(x[b], data[b], "o", label="min")
plot(x[c], data[c], "o", label="max")
legend()
show()

Bu +1önemlidir, çünkü difforijinal dizin numarasını azaltır.


1
yuvalanmış numpy işlevlerinin güzel kullanımı! ancak bu dizinin her iki ucundaki
maxima'yı

2
Tekrarlayan değerler varsa bu da tuhaf davranacaktır. Örneğin, diziyi alırsanız [1, 2, 2, 3, 3, 3, 2, 2, 1], yerel maksimumlar ortada 3'ler arasında bir yerdedir. Ancak sağladığınız fonksiyonları çalıştırırsanız 2,6 indekslerinde maksimas ve 1,3,5,7 endekslerinde minimalar elde edersiniz ki bu bana pek mantıklı gelmiyor.
Korem

5
Kullanmak +1yerine bundan kaçınmak np.diff()için np.gradient().
ankostis

Bu iş parçacığının eski olduğunu biliyorum, ancak eğriniz çok gürültülü ise, yumuşatma için her zaman önce düşük geçişli filtrelemeyi deneyebilirsiniz. En azından benim için yerel maks / min kullanımlarımın çoğu, bazı yerel alanlardaki global maks / min içindir (e, g, büyük zirveler ve vadiler, verilerdeki her varyasyon değil)
marcman

25

Yardımcı olabilecek başka bir yaklaşım (daha fazla kelime, daha az kod):

Yerel maksimum ve minimumların konumları aynı zamanda birinci türevin sıfır geçişlerinin yerleridir. Sıfır geçişleri bulmak, doğrudan yerel maksimum ve minimumları bulmaktan çok daha kolaydır.

Ne yazık ki, birinci türev gürültüyü "büyütme" eğilimindedir, bu nedenle orijinal verilerde önemli miktarda gürültü mevcut olduğunda, birinci türev en iyi, orijinal veriye bir dereceye kadar yumuşatma uygulandıktan sonra kullanılır.

Düzgünleştirme, en basit anlamıyla, bir düşük geçiş filtresi olduğundan, düzleştirme genellikle en iyi (iyi, en kolay) bir evrişim çekirdeği kullanılarak yapılır ve bu çekirdek şaşırtıcı miktarda özellik koruma / geliştirme yeteneği sağlayabilen "şekillendirme" dir . En uygun çekirdeği bulma süreci çeşitli yollarla otomatikleştirilebilir, ancak en iyisi basit kaba kuvvet olabilir (küçük çekirdekleri bulmak için çok hızlı). İyi bir çekirdek (amaçlandığı gibi) orijinal veriyi büyük ölçüde bozacaktır, ancak ilgili tepe noktalarının / vadilerin konumunu ETKİLEMEZ.

Neyse ki, çoğu zaman uygun bir çekirdek basit bir SWAG ("eğitimli tahmin") aracılığıyla oluşturulabilir. Düzgünleştirici çekirdeğin genişliği, orijinal verilerde beklenen en geniş "ilginç" tepeden biraz daha geniş olmalıdır ve şekli bu tepe noktasına (tek ölçekli bir dalgacık) benzeyecektir. Ortalama koruyan çekirdekler için (herhangi bir iyi yumuşatma filtresi ne olmalıdır) çekirdek elemanlarının toplamı tam olarak 1.00'a eşit olmalı ve çekirdek merkezi etrafında simetrik olmalıdır (yani tek sayıda elemana sahip olacaktır.

Optimal bir yumuşatma çekirdeği (veya farklı veri içeriği için optimize edilmiş az sayıda çekirdek) verildiğinde, yumuşatma derecesi, evrişim çekirdeği ("kazanımı") için bir ölçekleme faktörü haline gelir.

"Doğru" (optimal) yumuşatma derecesinin (evrişim çekirdek kazancı) belirlenmesi bile otomatik hale getirilebilir: İlk türev verilerinin standart sapmasını düzleştirilmiş verilerin standart sapmasıyla karşılaştırın. Etkili yumuşatma değerlerini tahmin etmek için iki standart sapmanın oranının düzgünleştirme kamının derecesindeki değişikliklerle nasıl değiştiği kullanılır. Gerekli olan tek şey birkaç manuel veri çalışması (gerçekten temsili) olmalıdır.

Yukarıda yayınlanan tüm önceki çözümler ilk türevi hesaplar, ancak bunu istatistiksel bir ölçü olarak ele almazlar ve yukarıdaki çözümler düzgünleştirmeyi koruyan / geliştiren özelliği gerçekleştirmeye çalışmaz (gürültünün "üstündeki küçük zirvelere" yardımcı olmak için).

Son olarak, kötü haber: "Gerçek" zirveleri bulmak, gürültünün gerçek zirvelere benzeyen özelliklere (örtüşen bant genişliği) sahip olması durumunda büyük bir acıya dönüşür. Bir sonraki daha karmaşık çözüm, genellikle bitişik "gerçek" zirveler arasındaki ilişkiyi (zirve oluşumu için minimum veya maksimum oranlar gibi) hesaba katan daha uzun bir evrişimli çekirdek ("daha geniş bir çekirdek açıklığı") kullanmak veya birden çok kullanmaktır. evrişim, farklı genişliklere sahip çekirdekler kullanılarak geçer (ancak yalnızca daha hızlıysa: sırayla gerçekleştirilen doğrusal evrişimlerin her zaman tek bir evrişime dönüştürülebileceği temel bir matematiksel gerçektir). Ancak, ilk önce bir dizi yararlı çekirdek (çeşitli genişliklerde) bulmak ve bunları bir araya getirmek, nihai çekirdeği tek bir adımda doğrudan bulmaktan çok daha kolaydır.

Umarım bu, Google'ın (ve belki de iyi bir istatistik metninin) boşlukları doldurmasına izin verecek kadar bilgi sağlar. Keşke çalışılmış bir örnek veya bir bağlantı sunacak zamanım olsaydı. Çevrimiçi olarak biri gelirse, lütfen buraya gönderin!


25

SciPy sürüm 1.1'den itibaren find_peaks'i de kullanabilirsiniz . Aşağıda belgelerin kendisinden alınan iki örnek verilmiştir.

Kullanılması heightbiri gürültülü başlangıca başa varsa bu çok yararlı olabilir;; argümanı, bir, tüm negatif olmayan maxima bu örnekte belli bir sınırın üzerinde tüm maxima (seçebilir Eğer minimum noktalarının istiyorsanız, sadece çarpın girmenizi tarafından -1):

import matplotlib.pyplot as plt
from scipy.misc import electrocardiogram
from scipy.signal import find_peaks
import numpy as np

x = electrocardiogram()[2000:4000]
peaks, _ = find_peaks(x, height=0)
plt.plot(x)
plt.plot(peaks, x[peaks], "x")
plt.plot(np.zeros_like(x), "--", color="gray")
plt.show()

görüntü açıklamasını buraya girin

Bir diğer son derece yararlı argüman, distanceiki tepe arasındaki minimum mesafeyi tanımlayan:

peaks, _ = find_peaks(x, distance=150)
# difference between peaks is >= 150
print(np.diff(peaks))
# prints [186 180 177 171 177 169 167 164 158 162 172]

plt.plot(x)
plt.plot(peaks, x[peaks], "x")
plt.show()

görüntü açıklamasını buraya girin


10

İşi yapmak için neden Scipy yerleşik işlevi signal.find_peaks_cwt'yi kullanmıyorsunuz ?

from scipy import signal
import numpy as np

#generate junk data (numpy 1D arr)
xs = np.arange(0, np.pi, 0.05)
data = np.sin(xs)

# maxima : use builtin function to find (max) peaks
max_peakind = signal.find_peaks_cwt(data, np.arange(1,10))

# inverse  (in order to find minima)
inv_data = 1/data
# minima : use builtin function fo find (min) peaks (use inversed data)
min_peakind = signal.find_peaks_cwt(inv_data, np.arange(1,10))

#show results
print "maxima",  data[max_peakind]
print "minima",  data[min_peakind]

Sonuçlar:

maxima [ 0.9995736]
minima [ 0.09146464]

Saygılarımızla


7
Bölme yapmak yerine (olası hassasiyet kaybıyla), neden maksimumdan minimuma gitmek için sadece -1 ile çarpmayalım?
Livius

"1 / data" yı "data * -1" olarak değiştirmeye çalıştım ama sonra bir hata oluştu, yönteminizi nasıl uygulayacağınızı paylaşabilir misiniz?
A STEFANI

Belki de son kullanıcıların ek olarak scipy yüklemesini istemediğimiz içindir.
Damian Yerrick

5

Güncelleme: Gradyandan memnun değildim, bu yüzden kullanımı daha güvenilir buldum numpy.diff. Lütfen istediğini yaparsa bana haber ver.

Gürültü konusuyla ilgili olarak, matematiksel problem, gürültüye bakmak istiyorsak, daha önce bahsedilen konvolve gibi bir şey kullanabiliriz.

import numpy as np
from matplotlib import pyplot

a=np.array([10.3,2,0.9,4,5,6,7,34,2,5,25,3,-26,-20,-29],dtype=np.float)

gradients=np.diff(a)
print gradients


maxima_num=0
minima_num=0
max_locations=[]
min_locations=[]
count=0
for i in gradients[:-1]:
        count+=1

    if ((cmp(i,0)>0) & (cmp(gradients[count],0)<0) & (i != gradients[count])):
        maxima_num+=1
        max_locations.append(count)     

    if ((cmp(i,0)<0) & (cmp(gradients[count],0)>0) & (i != gradients[count])):
        minima_num+=1
        min_locations.append(count)


turning_points = {'maxima_number':maxima_num,'minima_number':minima_num,'maxima_locations':max_locations,'minima_locations':min_locations}  

print turning_points

pyplot.plot(a)
pyplot.show()

Bu gradyanın nasıl hesaplandığını biliyor musunuz? Gürültülü verileriniz varsa, eğim muhtemelen çok değişir, ancak bu, bir maks / dakika olduğu anlamına gelmek zorunda değildir.
Navi

Evet biliyorum, ancak gürültülü veriler farklı bir konudur. Sanırım bunun için convolve kullanıyorum.
Mike Vella

Üzerinde çalıştığım ve yukarıda bahsedilen numpy.diff yöntemini kullandığım bir proje için benzer bir şeye ihtiyacım vardı, verilerim için yukarıdaki kodun her ikisinde de orta terimi değiştirerek birkaç maksimum ve minimum değeri kaçırdığını belirtmenin yararlı olabileceğini düşündüm. Sırasıyla <= ve> = ifadeleri varsa, tüm noktaları yakalayabildim.

5

Bu soru gerçekten eski olsa da. Ben hissizlikte çok daha basit bir yaklaşım olduğuna inanıyorum (tek astar).

import numpy as np

list = [1,3,9,5,2,5,6,9,7]

np.diff(np.sign(np.diff(list))) #the one liner

#output
array([ 0, -2,  0,  2,  0,  0, -2])

Yerel bir maksimum veya minimum bulmak için, esasen listedeki değerler (3-1, 9-3 ...) arasındaki farkın pozitiften negatife (maks.) Veya negatifden pozitife (min) ne zaman değiştiğini bulmak istiyoruz. Bu nedenle önce farkı buluyoruz. Sonra işareti buluyoruz ve sonra farkı tekrar alarak işaretteki değişiklikleri buluyoruz. (Analizdeki birinci ve ikinci türev gibi, sadece ayrık veriye sahibiz ve sürekli bir fonksiyona sahip değiliz.)

Örneğimdeki çıktı extrema (listedeki ilk ve son değerler) içermiyor. Ayrıca, matematikte olduğu gibi, eğer ikinci türev negatifse, maks, pozitifse, min.

Böylece şu eşleştirmeye sahibiz:

[1,  3,  9,  5,  2,  5,  6,  9,  7]
    [0, -2,  0,  2,  0,  0, -2]
        Max     Min         Max

1
Bu (iyi!) Cevabın RC'nin 2012 cevabı ile aynı olduğunu düşünüyorum. Çözümünü doğru okuyorsam, arayanın dakika, maksimum veya her ikisini birden isteyip istemediğine bağlı olarak üç tek satırlık çözüm sunar.
Brandon Rodos

3

Yinelenen değerlerin merkezinde zirveler bulmak istediğim için bu çözümlerin hiçbiri benim için işe yaramadı. örneğin, içinde

ar = np.array([0,1,2,2,2,1,3,3,3,2,5,0])

cevap olmalı

array([ 3,  7, 10], dtype=int64)

Bunu bir döngü kullanarak yaptım. Süper temiz olmadığını biliyorum ama işi hallediyor.

def findLocalMaxima(ar):
# find local maxima of array, including centers of repeating elements    
maxInd = np.zeros_like(ar)
peakVar = -np.inf
i = -1
while i < len(ar)-1:
#for i in range(len(ar)):
    i += 1
    if peakVar < ar[i]:
        peakVar = ar[i]
        for j in range(i,len(ar)):
            if peakVar < ar[j]:
                break
            elif peakVar == ar[j]:
                continue
            elif peakVar > ar[j]:
                peakInd = i + np.floor(abs(i-j)/2)
                maxInd[peakInd.astype(int)] = 1
                i = j
                break
    peakVar = ar[i]
maxInd = np.where(maxInd)[0]
return maxInd 

1
import numpy as np
x=np.array([6,3,5,2,1,4,9,7,8])
y=np.array([2,1,3,5,3,9,8,10,7])
sortId=np.argsort(x)
x=x[sortId]
y=y[sortId]
minm = np.array([])
maxm = np.array([])
i = 0
while i < length-1:
    if i < length - 1:
        while i < length-1 and y[i+1] >= y[i]:
            i+=1

        if i != 0 and i < length-1:
            maxm = np.append(maxm,i)

        i+=1

    if i < length - 1:
        while i < length-1 and y[i+1] <= y[i]:
            i+=1

        if i < length-1:
            minm = np.append(minm,i)
        i+=1


print minm
print maxm

minmve maxmsırasıyla minimum ve maksimum indislerini içerir. Büyük bir veri kümesi için çok sayıda maksim / minimas verecektir, bu durumda önce eğriyi düzeltin ve sonra bu algoritmayı uygulayın.


bu ilginç görünüyor. Kütüphane yok. O nasıl çalışır?
john ktejik

1
Eğriyi başlangıç ​​noktasından döndürün ve sürekli yukarı veya aşağı doğru gidip gitmediğinizi görün, yukarıdan aşağıya geçtiğinizde maksimuma sahip olduğunuz anlamına gelir, yukarı doğru gidiyorsanız bir minimuma sahipsiniz.
prtkp

1

Esasen bir genişletme operatörü kullanan başka bir çözüm:

import numpy as np
from scipy.ndimage import rank_filter

def find_local_maxima(x):
   x_dilate = rank_filter(x, -1, size=3)
   return x_dilate == x

ve minimumlar için:

def find_local_minima(x):
   x_erode = rank_filter(x, -0, size=3)
   return x_erode == x

Ayrıca, gelen scipy.ndimagesenin yerini alabilir rank_filter(x, -1, size=3)ile grey_dilationve rank_filter(x, 0, size=3)ile grey_erosion. Bu, yerel bir sıralama gerektirmeyeceği için biraz daha hızlıdır.


bu problem için düzgün çalışıyor. İşte çözüm mükemmel (+1)
Leos313

0

Bir diğeri:


def local_maxima_mask(vec):
    """
    Get a mask of all points in vec which are local maxima
    :param vec: A real-valued vector
    :return: A boolean mask of the same size where True elements correspond to maxima. 
    """
    mask = np.zeros(vec.shape, dtype=np.bool)
    greater_than_the_last = np.diff(vec)>0  # N-1
    mask[1:] = greater_than_the_last
    mask[:-1] &= ~greater_than_the_last
    return mask
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.