web-dev-qa-db-ja.com

MATLABで行列のすべての行/列に関数を適用するにはどうすればよいですか?

たとえば、v + 1と言うことで、ベクトル内のすべてのアイテムに関数を適用できます。または、関数arrayfunを使用できます。 forループを使用せずに行列のすべての行/列に対してそれを行うにはどうすればよいですか?

99
FurtiveFelon

sumprod などの多くの組み込み操作は、行または列をまたいで操作できるため、リファクタリングできる場合がありますこれを利用するために適用する関数。

それが実行可能なオプションではない場合、1つの方法は、 mat2cell または num2cell を使用して行または列をセルに収集することです。 cellfun 結果のセル配列を操作します。

例として、マトリックスMの列を合計するとします。これは、単に sum を使用して行うことができます。

M = magic(10);           %# A 10-by-10 matrix
columnSums = sum(M, 1);  %# A 1-by-10 vector of sums for each column

そして、より複雑な num2cell // cellfun オプションを使用してこれを行う方法を次に示します。

M = magic(10);                  %# A 10-by-10 matrix
C = num2cell(M, 1);             %# Collect the columns into cells
columnSums = cellfun(@sum, C);  %# A 1-by-10 vector of sums for each cell
68
gnovice

より不明瞭なMatlab関数 bsxfun が必要な場合があります。 Matlabのドキュメントから、bsxfunは「シングルトン展開を有効にして、関数ハンドルfunで指定された要素ごとのバイナリ演算を配列AとBに適用します。」

上記の@gnoviceは、合計および他の基本関数が最初の非シングルトン次元で既に動作していることを示しています(つまり、行が複数ある場合は行、行が1つしかない場合は列、下位の次元がすべてsize == 1の場合は上位の次元) )。ただし、bsxfunは、(特に)ユーザー定義関数を含むすべての関数で機能します。

たとえば、行列Aと行ベクトルBがあるとします。たとえば、次のようにします。

A = [1 2 3;
     4 5 6;
     7 8 9]
B = [0 1 2]

Aのすべての要素をBの対応する列のべき乗でベクトルCで返す関数power_by_colが必要です。

上記の例から、Cは3x3行列です。

C = [1^0 2^1 3^2;
     4^0 5^1 6^2;
     7^0 8^1 9^2]

すなわち、

C = [1 2 9;
     1 5 36;
     1 8 81]

これは、repmatを使用してブルートフォースで行うことができます。

C = A.^repmat(B, size(A, 1), 1)

または、bsxfunを使用してこれを上品な方法で行うことができます。これは、内部的にrepmatステップを処理します。

C = bsxfun(@(x,y) x.^y, A, B)

したがって、bsxfunを使用すると、いくつかの手順を省略できます(Aの次元を明示的に計算する必要はありません)。ただし、一部の非公式のテストでは、適用する関数(上記の私のべき関数など)が単純な場合、repmatの速度が約2倍になることがわかりました。そのため、シンプルにするかスピードにするかを選択する必要があります。

24
Daniel Golden

これがどれほど効率的であるかについてコメントすることはできませんが、解決策は次のとおりです。

applyToGivenRow = @(func, matrix) @(row) func(matrix(row, :))
applyToRows = @(func, matrix) arrayfun(applyToGivenRow(func, matrix), 1:size(matrix,1))'

% Example
myMx = [1 2 3; 4 5 6; 7 8 9];
myFunc = @sum;

applyToRows(myFunc, myMx)
19
Alex

Alexの答え に基づいて、より一般的な関数を次に示します。

applyToGivenRow = @(func, matrix) @(row) func(matrix(row, :));
newApplyToRows = @(func, matrix) arrayfun(applyToGivenRow(func, matrix), 1:size(matrix,1), 'UniformOutput', false)';
takeAll = @(x) reshape([x{:}], size(x{1},2), size(x,1))';
genericApplyToRows = @(func, matrix) takeAll(newApplyToRows(func, matrix));

以下は、2つの関数の比較です。

>> % Example
myMx = [1 2 3; 4 5 6; 7 8 9];
myFunc = @(x) [mean(x), std(x), sum(x), length(x)];
>> genericApplyToRows(myFunc, myMx)

ans =

     2     1     6     3
     5     1    15     3
     8     1    24     3

>> applyToRows(myFunc, myMx)
??? Error using ==> arrayfun
Non-scalar in Uniform output, at index 1, output 1.
Set 'UniformOutput' to false.

