web-dev-qa-db-ja.com

MATLABのn次元行列の各要素を反復処理するにはどうすればよいですか?

私は問題があります。 MATLABのn次元行列のすべての要素を反復処理する必要があります。問題は、任意の次元数でこれを行う方法がわからないことです。言えると思う

for i = 1:size(m,1)
    for j = 1:size(m,2)
        for k = 1:size(m,3)

など、しかし、任意の数の次元でそれを行う方法はありますか?

77
rlbond

線形インデックスを使用して、各要素にアクセスできます。

for idx = 1:numel(array)
    element = array(idx)
    ....
end

これは、現在のi、j、kを知る必要がない場合に便利です。ただし、現在のインデックスを知る必要がない場合は、arrayfun()を使用することをお勧めします

89
Andrew

Matlabの配列の線形インデックスのアイデアは重要です。 MATLABの配列は、実際にはメモリ内の要素のベクトルにすぎません。 MATLABでは、行と列のインデックス、または単一の線形インデックスを使用できます。例えば、

A = magic(3)
A =
     8     1     6
     3     5     7
     4     9     2

A(2,3)
ans =
     7

A(8)
ans =
     7

配列をベクトルに展開することで、要素がメモリに格納される順序を確認できます。

A(:)
ans =
     8
     3
     4
     1
     5
     9
     6
     7
     2

ご覧のとおり、8番目の要素は数値7です。実際、関数findは結果を線形インデックスとして返します。

find(A>6)
ans =
     1
     6
     8

その結果、単一のループを使用して、一般的なn-d配列の各要素に順番にアクセスできます。たとえば、Aの要素を2乗したい場合(はい、これを行うより良い方法があることを知っています)、これを行うことができます。

B = zeros(size(A));
for i = 1:numel(A)
  B(i) = A(i).^2;
end

B
B =
    64     1    36
     9    25    49
    16    81     4

線形インデックスがより便利な多くの状況があります。線形インデックスと2つ(またはそれ以上)の次元の添え字間の変換は、sub2indおよびind2sub関数を使用して行われます。

一般に、線形インデックスはmatlabのすべての配列に適用されます。したがって、構造体、セル配列などで使用できます。線形インデックスの唯一の問題は、サイズが大きくなりすぎることです。 MATLABは、32ビット整数を使用してこれらのインデックスを保存します。そのため、配列に合計2 ^ 32を超える要素がある場合、線形インデックスは失敗します。スパース行列を頻繁に使用する場合にのみ問題になりますが、ときどきこれが問題を引き起こすことがあります。 (64ビットのMATLABリリースは使用していませんが、幸運な個人の問題は解決されたと思います。)

33
user85109

他のいくつかの回答で指摘したように、単一のforループで 線形インデックス from 1からnumel(A)を使用して、(任意の次元の)マトリックスAのすべての要素を反復処理できます。使用できる関数もいくつかあります: arrayfun および cellfun

最初に、Aの各要素(my_funcと呼ばれる)に適用する関数があると仮定します。まず、この関数に 関数ハンドル を作成します。

fcn = @my_func;

Aが任意の次元の行列(double、singleなど)の場合、arrayfunを使用して各要素にmy_funcを適用できます。

outArgs = arrayfun(fcn, A);

Aが任意の次元の セル配列 である場合、cellfunを使用してmy_funcを各セルに適用できます。

outArgs = cellfun(fcn, A);

関数my_funcは、入力としてAを受け入れなければなりません。 my_funcからの出力がある場合、それらはoutArgsに配置され、Aと同じサイズ/次元になります。

出力に関する注意点... my_funcAの異なる要素を操作するときに異なるサイズとタイプの出力を返す場合、outArgsをセル配列にする必要があります。これは、追加のパラメーター/値のペアでarrayfunまたはcellfunを呼び出すことで実行されます。

outArgs = arrayfun(fcn, A, 'UniformOutput', false);
outArgs = cellfun(fcn, A, 'UniformOutput', false);
15
gnovice

もう1つのトリックは、ind2subsub2indを使用することです。 numelおよびsizeと組み合わせることで、次のようなことができます。これにより、N次元配列が作成され、「対角線」上のすべての要素が1に設定されます。

d = zeros( 3, 4, 5, 6 ); % Let's pretend this is a user input
nel = numel( d );
sz = size( d );
szargs = cell( 1, ndims( d ) ); % We'll use this with ind2sub in the loop
for ii=1:nel
    [ szargs{:} ] = ind2sub( sz, ii ); % Convert linear index back to subscripts
    if all( [szargs{2:end}] == szargs{1} ) % On the diagonal?
        d( ii ) = 1;
    end
end
13
Edric

これらのソリューションは、numel;を使用するよりも高速です(約11%)。

for idx = reshape(array,1,[]),
     element = element + idx;
end

または

for idx = array(:)',
    element = element + idx;
end

UPD。最後の回答で検出されたエラーのtnx @rayryeng


免責事項

この投稿が参照したタイミング情報は、作成された基本的なタイプミスのために不正確で不正確です(以下のコメントストリームと 編集履歴 -特にこの回答の最初のバージョンを参照)。 警告Emptor

1
mathcow

あなたは再帰関数に仕事をさせることができます

  • L = size(M)
  • idx = zeros(L,1)
  • length(L)を最大深度とします
  • ループfor idx(depth) = 1:L(depth)
  • 深さがlength(L)の場合、要素操作を実行し、そうでない場合はdepth+1を使用して関数を再度呼び出します

すべてのポイントをチェックする場合、ベクトル化されたメソッドほど高速ではありませんが、ほとんどのポイントを評価する必要がない場合は、かなり時間を節約できます。

1