非常にノイズの多いデータがあり、信号の高低のエンベロープを処理しようとしています。これは、MATLABのこの例のようなものです。
http://uk.mathworks.com/help/signal/examples/signal-smoothing.html
「ピークエンベロープの抽出」 Pythonそれを行うことができる同様の関数はありますか?私のプロジェクト全体はPythonで書かれており、最悪の場合のシナリオです。私はmatplotlibの外観を好む...そして実際にcbaは、MATLABとPythonの間でこれらすべてのI/Oを実行しています...
おかげで、
Pythonで実行できる同様の関数はありますか?
私が知る限り、Numpy/Scipy/Pythonにはそのような機能はありません。ただし、作成するのはそれほど難しくありません。一般的な考え方は次のとおりです。
値のベクトル(s)が与えられた場合:
ご覧のとおり、これは3つのステップ(場所の検索、モデルの適合、モデルの評価)のシーケンスですが、2回適用されます。1回はエンベロープの上部に、1回はエンベロープに適用されます。
(s)の「ピーク」を収集するには、(s)の勾配が正から負に変化するポイントを見つける必要があり、(s)の「谷」を収集するには、(s)の勾配がポイントを見つける必要があります)負から正に変化します。
ピークの例:s = [4,5,4] 5-4は正、4-5は負
谷の例:s = [5,4,5] 4-5は負、5-4は正
たくさんのインラインコメントから始めるためのスクリプト例を以下に示します:
_from numpy import array, sign, zeros
from scipy.interpolate import interp1d
from matplotlib.pyplot import plot,show,hold,grid
s = array([1,4,3,5,3,2,4,3,4,5,4,3,2,5,6,7,8,7,8]) #This is your noisy vector of values.
q_u = zeros(s.shape)
q_l = zeros(s.shape)
#Prepend the first value of (s) to the interpolating values. This forces the model to use the same starting point for both the upper and lower envelope models.
u_x = [0,]
u_y = [s[0],]
l_x = [0,]
l_y = [s[0],]
#Detect peaks and troughs and mark their location in u_x,u_y,l_x,l_y respectively.
for k in xrange(1,len(s)-1):
if (sign(s[k]-s[k-1])==1) and (sign(s[k]-s[k+1])==1):
u_x.append(k)
u_y.append(s[k])
if (sign(s[k]-s[k-1])==-1) and ((sign(s[k]-s[k+1]))==-1):
l_x.append(k)
l_y.append(s[k])
#Append the last value of (s) to the interpolating values. This forces the model to use the same ending point for both the upper and lower envelope models.
u_x.append(len(s)-1)
u_y.append(s[-1])
l_x.append(len(s)-1)
l_y.append(s[-1])
#Fit suitable models to the data. Here I am using cubic splines, similarly to the MATLAB example given in the question.
u_p = interp1d(u_x,u_y, kind = 'cubic',bounds_error = False, fill_value=0.0)
l_p = interp1d(l_x,l_y,kind = 'cubic',bounds_error = False, fill_value=0.0)
#Evaluate each model over the domain of (s)
for k in xrange(0,len(s)):
q_u[k] = u_p(k)
q_l[k] = l_p(k)
#Plot everything
plot(s);hold(True);plot(q_u,'r');plot(q_l,'g');grid(True);show()
_
これにより、次の出力が生成されます。
さらなる改善のポイント:
上記のコードは、いくつかのしきい値「距離」(Tl)(たとえば、時間)より近くに発生している可能性のあるピークまたは谷をフィルターしません。これは、envelope
の2番目のパラメーターに似ています。ただし、_u_x,u_y
_の連続する値の違いを調べることで簡単に追加できます。
ただし、前述のポイントをすばやく改善するには、移動平均フィルターでデータをローパスフィルター処理します[〜#〜] before [〜#〜]上限および下限エンベロープ関数の補間。これは、適切な移動平均フィルターで(s)を畳み込むことで簡単に実行できます。ここで詳細な説明を行わずに(必要に応じて実行できます)、連続するN個のサンプルで動作する移動平均フィルターを作成するには、s_filtered = numpy.convolve(s, numpy.ones((1,N))/float(N)
のようにします。 (N)が高いほど、データは滑らかになります。ただし、スムージングフィルターの group delay と呼ばれるもののため、これにより(s)値(N/2)サンプルが(_s_filtered
_で)右にシフトします。移動平均の詳細については、 このリンク を参照してください。
お役に立てれば。
(元のアプリケーションに関する詳細情報が提供されている場合は、応答を修正して満足しています。おそらく、データをより適切な方法で前処理できます(?))
@A_Aの答えに基づいて、符号チェックをnim/maxテストに置き換え、より堅牢にします。
import numpy as np
import scipy.interpolate
import matplotlib.pyplot as pt
%matplotlib inline
t = np.multiply(list(range(1000)), .1)
s = 10*np.sin(t)*t**.5
u_x = [0]
u_y = [s[0]]
l_x = [0]
l_y = [s[0]]
#Detect peaks and troughs and mark their location in u_x,u_y,l_x,l_y respectively.
for k in range(2,len(s)-1):
if s[k] >= max(s[:k-1]):
u_x.append(t[k])
u_y.append(s[k])
for k in range(2,len(s)-1):
if s[k] <= min(s[:k-1]):
l_x.append(t[k])
l_y.append(s[k])
u_p = scipy.interpolate.interp1d(u_x, u_y, kind = 'cubic', bounds_error = False, fill_value=0.0)
l_p = scipy.interpolate.interp1d(l_x, l_y, kind = 'cubic', bounds_error = False, fill_value=0.0)
q_u = np.zeros(s.shape)
q_l = np.zeros(s.shape)
for k in range(0,len(s)):
q_u[k] = u_p(t[k])
q_l[k] = l_p(t[k])
pt.plot(t,s)
pt.plot(t, q_u, 'r')
pt.plot(t, q_l, 'g')
関数の増加が予想される場合は、以下を試してください。
for k in range(1,len(s)-2):
if s[k] <= min(s[k+1:]):
l_x.append(t[k])
l_y.append(s[k])
下の封筒用。
回答は補間を使用してMatlab関数の動作を模倣していたため、これは私にとってはあまり役に立ちませんでした。また、非常に長くノイズの多いデータ配列では、少し不安定になり(偽の振動)、時間がかかる傾向がありました。私はここで私のためにかなりうまくいくように見える、よりシンプルでより速いバージョンで貢献します:
import numpy as np
from matplotlib import pyplot as plt
def hl_envelopes_idx(s,dmin=1,dmax=1):
"""
s : 1d-array, data signal from which to extract high and low envelopes
dmin, dmax : int, size of chunks, use this if size of data is too big
"""
# locals min
lmin = (np.diff(np.sign(np.diff(s))) > 0).nonzero()[0] + 1
# locals max
lmax = (np.diff(np.sign(np.diff(s))) < 0).nonzero()[0] + 1
"""
# the following might help in some case by cutting the signal in "half"
s_mid = np.mean(s) (0 if s centered or more generally mean of signal)
# pre-sort of locals min based on sign
lmin = lmin[s[lmin]<s_mid]
# pre-sort of local max based on sign
lmax = lmax[s[lmax]>s_mid]
"""
# global max of dmax-chunks of locals max
lmin = lmin[[i+np.argmin(s[lmin[i:i+dmin]]) for i in range(0,len(lmin),dmin)]]
# global min of dmin-chunks of locals min
lmax = lmax[[i+np.argmax(s[lmax[i:i+dmax]]) for i in range(0,len(lmax),dmax)]]
return lmin,lmax
たとえば、次のようになります。
t = np.linspace(0,20*np.pi,5000,endpoint=False)
s = 0.8*np.cos(t)**3 + 0.5*np.sin(np.exp(1)*t)
high_idx, low_idx = hl_envelopes_idx(s)
# plot
plt.plot(t,s,label='signal')
plt.plot(t[high_idx], s[high_idx], 'r', label='low')
plt.plot(t[low_idx], s[low_idx], 'g', label='high')
または、この騒々しい時間サンプルの場合:
t = np.linspace(0,2*np.pi,5000,endpoint=False)
s = 5*np.cos(t) + np.random.Rand(len(t))
high_idx, low_idx = hl_envelopes_idx(s,dmin=15,dmax=15)
# plot
plt.plot(t,s,label='signal')
plt.plot(t[high_idx], s[high_idx], 'r', label='low')
plt.plot(t[low_idx], s[low_idx], 'g', label='high')
または最後に、形状のはるかに複雑な信号(18867925,)
(ここには含めませんでした):
お役に立てれば。