2

最初と最後にNaNがある30*26000サイズのマトリックスを使用しています。NaNも各行に散らばっています。線形補間でNaNを入力できますが、各行の最初と最後にNaNが残ります。端でこれらのNaNを置き換えるために外挿することは、私のデータセットにとって理想的ではありません。

マトリックスをトリミングしたいだけです。たとえば、3x6の行列を考えてみましょう。

NaN NaN 1 2  3  NaN
NaN  1  2 3 NaN NaN
 1  NaN 2 3  4   5

行がNaNで開始または終了しないように、左端と右端の列を切り取ります。

1 2
2 3
2 3

したがって、3x2の行列が残ります。

Matlabでこれを行うにはどうすればよいですか?(速度が最適化されています。これを100万サイズのマトリックスに適用する必要があります)

ありがとう!

4

3 に答える 3

7

あなたの例では、次のことができます。

NaNと数値を使用して行列を作成します。

ind1 = sum(isnan(a),1); % count the NaN values along columns

s = find(ind1 == 0, 1, 'first'); % find the first column without any NaN

e = find(ind1 == 0, 1, 'last'); % find the last column without any NaN

したがって、行列のこの部分をs番目からe番目の列に保持します。

b = a(:、s:e);

カラムにNaNがない場合は、追加のチェックが必要になる場合があります。

于 2012-11-21T23:46:17.947 に答える
2

まず、argyrisのベクトル化されたソリューションは完全にうまく機能します(+1)。速度が最適化されたソリューションが必要だと強調されたため、これを投稿するだけです。さて、アージリス解の欠点は、sumisnan操作が行列全体で実行されることです。これは、最初の非NaNカラムを見つけるためにどちらかの側に長い道のりをたどる必要がある場合に最適です。しかし、そうでない場合はどうなりますか?数列に入るだけでよいという事実を利用するループベースのソリューションの方がうまくいく可能性があります(特に、JITアクセラレーターが単一ループを迅速に実行するのにどれだけ優れているかを考えると)。アージリスと私のソリューションの両方を含む速度テストをまとめました。

%#Set up an example case using the matrix size you indicated in the question
T = 30;
N = 26000;
X = rand(T, N);
TrueL = 8;
TrueR = N - 8;
X(:, 1:TrueL) = NaN;
X(:, TrueR:end) = NaN;

%#argyris solution
tic
I1 = sum(isnan(X));
argL = find(I1 == 0, 1, 'first');
argR = find(I1 == 0, 1, 'last');
Soln1 = X(:, argL:argR);
toc

%#My loop based solution (faster if TrueL and TrueR are small)
tic
for n = 1:N
    if ~any(isnan(X(:, n)))
        break
    end
end
ColinL = n;
for n = N:-1:1
    if ~any(isnan(X(:, n)))
        break
    end
end
ColinR = n;
Soln2 = X(:, ColinL:ColinR);
toc

上記の例では、ソリューションは最初の8列と最後の8列を削除する必要があります。スピードテストの結果は?

Elapsed time is 0.002919 seconds. %#argyris solution
Elapsed time is 0.001007 seconds. %#My solution

ループベースのソリューションは、ほぼ3倍高速です。では、どちらかの側で削除する必要のある列の数を100に増やしましょう。

Elapsed time is 0.002769 seconds. %#argyris solution
Elapsed time is 0.001999 seconds. %#My solution

まだ先です。両側の1000列はどうですか?

Elapsed time is 0.003597 seconds. %#argyris solution
Elapsed time is 0.003719 seconds. %#My solution

だから私たちは転換点を見つけました(少なくとも私のマシンでは-クアッドコアi7、Linux Mint v12、Matlab R2012b)。どちらかの側に約1000列を入力する必要がある場合は、ベクトル化されたソリューションを使用することをお勧めします。

注意の最後の注意:ルーチンが別の(おそらく無関係の)ループ内で発生している場合は、速度の比較をやり直す必要があります。これは、私のソリューションにダブルループが含まれるようになるためです。ループが無関係であっても、JITアクセラレーターは二重ループではあまり良くありません。私は自分のマシンでいくつかの簡単なテストを行いましたが、小さなTrueLとTrueR(つまり、100未満)のソリューションはまだ先に出ていますが、外側のループが存在しない場合ほど大きな利点はありません。

とにかく、これがあなたや読書に来る他の人に役立つことを願っています。

乾杯!

編集:私は、アナガーの非常にきちんとしたワンライナー(+1)を組み込んだいくつかの速度テストを行いました。削除する列の数が少ない場合は、ループベースのソリューションとほぼ同じように機能します。驚いたことに、アージリスのソリューションとは異なり、削除する列の数が多い場合は、それほど適切にスケーリングされませんでした。それは私が今使っているコンピュータと関係があるかもしれません:Windowsマシンを動かしてください-私はそれを完全に信頼したことはありません:-)

于 2012-11-22T02:01:14.807 に答える
2

以前に提案された両方のソリューションは素晴らしいです、私は完全を期すためにこのワンライナーを投稿しています:

A(:,isfinite(sum(A)))

ans =

 1     2
 2     3
 2     3

最初に行の合計を計算し、その後で.を呼び出すことにより、行列エントリを2回(Colinが指摘したように)通過することを回避しますisfinite。また、呼び出しを削除しましたfind。代わりに論理インデックスを使用できるため、呼び出しは必要ありません。

ここにはコンピューターがないので、パフォーマンステストは省略します。

于 2012-11-22T11:52:19.670 に答える