1D numpy配列で極大/極小を見つけることができるnumpy / scipyのモジュール関数を提案できますか?明らかに、これまでで最も単純なアプローチは、最も近い隣人を調べることですが、私は厄介なディストリビューションの一部である受け入れられた解決策が欲しいです。
1D numpy配列で極大/極小を見つけることができるnumpy / scipyのモジュール関数を提案できますか?明らかに、これまでで最も単純なアプローチは、最も近い隣人を調べることですが、私は厄介なディストリビューションの一部である受け入れられた解決策が欲しいです。
回答:
あなたがa
それらの隣人より小さい1d配列のすべてのエントリーを探しているなら、あなたは試すことができます
numpy.r_[True, a[1:] < a[:-1]] & numpy.r_[a[:-1] < a[1:], True]
この手順の前に、を使用して配列を平滑化することもできますnumpy.convolve()
。
これ専用の機能はないと思います。
<
では、>
あなたの代わりに、最小値の極大値を与えるだろう
[False False]
何が問題なのでしょうか?
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のインデックスであることに注意してください。値を取得するには、次のことを試してください。
>>> x[argrelextrema(x, np.greater)[0]]
scipy.signal
またargrelmax
、argrelmin
それぞれ最大値と最小値を提供します。
np.random.random(12)
12個のランダムな値を生成します。これらは関数のデモに使用されますargrelextrema
。
test02=np.array([10,4,4,4,5,6,7,6])
しません。連続する値は極小値として認識されません。
ノイズが多すぎない曲線の場合、次の小さなコードスニペットをお勧めします。
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
です。
[1, 2, 2, 3, 3, 3, 2, 2, 1]
、極大値は明らかに3の中間にあります。しかし、提供した関数を実行すると、インデックス2、6で最大値、インデックス1、3、5、7で最小値が得られますが、私にはあまり意味がありません。
+1
代わりにこれを回避するには。np.diff()
np.gradient()
役立つ別のアプローチ(より多くの単語、より少ないコード):
極大と極小の位置は、一次導関数のゼロ交差の位置でもあります。一般に、極大値と極小値を直接見つけるよりも、ゼロクロッシングを見つける方がはるかに簡単です。
残念ながら、一次導関数はノイズを「増幅」する傾向があるため、元のデータに大きなノイズが存在する場合、一次導関数は、元のデータにある程度の平滑化が適用された後でのみ使用するのが最適です。
平滑化は、最も単純な意味ではローパスフィルターであるため、畳み込みカーネルを使用することで平滑化が最もよく(よく、最も簡単に)行われることが多く、そのカーネルを使用して驚くべき量の機能保持/強化機能を提供できます。 。最適なカーネルを見つけるプロセスは、さまざまな手段を使用して自動化できますが、最善の方法は、単純なブルートフォースです(小さなカーネルを見つけるにはかなり高速です)。良いカーネルは(意図したとおりに)元のデータを大幅に歪めますが、対象のピーク/バレーの場所には影響しません。
幸いなことに、単純なSWAG(「教育的推測」)を使用して適切なカーネルを作成できることがよくあります。平滑化カーネルの幅は、元のデータで予想される最も広い「興味深い」ピークよりも少し広くする必要があり、その形状はそのピーク(単一スケールのウェーブレット)に似ています。平均を維持するカーネル(適切な平滑化フィルターが何であるべきか)の場合、カーネル要素の合計は正確に1.00に等しく、カーネルはその中心に対して対称である必要があります(つまり、要素の数が奇数になります)。
最適なスムージングカーネル(または異なるデータコンテンツ用に最適化された少数のカーネル)が与えられると、スムージングの程度がたたみ込みカーネル(の「ゲイン」)のスケーリング係数になります。
"正しい"(最適な)平滑化の程度(畳み込みカーネルゲイン)の決定は、自動化することもできます。一次導関数データの標準偏差と平滑化データの標準偏差を比較します。2つの標準偏差の比率がスムージングカムの度合いの変化とともにどのように変化するかを使用して、効果的なスムージング値を予測します。いくつかの手動のデータ実行(真に代表的なもの)で十分です。
上記のすべての以前のソリューションは一次導関数を計算しますが、それらは統計的測度としては扱いません。また、上記のソリューションは、特徴を維持/平滑化することを試みません(ノイズを「飛び越えて」微妙なピークを助けるため)。
最後に、悪いニュース:「実際の」ピークを見つけることは、ノイズが実際のピーク(帯域幅のオーバーラップ)のように見える機能も持っている場合、王室の苦痛になります。次のより複雑なソリューションは、一般に、隣接する「実際の」ピーク間の関係(ピーク発生の最小または最大レートなど)を考慮した、より長い畳み込みカーネル(「より広いカーネルアパーチャ」)を使用するか、複数の幅の異なるカーネルを使用してたたみ込みが通過します(ただし、より高速な場合のみ:シーケンスで実行される線形たたみ込みは常に単一のたたみ込みにたたみ込みできるというのは、基本的な数学的真理です)。しかし、単一のステップで最終的なカーネルを直接見つけるよりも、最初に(幅が変化する)有用なカーネルのシーケンスを見つけてそれらを畳み込む方がはるかに簡単です。
うまくいけば、これは、Google(そしておそらく良い統計テキスト)がギャップを埋めるのに十分な情報を提供します。実際に動作する例、または例へのリンクを提供する時間をいただければ幸いです。誰かがオンラインで見つけた場合は、ここに投稿してください!
SciPyバージョン1.1以降は、find_peaksも使用できます。以下は、ドキュメント自体から取った2つの例です。
height
引数を使用して、特定のしきい値を超えるすべての最大値(この例では、すべての非負の最大値)を選択できます。これは、ノイズの多いベースラインを処理する必要がある場合に非常に役立ちます。最小値を求めたい場合は、入力を乗算するだけです。による-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()
別の非常に役立つ引数はですdistance
。これは、2つのピーク間の最小距離を定義します。
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()
Scipy組み込み関数signal.find_peaks_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
。それがあなたの望むことをするかどうか私に知らせてください。
ノイズの問題に関して、数学的な問題は、最大値/最小値を特定することです。ノイズを見たい場合は、前述のコンボルブのようなものを使用できます。
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()
この質問は本当に古いですが。私はnumpy(1つのライナー)ではるかに単純なアプローチがあると信じています。
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次および2次導関数のようなもので、離散データのみがあり、連続関数はありません。)
この例の出力には、極値(リストの最初と最後の値)が含まれていません。また、微積分のように、2次導関数が負の場合は最大値を持ち、正の場合は最小値を持っています。
したがって、次の対戦があります。
[1, 3, 9, 5, 2, 5, 6, 9, 7]
[0, -2, 0, 2, 0, 0, -2]
Max Min Max
繰り返し値の中心にもピークを見つけたかったので、これらの解決策はどれもうまくいきませんでした。たとえば、
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
、それぞれ最小値と最大値のインデックスを含みます。巨大なデータセットの場合、最大値/最小値が多くなるため、最初に曲線を平滑化してから、このアルゴリズムを適用します。
基本的に膨張演算子を使用する別のソリューション:
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