こんにちは。
整形外科医師ブロガーのボククボです。
今回も自分が
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にしてくれんかな。。)
タイピングが面倒なら
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で教えてください。