はじめに
セリとセロリって名前は似ていますが、どういう関係か?つい疑問に思ってしまったのですが、ここはそれについて説明する記事ではないです。
MATLABを使っていたら「cell配列」という概念にぶつかるのですね。最初は「何それ美味しいの?」と思ってしまいました。
私は仙台でセリ鍋を食べたことがあるからこれは美味しそうに聞こえますよね。
全然関係ない話ですけど……。
基本的にMATLABで出番が多いのはただの「配列」(array)で、その配列の仕組みについて前回の記事で詳しく紹介しました。
しかしその他にも「cell配列」というものもよく使われています。ただの配列と似ていますが、違うところもあります。
それにこれは他の言語にはない概念でもあるので、他の言語の経験と知識から説明することはちょっと難しいです。Pythonのlist、tuple、setとも違います。
セロリが嫌で食べられない人もいるとよく聞いた話ですが、cell配列も最初はわけわからなくて嫌だという人もいるでしょうね。
でもちゃんと扱い方と必要性を理解したらこれもプログラミング言語として興味深いと感じます。
ということで、今回の記事では「cell配列」について解説します。
cell配列の基本
cell配列とは
簡単に言うとcell配列はMATLABにおける配列の一種です。
普通の配列と同じように色んなものが入っている配列ですが、普通の配列が同じデータ型のものから成されるのに対し、cell配列は色々違うものを同時に入れることができます。
これはPythonのlistやRuby、JavaScript、Dart、PHPなどの配列と似ていますね。ただし使い方はもっと複雑で、そう簡単にはいかないのです。
どう違うか以下説明していきます。
cell配列の作成
普通の配列は[ ]
で囲むことで作るのに対し、cell配列は{ }
で作るのです。
celery = {3 "4" '5'}
又は
celery = {3,"4",'5'}
celery =
1×3 の cell 配列
{[3]} {["4"]} {'5'}
こうやって違うものを入れてもそのまま問題ないのはcell配列の一番わかりやすい特徴です。
試しにこのような違うものを混ぜて普通の配列を作ってみたら勝手に同じものに変換させられますね。(変換できない場合はエラー)
ar = [3 "4" '5']
ar =
1×3 の string 配列
"3" "4" "5"
配列の中にcell配列を入れることもできます。これは「ネスト」と呼びます。普通の配列はネストできません。
{0,1,{2,3}}
{[0]} {[1]} {1×2 cell}
ネストできるだけでなく、普通の配列と同様に2次元又はまたともっと多い次元にできます。
{1 "2" {3};'4' [] [5 6]}
2×3 の cell 配列
{[1]} {["2" ]} {1×1 cell}
{'4'} {0×0 double} {[ 5 6]}
ただし、普通の配列みたいに数学の演算子など使うことができません。
{1}+{2}
'cell' タイプのオペランドに対して、演算子 '+' はサポートされていません。
PythonのりlistやRubyの配列などは+
で連結することができますが、MATLABのcell配列はそんなことはできませんね。
cell配列の要素を取る
cell配列も配列だから、後ろに数字が入った括弧( )
を書くことで要素にアクセスすることができます。ただし出てくるのはcell配列のままです。
celery = {5 6 7}
celery(1)
1×1 の cell 配列
{[5]}
1×1サイズになったとしてもまだcellという状態なので、そのまま使うことはできません。
cellは何か中身を包む宝箱で、解除しないと中身を取り出して扱うことができないですね。
cellの中の要素を取りたいなら( )
の代わりに、{ }
を使います。
celery{1}
5
数値の配列を入れて同時に複数の要素を取ることもできますが、それがバラバラに出てきてしまいます。
celery = {11 22 33 44 55};
celery{[1 3 4]}
ans =
11
ans =
33
ans =
44
そこで複数の配列を受け皿にすることができます。
celery = {111 222 333 444 555 666};
[s,e,r,i] = celery{2:5}
s =
222
e =
333
r =
444
i =
555
又は[ ]
で囲んで配列に纏めます。
ar = [celery{[1 3 4]}]
ar =
111 333 444
書き方はなんか複雑になってしまいますね。だからちゃんと理解しておかないとわけわからなくて悶々としてしまって困りますよね。
配列の中の全部のcellを解除する
{:}
をcell配列の後ろに付けると中身が全部はみ出ます。ただしそのままだとバラバラに出てしまうので、配列に纏めたいなら[ ]
で囲んでおきましょう。
celery = {12 34 56};
[celery{:}]
12 34 56
また、もし要素が配列だったらそこで連結されます。
celery = {[1;2] [3;4] [5;6]};
[celery{:}]
1 3 5
2 4 6
ただし形が合わないなどの原因で連結できない場合はエラーになるので注意です。
celery = {12 [3;4] 56};
[celery{:}]
次を使用中のエラー: horzcat
連結する配列の次元が一致しません。
このエラーメッセージを見ると、実際にここでhorzcat
関数が呼ばれるという仕組みになっていますね。この動作を理解していなければこれはわけわからないエラーメッセージだと思って挫折してしまうかもしれません。
解除されたcell配列の中身を関数の入力に使う
cellからはみ出るものは変数に収める他に、直接そのまま関数に入れて使うこともできます。
例えば3つ数値を入れる必要がある関数を作って、それをcell配列に入れておいて、使う時に{:}
ではみ出して関数に入力することができます。
function soukei(a,b,c)
fprintf("%d+%d+%d = %d\n",a,b,c,a+b+c)
end
celery = {1 2 5};
soukei(celery{:});
1+2+5 = 8
cell配列の要素を書き換える
普通の配列と同様に、cell配列の要素にアクセスできたら書き換えることができます。
celery = {1 2 3};
celery{2} = [4 5];
celery
celery =
1×3 の cell 配列
{[1]} {[4 5]} {[3]}
ただし( )
でアクセスしたらエラーになるので、{ }
を使わないとならないですね。
celery = {1 2 3};
celery(2) = 4;
double から cell に変換できません。
このエラーメッセージから見ると、代入しようとする時に勝手に変換しようとされますね。
ではもしその代わりにcellとして入れたら上手くいくのではないかと思ったら、本当にできてしまいます。
celery = {4 5 6};
celery(2) = {[7 8]};
celery
celery =
1×3 の cell 配列
{[4]} {[7 8]} {[6]}
cell配列に関わる関数
cell
cell
という関数を使ったら空っぽなcell配列を作成することもできます。
cell(4,2)
4×2 の cell 配列
{0×0 double} {0×0 double}
{0×0 double} {0×0 double}
{0×0 double} {0×0 double}
{0×0 double} {0×0 double}
ただし数値一つ入れたらそのサイズのとなります
cell(3)
3×3 の cell 配列
{0×0 double} {0×0 double} {0×0 double}
{0×0 double} {0×0 double} {0×0 double}
{0×0 double} {0×0 double} {0×0 double}
cell2mat
cell配列を解除して普通の配列にする時に{ }
を後ろに付ける他にcell2mat
を使うこともできます。これを使うと多次元のcell列をその形のまま普通の配列に変換できます。
celery = {3 4 5;7 8 9};
cell2mat(celery)
3 4 5
7 8 9
中の要素が配列だったら勝手に連結してしまいます。
celery = {[1;4] [2 3;5 6];7 [8 9]}
mat = cell2mat(celery)
celery =
2×2 の cell 配列
{2×1 double} {2×2 double}
{[ 7]} {[ 8 9]}
mat =
1 2 3
4 5 6
7 8 9
ただし連結できないような形だったらエラーになります。
celery = {[5;6],[7 8]}
mat = cell2mat(celery)
celery =
1×2 の cell 配列
{2×1 double} {[7 8]}
次を使用中のエラー: cat
連結する配列の次元が一致しません。
エラーメッセージから見ると、中でcat
関数が使われるという仕組みだとわかります。
mat2cell
mat2cell
関数はcell2mat
とは逆に、普通の配列からcell配列を作る関数です。
ただし使う時にそれぞれの軸でどのように配列を分割するか指定する必要があります。
mat = ones(5,4);
mat2cell(mat,[2 2 1],[3 1])
3×2 の cell 配列
{2×3 double} {2×1 double}
{2×3 double} {2×1 double}
{[ 1 1 1]} {[ 1]}
1次元の横方向の配列の分割にもこのように使えます。
mat = [2 2 1 3 3 3];
mat2cell(mat,1,[2 1 3])
1×3 の cell 配列
{[2 2]} {[1]} {[3 3 3]}
num2cell
mat2cell
と違って、分割せずに単に配列をそのままcell配列に書き換えるならnum2cell
を使います。
mat = reshape(1:12,3,4)
celery = num2cell(mat)
mat =
1 4 7 10
2 5 8 11
3 6 9 12
celery =
3×4 の cell 配列
{[1]} {[4]} {[7]} {[10]}
{[2]} {[5]} {[8]} {[11]}
{[3]} {[6]} {[9]} {[12]}
軸を指定たらその軸だけ纏めて一緒のcellに入れることになります。
mat = ones(4,3,2);
celery1 = num2cell(mat,1)
celery2 = num2cell(mat,2)
celery3 = num2cell(mat,3)
celery13 = num2cell(mat,[1 3])
1×3×2 の cell 配列
celery1(:,:,1) =
{4×1 double} {4×1 double} {4×1 double}
celery1(:,:,2) =
{4×1 double} {4×1 double} {4×1 double}
4×1×2 の cell 配列
celery2(:,:,1) =
{[1 1 1]}
{[1 1 1]}
{[1 1 1]}
{[1 1 1]}
celery2(:,:,2) =
{[1 1 1]}
{[1 1 1]}
{[1 1 1]}
{[1 1 1]}
celery3 =
4×3 の cell 配列
{1×1×2 double} {1×1×2 double} {1×1×2 double}
{1×1×2 double} {1×1×2 double} {1×1×2 double}
{1×1×2 double} {1×1×2 double} {1×1×2 double}
{1×1×2 double} {1×1×2 double} {1×1×2 double}
celery13 =
1×3 の cell 配列
{4×1×2 double} {4×1×2 double} {4×1×2 double}
celldisp
普段cell配列の中身が複数行の配列などだったらその配列の内容が表示されませんね。
celery = {0,1;ones(2),zeros(3)}
2×2 の cell 配列
{[ 0]} {[ 1]}
{2×2 double} {3×3 double}
そこでcelldisp
関数を使ったらcell配列の中身を詳しく表示することができます。
celldisp(celery)
celery{1,1} =
0
celery{2,1} =
1 1
1 1
celery{1,2} =
1
celery{2,2} =
0 0 0
0 0 0
0 0 0
cell配列が出る関数
直接作らなくても実はcell配列は色んな場面で出てきます。
例えばexcelやcsvファイルなどを読み込むために使うreadcell
関数です。
1,セル,cell
2,セリ,seri
3,セロリ,celery
readcell("celery.csv")
3×3 の cell 配列
{[1]} {'セル' } {'cell' }
{[2]} {'セリ' } {'seri' }
{[3]} {'セロリ'} {'celery'}
又はjsonを読み込むjsondecode
関数。
[
["セロリ","celery"],
[1,2],
{"セリ":"seri"}
]
jsondecode(fileread("celery.json"))
3×1 の cell 配列
{2×1 cell }
{2×1 double}
{1×1 struct}
それと、機械学習などで沢山な画像を読み込むのに使うimageDatastore
も、readall
メソッドを使ったらcell配列になります。これについて詳しくはこの記事で紹介しています。よろしければこれも読んでください。
このように色んな場面でcell配列が使われるので、どうやって対処するかわかっておくのも大事でしょう。
参考&もっと読む
- Matlab で JSON ファイルを扱う
- MATLABにファイルデータを読みこむ関数
- MATLABからファイルデータを書き出す関数
- MATLABの隠れたデータ型? コンマ区切りリスト
- ネスト化されたcellをアンネストする
終わりに
ここまで読んでセロリを食べたくなってきたでしょうか?大体cell配列についてわかってきたでしょうか。
他の言語と比べてちょっと風変わりな概念だからちょっとわかりにくいかもしれませんが、これを理解したら面白く感じます。