シーケンスが非常に長いと想像してください。シーケンスがすべてゼロである間隔を見つける最も効率的な方法は何ですか(より正確には、シーケンスはほぼゼロの値に低下しますabs(X)<eps
):
簡単にするために、次のシーケンスを想定します。
sig = [1 1 0 0 0 0 1 1 1 1 1 0 1 0 0 0 1 1 1 1 1 1 1 1 0 0 1 1 1 0];
次の情報を取得しようとしています。
startIndex EndIndex Duration
3 6 4
12 12 1
14 16 3
25 26 2
30 30 1
次に、この情報を使用して、期間> =から指定された値までの間隔を見つけます(たとえば、3
)、これらすべての間隔の値のインデックスを組み合わせて返します。
indices = [3 4 5 6 14 15 16];
その最後の部分は前の質問に関連しています:
これは私がこれまでに持っているものです:
sig = [1 1 0 0 0 0 1 1 1 1 1 0 1 0 0 0 1 1 1 1 1 1 1 1 0 0 1 1 1 0];
len = length(sig);
thresh = 3;
%# align the signal with itself successively shifted by one
%# v will thus contain 1 in the starting locations of the zero interval
v = true(1,len-thresh+1);
for i=1:thresh
v = v & ( sig(i:len-thresh+i) == 0 );
end
%# extend the 1's till the end of the intervals
for i=1:thresh-1
v(find(v)+1) = true;
end
%# get the final indices
v = find(v);
コードをベクトル化/最適化することを検討していますが、他のソリューションも利用できます。私は多数の長い生体信号を処理しているので、空間と時間の効率が非常に重要であることを強調する必要があります。
これらは、与えられたベクトルsig
から始めて、ベクトル化された方法で問題を解決するために私が取るステップです。
まず、ベクトルにしきい値を設定して、ゼロと1のベクトルtsig
を取得します(信号の絶対値がゼロに十分近くなるゼロ、他の場所にあるゼロ)。
tsig = (abs(sig) >= eps); %# Using eps as the threshold
次に、関数 [〜#〜] diff [〜#〜] および [〜#〜] find [ 〜#〜] :
dsig = diff([1 tsig 1]);
startIndex = find(dsig < 0);
endIndex = find(dsig > 0)-1;
duration = endIndex-startIndex+1;
次に、ある値(この例では3など)以上の期間を持つゼロの文字列を見つけます。
stringIndex = (duration >= 3);
startIndex = startIndex(stringIndex);
endIndex = endIndex(stringIndex);
最後に、 リンクされた質問に対する私の回答からの方法 を使用して、インデックスの最終セットを生成します。
indices = zeros(1,max(endIndex)+1);
indices(startIndex) = 1;
indices(endIndex+1) = indices(endIndex+1)-1;
indices = find(cumsum(indices));
長さthresh
のゼロの文字列を見つけることにより、これを文字列検索タスクとして解決できます(STRFIND関数は非常に高速です)
startIndex = strfind(sig, zeros(1,thresh));
長い部分文字列は複数の場所でマークされますが、startIndex
で始まる間隔からstart+thresh-1
で終わる間隔までの間に場所を追加すると、最終的に結合されることに注意してください。
indices = unique( bsxfun(@plus, startIndex', 0:thresh-1) )';
リンクされた質問 から@gnoviceによって、この最後のステップをCUMSUM/FINDソリューションといつでも交換できることに注意してください。
function indice=sigvec(sig,thresh)
%extend sig head and tail to avoid 0 head and 0 tail
exsig=[1,sig,1];
%convolution sig with extend sig
cvexsig=conv(exsig,ones(1,thresh));
tempsig=double(cvexsig==0);
indice=find(conv(tempsig,ones(1,thresh)))-thresh;
genoviceによる上記の回答は、次のようにベクトル内の非ゼロ要素のインデックスを見つけるように変更できます。
tsig = (abs(sig) >= eps);
dsig = diff([0 tsig 0]);
startIndex = find(dsig > 0);
endIndex = find(dsig < 0)-1;
duration = endIndex-startIndex+1;
それを行う最もMATLAB /「ベクトル化された」方法は、[-11]のようなフィルターを使用して信号の畳み込みを計算することだと思います。関数convのドキュメントを参照する必要があります。次に、convの出力で、findを使用して関連するインデックスを取得します。
Gnoviceが示したように、「ゼロに近い」を実際にゼロにするためのしきい値テストを実行します。
logcl = abs(sig(:)) >= zero_tolerance;
次に、累積合計が増加していない地域を見つけます。
cs = cumsum(logcl);
islands = cs(1+thresh:end) == cs(1:end-thresh);
覚えている インデックスの範囲を埋めるためのgnoviceの素晴らしい方法
v = zeros(1,max(endInd)+1); %# An array of zeroes v(startInd) = 1; %# Place 1 at the starts of the intervals v(endInd+1) = v(endInd+1)-1; %# Add -1 one index after the ends of the intervals indices = find(cumsum(v)); %# Perform a cumulative sum and find the nonzero entries
islands
ベクトルにはすでにstartInd
の場所にあるものがあり、私たちの目的のために、endInd
は常にthresh
スポット後に来ることに注意してください(より長い実行にはislands
にあるもの)
endcap = zeros(thresh,1);
indices = find(cumsum([islands ; endcap] - [endcap ; islands]))
sig = [1 1 0 0 0 0 1 1 1 1 1 0 1 0 0 0 1 1 1 1 1 1 1 1 0 0 1 1 1 0];
logcl = abs(sig(:)) >= .1;
cs = cumsum(logcl);
islands = cs(1+thresh:end) == cs(1:end-thresh);
endcap = zeros(thresh,1);
indices = find(cumsum([islands ; endcap] - [endcap ; islands]))
indices = 2 3 4 5 13 14 15