webentwicklung-frage-antwort-db.com.de

Finden lokaler Maxima / Minima mit Numpy in einem 1D-Numpy-Array

Können Sie eine Modulfunktion von numpy/scipy vorschlagen, die lokale Maxima/Minima in einem 1D numpy-Array findet? Natürlich ist es der einfachste Ansatz, einen Blick auf die nächsten Nachbarn zu werfen, aber ich hätte gerne eine akzeptierte Lösung, die Teil der Numpy Distribution ist.

102
Navi

Wenn Sie alle Einträge im 1d-Array a suchen, die kleiner sind als die Nachbarn, können Sie es versuchen

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

Sie können Ihr Array auch vor diesem Schritt mit numpy.convolve()glätten .

Ich glaube nicht, dass es dafür eine spezielle Funktion gibt.

55
Sven Marnach

In SciPy> = 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)

Produziert

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

Beachten Sie, dass dies die Indizes von x sind, die lokale Max/Min sind. Um die Werte zu erhalten, versuchen Sie:

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

scipy.signal bietet auch argrelmax und argrelmin zum Auffinden von Maxima bzw. Minima.

194
danodonovan

Für Kurven mit nicht zu viel Rauschen empfehle ich den folgenden kleinen Codeausschnitt:

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

Das +1 ist wichtig, da diff die ursprüngliche Indexnummer reduziert.

34
R. C.

Ein anderer Ansatz (mehr Wörter, weniger Code), der helfen kann:

Die Orte der lokalen Maxima und Minima sind auch die Orte der Nulldurchgänge der ersten Ableitung. Im Allgemeinen ist es viel einfacher, Nulldurchgänge zu finden, als lokale Maxima und Minima direkt zu finden.

Leider tendiert die erste Ableitung dazu, das Rauschen zu "verstärken". Wenn also in den Originaldaten signifikantes Rauschen vorhanden ist, wird die erste Ableitung am besten erst verwendet, nachdem auf die Originaldaten ein gewisses Maß an Glättung angewendet wurde.

Da das Glätten im einfachsten Sinne ein Tiefpassfilter ist, wird das Glätten häufig am besten (gut, am einfachsten) unter Verwendung eines Faltungskerns durchgeführt, und das "Formen" dieses Kerns kann eine überraschende Menge an Merkmalserhaltungs-/Verbesserungsmöglichkeiten bieten . Die Suche nach einem optimalen Kernel kann auf verschiedene Weise automatisiert werden. Am besten ist jedoch eine einfache Brute-Force-Methode (ausreichend schnell, um kleine Kernel zu finden). Ein guter Kernel wird (wie beabsichtigt) die ursprünglichen Daten massiv verzerren, jedoch NICHT die Position der interessierenden Gipfel/Täler beeinflussen.

Glücklicherweise kann häufig ein geeigneter Kernel über einen einfachen Swag erstellt werden ("fundierte Vermutung"). Die Breite des Glättungskerns sollte etwas breiter sein als der breiteste erwartete "interessante" Peak in den Originaldaten, und seine Form ähnelt diesem Peak (einem einfach skalierten Wavelet). Für Kernel, deren Mittelwert erhalten bleibt (was ein guter Glättungsfilter sein sollte), sollte die Summe der Kernelelemente genau 1,00 betragen, und der Kernel sollte symmetrisch zu seiner Mitte sein (was bedeutet, dass er eine ungerade Anzahl von Elementen hat).

Bei einem optimalen Glättungskernel (oder einer kleinen Anzahl von Kerneln, die für unterschiedliche Dateninhalte optimiert sind) wird der Glättungsgrad zu einem Skalierungsfaktor für (den "Gewinn") des Faltungskernels.

Das Ermitteln des "richtigen" (optimalen) Glättungsgrades (Faltungskernverstärkung) kann sogar automatisiert werden: Vergleichen Sie die Standardabweichung der Daten der ersten Ableitung mit der Standardabweichung der geglätteten Daten. Wie sich das Verhältnis der beiden Standardabweichungen mit Änderungen des Glättungsgrades ändert, kann verwendet werden, um effektive Glättungswerte vorherzusagen. Ein paar manuelle Datenläufe (die wirklich repräsentativ sind) sollten alles sein, was benötigt wird.

Alle oben genannten früheren Lösungen berechnen die erste Ableitung, sie behandeln sie jedoch nicht als statistische Kennzahl, und die obigen Lösungen versuchen auch nicht, die Glättung von Merkmalen beizubehalten/zu verbessern (um subtilen Spitzen zu helfen, "über das Rauschen zu springen").

