programing

1D Numpy 배열에서 Numpy를 사용하여 로컬 최대값/minima 찾기

powerit 2023. 7. 17. 21:30
반응형

1D Numpy 배열에서 Numpy를 사용하여 로컬 최대값/minima 찾기

1D numpy 배열에서 로컬 maxima/minima를 찾을 수 있는 numpy/scipy의 모듈 기능을 제안할 수 있습니까?분명히 가장 간단한 접근법은 가장 가까운 이웃을 보는 것이지만, 저는 numpy distro의 일부인 수용된 해결책을 갖고 싶습니다.

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)

프로듀스

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

참고로 x의 인덱스는 로컬 max/min입니다.값을 가져오려면 다음을 수행합니다.

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

scipy.signal에서는 또한제니다도 제공합니다.argrelmax그리고.argrelmin각각 최대값과 최소값을 찾기 위해.

의 모든 항목을 배서모에항든목을찾는경우열▁in경우▁if찾는a그들의 이웃들보다 더 작은, 당신은 시도할 수 있습니다.

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

또한 이 단계 전에 다음을 사용하여 배열을 평활할 수 있습니다.numpy.convolve().

저는 이것을 위한 전용 기능이 없다고 생각합니다.

SciPy 버전 1.1부터는 find_peaks도 사용할 수 있습니다.다음은 설명서 자체에서 가져온 두 가지 예입니다.

height할 수 예에서는 최대값을 선택할 수 ; 기준선을 해야 할 할 수 ; 에 수, 특정인이이임선있수다니모습상을의값든택할계최아값최값대닌대음이모든는서예에이▁you▁multiply▁by▁argument,▁input▁just(다값▁if▁one대▁all최▁(인니a있in)를 곱하면 됩니다. 노이즈가 많은 기준선을 처리해야 할 경우 매우 유용할 수 있습니다. 최소값을 찾으려면 입력 값을 곱하십시오.-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

주장은 또다매도되주는은장이움입니다.distance의 최소 를 정의합니다. 즉, 피 크 사 정 합 니 의 다 를 거 리 두 최 소 의

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

노이즈가 많지 않은 곡선의 경우 다음과 같은 작은 코드 조각을 권장합니다.

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

+1중합니다면왜, 하냐요면.diff원래 인덱스 번호를 줄입니다.

다음을 지원할 수 있는 또 다른 접근 방식(단어 수 증가, 코드 수 감소):

국소 최대치와 최소치의 위치는 첫 번째 도함수의 영 교차 위치이기도 합니다.일반적으로 제로 교차를 찾는 것이 로컬 최대값과 최소값을 직접 찾는 것보다 훨씬 쉽습니다.

불행히도 첫 번째 파생 모델은 노이즈를 "증폭"하는 경향이 있으므로 원래 데이터에 상당한 노이즈가 있는 경우 원래 데이터가 어느 정도 평활화된 후에만 첫 번째 파생 모델을 사용하는 것이 가장 좋습니다.

평활은 가장 단순한 의미에서 로우패스 필터이기 때문에 종종 컨볼루션 커널을 사용하여 평활이 가장 잘(가장 쉽게) 수행되며 커널이 놀라운 양의 기능 보존/향상 기능을 제공할 수 있는 "쉐이핑"됩니다.최적의 커널을 찾는 프로세스는 다양한 방법을 사용하여 자동화될 수 있지만, 가장 좋은 방법은 단순한 무차별 대입(작은 커널을 찾는 데 많은 속도)일 수 있습니다.좋은 커널은 원래 데이터를 (의도대로) 크게 왜곡하지만 관심 있는 피크/밸리의 위치에는 영향을 주지 않습니다.

다행히도, 종종 간단한 SWAG("교육된 추측")를 통해 적합한 커널을 만들 수 있습니다.평활 커널의 폭은 원래 데이터에서 예상되는 가장 넓은 "관심 있는" 피크보다 약간 넓어야 하며, 그 모양은 해당 피크(단일 스케일 웨이블릿)와 유사합니다.평균 보존 커널(좋은 스무딩 필터가 되어야 하는 것)의 경우 커널 요소의 합은 정확하게 1.00이어야 하며 커널은 중심에 대해 대칭이어야 합니다(홀수의 요소를 가질 것임을 의미).

최적의 평활화 커널(또는 다른 데이터 내용에 최적화된 소수의 커널)이 주어지면 평활화 정도는 컨볼루션 커널의 "이득"에 대한 스케일링 계수가 됩니다.

"올바른" (최적의) 스무딩 정도(콘볼루션 커널 게인)를 결정하는 것은 자동화될 수도 있습니다.첫 번째 파생 데이터의 표준 편차와 평활 데이터의 표준 편차를 비교합니다.두 표준 편차의 비율이 평활화 정도의 변화에 따라 어떻게 변하는지를 사용하여 효과적인 평활화 값을 예측할 수 있습니다.몇 번의 수동 데이터 실행(진정으로 대표적인)만 있으면 됩니다.

위에 게시된 모든 이전 솔루션은 첫 번째 도함수를 계산하지만, 이를 통계적 척도로 취급하지 않으며, 위 솔루션은 기능 보존/향상 스무딩(미묘한 피크가 노이즈를 "넘는" 데 도움이 됨)을 수행하려고 시도하지 않습니다.

마지막으로, 나쁜 소식은:노이즈에 실제 피크(대역폭 중복)처럼 보이는 기능이 있을 때는 "실제" 피크를 찾는 것이 가장 큰 문제가 됩니다.다음으로 복잡한 솔루션은 일반적으로 인접한 "실제" 피크(피크 발생에 대한 최소 또는 최대 속도 등) 사이의 관계를 고려하는 더 긴 컨볼루션 커널("더 넓은 커널 구멍")을 사용하는 것입니다.또는 폭이 다른 커널을 사용하여 여러 개의 컨볼루션 패스를 사용하는 것(단, 더 빠른 경우에만: 순차적으로 수행되는 선형 컨볼루션은 항상 하나의 컨볼루션으로 함께 컨볼루션될 수 있다는 기본 수학적 진리).그러나 단일 단계에서 최종 커널을 직접 찾는 것보다 먼저 유용한 커널 시퀀스(다양한 폭)를 찾고 이를 함께 통합하는 것이 훨씬 더 쉬운 경우가 많습니다.

이것이 구글(그리고 아마도 좋은 통계 텍스트)이 공백을 메울 수 있는 충분한 정보를 제공하기를 바랍니다.저는 정말로 제가 작업한 예시나 링크를 제공할 시간이 있었으면 좋겠습니다.온라인에서 우연히 발견된 사람이 있다면, 여기에 올려주세요!

나는 numpy(원 라이너)에 훨씬 더 간단한 접근법이 있다고 생각합니다.

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

로컬 최대값 또는 최소값을 찾기 위해 목록(3-1, 9-3...)의 값 차이가 양수에서 음수(최대)로 또는 음수에서 양수(최소)로 변경될 때 확인하려고 합니다.그러므로, 먼저 우리는 차이점을 참조하십시오.그런 다음 기호를 찾고, 그 차이를 다시 취함으로써 기호의 변화를 찾습니다.(미분학의 첫 번째와 두 번째 미분처럼, 우리는 이산 데이터만 가지고 있고 연속 함수는 가지고 있지 않습니다.)

이 예제의 출력에는 극단값(목록의 첫 번째와 마지막 값)이 포함되어 있지 않습니다.또한 미적분학과 마찬가지로 두 번째 미분이 음수이면 최대값이 되고 양수이면 최소값이 됩니다.

따라서 다음과 같은 매치업이 있습니다.

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

Scipy 내장 기능 신호를 사용하는 것은 어떨까요?그 일을 하기 위해 _find_cwt를 찾으시겠습니까?

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]