Error in ==> @(func,matrix)arrayfun(applyToGivenRow(func,matrix),1:size(matrix,1))'
11
Wok

完全性/利益のために、matlabには要素ごとではなく行ごとのデータを操作できる機能があることを追加したいと思います。 rowfunhttp://www.mathworks.se/help/matlab/ref/rowfun.html )と呼ばれますが、唯一の「問題」はそれが動作することですtableshttp://www.mathworks.se/help/matlab/ref/table.html )ではなく行列

6
kamjagin

R2016b以降、この質問に対する答えの進化する性質に加えて、MATLABは暗黙的にシングルトン次元を拡張し、多くの場合bsxfunの必要性を取り除きます。

r2016bリリースノート から:

暗黙的な拡張:要素単位の操作と関数を配列に適用し、長さ1の次元を自動的に拡張します

暗黙的な展開は、スカラー展開の一般化です。スカラー拡張では、要素ごとの操作を容易にするために、スカラーは別の配列と同じサイズに拡張されます。暗黙的な展開では、配列に互換性のあるサイズがある限り、ここにリストした要素ごとの演算子と関数は、入力を暗黙的に同じサイズに展開できます。すべての次元で入力の次元サイズが同じか、その1つが1である場合、2つの配列のサイズは互換性があります。詳細については、「基本操作と配列とマトリックス操作の互換配列サイズ」を参照してください。

Element-wise arithmetic operators — +, -, .*, .^, ./, .\

Relational operators — <, <=, >, >=, ==, ~=

Logical operators — &, |, xor

Bit-wise functions — bitand, bitor, bitxor

Elementary math functions — max, min, mod, rem, hypot, atan2, atan2d

たとえば、行列Aの各列の平均を計算し、A-mean(A)を使用して各列から平均値のベクトルを減算できます。

以前は、この機能はbsxfun関数を介して利用できました。 bsxfunのほとんどの使用を、暗黙的な展開をサポートする関数と演算子の直接呼び出しに置き換えることをお勧めします。 bsxfunを使用する場合と比較して、暗黙的な拡張は、高速化、メモリ使用量の向上、コードの可読性の向上を実現します。

4
craigim

受け入れられた答えは、最初にセルに変換してからcellfunを使用してすべてのセルを操作することです。特定のアプリケーションはわかりませんが、一般的には、行列を操作するために bsxfun を使用する方が効率的だと思います。基本的に、bsxfunは、2つの配列に要素ごとに演算を適用します。したがって、n x 1ベクトルの各アイテムにm x 1ベクトルの各アイテムを乗算してn x m配列を取得する場合は、次のように使用できます。

vec1 = [ stuff ];    % n x 1 vector
vec2 = [ stuff ];    % m x 1 vector
result = bsxfun('times', vec1.', vec2);

これにより、resultという行列が得られます。ここで、(i、j)エントリは、vec1のi番目の要素にvec2のj番目の要素を掛けたものになります。

あらゆる種類の組み込み関数にbsxfunを使用でき、独自の関数を宣言できます。ドキュメントには多くの組み込み関数のリストがありますが、基本的には、引数として2つの配列(ベクトルまたは行列)を受け入れる関数に名前を付けて、機能させることができます。

1
Engineero

上記の回答はどれも「箱から出して」すぐには機能しませんでしたが、他の回答のアイデアをコピーして得られる次の機能は機能します。

apply_func_2_cols = @(f,M) cell2mat(cellfun(f,num2cell(M,1), 'UniformOutput',0));

関数fを取り、それを行列Mのすべての列に適用します。

たとえば、次のとおりです。

f = @(v) [0 1;1 0]*v + [0 0.1]';
apply_func_2_cols(f,[0 0 1 1;0 1 0 1])

 ans =

   0.00000   1.00000   0.00000   1.00000
   0.10000   0.10000   1.10000   1.10000
1
patapouf_ai

Matlabの最近のバージョンでは、Tableデータ構造を有利に使用できます。 「rowfun」操作もありますが、これを行うだけで簡単になりました。

a = magic(6);
incrementRow = cell2mat(cellfun(@(x) x+1,table2cell(table(a)),'UniformOutput',0))

または、古いMatlabバージョン用に、テーブルを必要としない古いバージョンがあります。

dataBinner = cell2mat(arrayfun(@(x) Binner(a(x,:),2)',1:size(a,1),'UniformOutput',0)')
1
Jordan