Abschließend die schlechte Nachricht: Das Auffinden "echter" Spitzen wird zu einem echten Schmerz, wenn das Rauschen auch Merkmale aufweist, die wie echte Spitzen aussehen (überlappende Bandbreite). Die nächst komplexere Lösung besteht im Allgemeinen darin, einen längeren Faltungskern (eine "größere Kernöffnung") zu verwenden, die die Beziehung zwischen benachbarten "realen" Peaks (wie minimale oder maximale Raten für das Auftreten von Peaks) berücksichtigt, oder mehrere zu verwenden Faltungsdurchläufe werden mit Kerneln unterschiedlicher Breite durchgeführt (aber nur, wenn es schneller ist: Es ist eine grundlegende mathematische Wahrheit, dass nacheinander ausgeführte lineare Faltungen immer zu einer einzigen Faltung zusammengefasst werden können). Oft ist es jedoch viel einfacher, zuerst eine Sequenz nützlicher Kernel (unterschiedlicher Breite) zu finden und diese zusammenzufalten, als den endgültigen Kernel direkt in einem einzigen Schritt zu finden.

Hoffentlich liefert dies genug Informationen, damit Google (und möglicherweise ein guter Statistik-Text) die Lücken füllen kann. Ich wünschte wirklich, ich hätte die Zeit, ein funktionierendes Beispiel oder einen Link zu einem zu liefern. Wenn jemand online auf eines stößt, poste es bitte hier!

21
BobC

Warum nicht die in Scipy integrierte Funktion signal.find_peaks_cwt verwenden, um die Arbeit zu erledigen?

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]

ergebnisse:

maxima [ 0.9995736]
minima [ 0.09146464]

Grüße

9
A STEFANI

Ab SciPy Version 1.1 können Sie auch find_peaks verwenden. Nachfolgend finden Sie zwei Beispiele aus der Dokumentation.

Mit dem Argument height kann man alle Maxima über einem bestimmten Schwellenwert auswählen (in diesem Beispiel alle nicht negativen Maxima; dies kann sehr nützlich sein, wenn man sich mit einer verrauschten Grundlinie befassen muss; wenn Sie suchen möchten Minima, multiplizieren Sie einfach Ihre Eingabe mit -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()

enter image description here

Ein weiteres äußerst hilfreiches Argument ist distance, das den Mindestabstand zwischen zwei Peaks definiert:

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

enter image description here

8
Cleb

pdate: Ich war mit dem Farbverlauf nicht zufrieden und fand es zuverlässiger, numpy.diff Zu verwenden. Bitte lassen Sie mich wissen, ob es das tut, was Sie wollen.

In Bezug auf das Problem des Rauschens besteht das mathematische Problem darin, Maxima/Minima zu lokalisieren, wenn wir das Rauschen betrachten wollen, können wir so etwas wie die oben erwähnte Faltung verwenden.

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()
5
Mike Vella

Während diese Frage wirklich alt ist. Ich glaube, es gibt einen viel einfacheren Ansatz bei Numpy (Einzeiler).

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

Um ein lokales Maximum oder Minimum zu finden, möchten wir im Wesentlichen herausfinden, wann sich die Differenz zwischen den Werten in der Liste (3-1, 9-3 ...) von positiv zu negativ (Maximum) oder negativ zu positiv (Minute) ändert. Deshalb finden wir zuerst den Unterschied. Dann finden wir das Vorzeichen und dann finden wir die Vorzeichenänderungen, indem wir die Differenz erneut nehmen. (Eine Art erste und zweite Ableitung im Kalkül, nur haben wir diskrete Daten und keine kontinuierliche Funktion.)

Die Ausgabe in meinem Beispiel enthält keine Extrema (den ersten und letzten Wert in der Liste). Ebenso wie im Kalkül haben Sie, wenn die zweite Ableitung negativ ist, ein Maximum und, wenn sie positiv ist, ein Minimum.

Wir haben also folgendes Matchup:

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

Keine dieser Lösungen hat bei mir funktioniert, da ich auch Peaks im Zentrum sich wiederholender Werte finden wollte. zum Beispiel in

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

die Antwort sollte sein

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

Ich habe das mit einer Schleife gemacht. Ich weiß, es ist nicht super sauber, aber es erledigt die Arbeit.

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 
3
Misha Smirnov
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

minm und maxm enthalten Indizes von Minima bzw. Maxima. Für einen riesigen Datensatz gibt es viele Maximas/Minimas. Glätten Sie in diesem Fall zuerst die Kurve und wenden Sie dann diesen Algorithmus an.

1
prtkp