결과:

maxima [ 0.9995736]
minima [ 0.09146464]

안부 전해요

업데이트: 그라데이션이 마음에 들지 않아 사용하기에 더 신뢰할 수 있음을 알게 되었습니다.numpy.diff.

소음 문제와 관련하여 수학적 문제는 소음을 보려면 앞에서 언급한 콘볼브와 같은 것을 사용할 수 있는 최대값/minima를 찾는 것입니다.

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

반복되는 값의 중심에서도 피크를 찾고자 했기 때문에 이러한 솔루션 중 어느 것도 제대로 작동하지 않았습니다.예를 들어, 에서

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

답은 다음과 같아야 합니다.

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

저는 이걸 고리를 이용해서 했어요.아주 깨끗하지 않다는 건 알지만, 일을 잘 해냅니다.

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 
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그리고.maxm각각 최소값과 최대값의 인덱스를 포함합니다.거대한 데이터 세트의 경우, 많은 최대값/minimas를 제공하므로 이 경우 먼저 곡선을 평활한 다음 이 알고리즘을 적용합니다.

기본적으로 확장 연산자를 사용하는 다른 솔루션:

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

최소치의 경우:

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

또한, 에서scipy.ndimage대체할 수 있습니다.rank_filter(x, -1, size=3)와 함께grey_dilation그리고.rank_filter(x, 0, size=3)와 함께grey_erosion로컬 정렬이 필요하지 않으므로 조금 더 빠릅니다.

또 다른 하나:

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

그리고... 또 다른 대답은.

이것은 추가 패키지가 필요하지 않습니다(넘피 제외).예를들면,

points = [ 0, 0, 1, 2, 3, 3, 2, 2, 3, 1, 1 ]
minimums   ^  ^              ^  ^     ^  ^

모든 로컬 최소값의 목록을 반환합니다.

result = [ 0, 1, 6, 7, 9, 10 ]

또한 최대치를 찾기 위해 쉽게 확장될 수 있습니다.

def find_valleys(points: np.ndarray, edges=True) -> list:
    """
    Find the indices of all points that are local minimums.

    :param np.ndarray points: a 1D array of numeric data
    :param bool edges: allows the first and last indices to be returned, defaults to True
    :return list: a list of integers, indices into the array
    """
    dif = np.diff(points)
    p = -1 if edges else 1
    s = 0
    result = []
    for i,d in enumerate(dif):
        if d < 0: s = i + 1
        if p < 0 and d > 0:   # found a valley
            result.extend(range(s,i + 1))
        if d: p = d
    if p < 0 and edges:
        result.extend(range(s,i + 2))
    return result

언급URL : https://stackoverflow.com/questions/4624970/finding-local-maxima-minima-with-numpy-in-a-1d-numpy-array

반응형