プログラミング

【MATLAB】cellfunに、引数が複数ある関数を適用する方法

更新日:

こんにちは。

整形外科医師ブロガーのボククボです。

今回も自分が
MATLAB始めたばかりのときに困った事です。

MATLABのcellfunやstructfunって

全部のcellやstructに関数を適用できて便利ですよね。

meanとかstdだとか単純な関数をオプションなしで
適用するのはいいんですが、

「引数二つあるときって使えないじゃん!」

ってことに気付きます。

MATLAB Answersで聞けば教えてくれるでしょうが、
英語非対応な方もいると思いますので、

なるべく詳しく日本語で方法を解説します。

引数が複数必要な例

簡単な例をあげておきます。

10×1のcell配列Cがあり
各セルに100×1のdouble配列が格納されているとします。

ためしに適当に作ってみると。

%例示用のデータを作る
C=cell(10,1);

for i=1:length(C)
C{i}=rand(100,1);
end

さて、

各cellのdouble配列で、2番目に大きい値をそれぞれ調べたい】

ときはどうしましょうか?

matlabでは
"maxk"という関数を使います。

maxk(A,k)は
配列Aのk番目に大きい値を返してくれます。

これを各cellに適用するわけですが

%エラーになるやつ
second_max=cellfun(@maxk,C,2);

と書いても

”入力♯3にはcellが想定されていますがdoubleになっています”

とエラーがでます。(そりゃそうなんですが)

「じゃあ2番目を意味する"2"はどこに書けばいいんだ。。。」

となるわけです。

方法1.cell配列にしちゃう

ちょっとイモい方法ですが、

理解しやすい方法だと思います。

cellfunに適用する関数funcが二つ以上の引数

を取る場合、

cellfun(@func,C1,C2,....,Cn);

は実行可能です。

C1,C2,,,,,,Cnが同じ大きさのcellであれば、
いくつでも入力引数を指定できるということです。

これを利用します。

つまり’2’の部分もcell配列にします。

%% それぞれに2が格納された10×1のcell配列Kを作る
%宣言
K=cell(10,1);
%Kのすべてのcellに2を代入
K(:)={2}


%% cellfunを適用
second_max=cellfun(@maxk,C,K,'UniformOutput',false);

これでうまくできます。

ちなみに、'UniformOutput',falseは、
「返り値がcell配列になりますよ。」という意味です。

返り値がスカラーの時だけ、入力不要です。
(既定値がtrueのため。既定値falseにしてくれんかな。。)

MATLAB cellfun

タイピングが面倒なら
second_max=cellfun(@maxk,C,K);
を実行すると

もしかして:
second_max=cellfun(@maxk,C,K,'UniformOutput',false);?
と帰ってくるので、コピペしてもよいでしょう。

方法2. 無名関数を使う

こちらのほうが、ふつうのやり方だと思います。

無名関数とは

ざっくりいうと

「一時的にちょろっと使うだけの
名前つけるほどでない自作関数」

です。

通常自作関数は、
function を宣言して、関数名をつけて、
プログラムファイルとして保存。

っていう手続きをしますが、

無名関数はこれを省略するので
名前を付けられません。
(無名関数たるゆえんです。)

%変数=@(x) 処理内容
sqr = @(x) x.^2;

そのかわり、無名関数は変数に格納できます。
変数に格納しているので
他のスクリプトへの汎用性はありません。
(変数としてワークスペースにあれば別ですが。)

cellfunでの利用法

さて、本題です。

まずは、2番目以降の引数を指定済みの、
変数が1個の無名関数を作ります。

%変数=@(x) 処理内容
noNamefunc=@(x) maxk(x,2);

これで、変数が1個の無名関数ができました。
名前はありませんが、noNamefuncという変数に格納されています。

変数が1種類なので、普通にcellfunを適用できます。

%cellfunを実行
second_max=cellfun(noNamefunc,C,'UniformOutput',false);

慣れてくれば、いちいち変数に格納しなくても

second_max=cellfun(@(x) maxk(x,2),C,'UniformOutput',false);

と書けばよいでしょう。

@の使い方

【cellfun(@noNamefunc,C)でなくてよいのか?】

という疑問を持たれるかもしれません。

"@"は関数ハンドル(という種類の変数)
を生成する記号です。

上の例でいえば、noNamefuncは関数ハンドルです。

つまり、

関数ハンドル=@関数名

という関係です。

そしてcellfunは、

cellfun(関数ハンドル,cell配列)という形をとりますので

cellfun(noNamefunc,C)でよいわけですね。

まとめ

2番目以降の引数が、定数だったり、
同じName,Valueだったりすると

方法1はダサいかもしれません。
方法2を利用しましょう。

逆に、2番目以降の引数が、cellによって変化する場合は、
方法1しか無理ですね。

ボククボは長いこと
無名関数の存在をしらなかったので

方法2を知るまでかなり時間がかかりましたし
方法2の意味もなかなか理解できませんでした。

同じような状況で困っている方の役に立てば幸いです。

間違いなどあれば、コメントかtwitterで教えてください。

-プログラミング
-, , , , ,

Copyright© ボククボのブログ , 2020 All Rights Reserved Powered by STINGER.