はじめに
昨年の今日、『MATLABにファイルデータを読み込む関数』を投稿したわけですが、1年後、ついに本記事『MATLABからファイルデータを書き出す関数』を投稿することになり、まさに胸熱の展開であります。
本記事は、古くからある(そして非推奨となってしまった)書き出し関数、代わりにお勧めされている書き出し関数についてまとめてみました。
1.古くからある関数
1-1 save
saveは古くからある関数ですが、この先もなくなることはないと思われます。昨年も書きましたが、データが数値のみで、asciiコードで書かれているならば、saveが使えます。
matlabを使っていて、作業を中断したいが、変数などは取っておきたい場合には、
save
をやると、今ある変数が取りこまれたmatlab.matという構造体ファイルが生成されます。
復活させたいときは、
load
をやると、matlab.matに取りこまれていた変数が戻ってきます。これさえ知っていればMATLABを終了しても、PCをシャットダウンしても、明日、また続きができるという美味しさがあります。
また、計算途中でテキストデータとして数値データxxをファイルに落としたい場合は、コマンドウインドウで、
save abc.dat xx -ascii;
とやると、abc.datのテキストファイルが生成され、メモ帳などでも開くことができます。
(abc.datは、abc.txtでもOKです)
-ascii のオプションをつけるのを忘れると、バイナリーデータで保存されてしまい、テキストとして開けなくなるので注意しましょう。
スクリプトの中で、saveコマンドを使うときは、
p = rand(1,10);
save('pqfile.txt','p','-ascii')
こうなるのですが、大事なことは変数pは、'p'とクォーテーションをつけないとエラーになることです。変数なのに?と思うのですが、そういうことになっているようです。
(おそらく数値変数ではダメで、文字変数でないといけないのだと思われます)
ちなみに、数値変数 --> 文字変数の変換手段があり、それを使うと、
a=1:10;
vname=getname(a);
fname='adata.txt';
save(fname,vname,'-ascii');
と、なります。このgetname関数を使うことで、数値変数aが文字変数aとして変数vnameに入力されます。ですが、あまり楽しい感じがしないのはなぜでしょうか。ちなみにファイル名('adata.txt')は、この例のように指定することが可能です。
ファイル名を次々と変えて、複数のファイル保存することがあるけれど、変数名を次々と変えることは、あまりないからでしょうか。
付け足しですが、スプリプトの中で(カッコ)なしのsaveを書いた場合です。
a=rand(1,10);
save 'a2data.dat' a -ascii;% これはOK
fname='a2data.dat';
save fname a -ascii;% NG。ファイル名がfnameになってしまう。
となります。
1-2 csvwrite(非推奨)
カンマ区切りのcsvファイルを書き出すのに使う関数です(txtファイルも可)。よく使われます。なんといってもcsvファイルはエクセルで開けるのが大きいと思います。
前述のsaveでは一列分のデータしか書き出しができないところ、csvwriteではm×n行列が書き出しできるところが勝っています。
フォーマットに関しては、save;浮動小数点、csvwrite:5桁までの有効数字、となっており、変更は効きません。融通のなさが古い関数という感じでしょうか。
csvwrite('testsignalA.csv',A); % Aは書き出す変数
変数のヘッダーに文字をつける、ということもできません。扱えるのは数値のみです。
エクセルのセルを意識した書き方もできます。
A = csvwrite('testsignalA.csv',A,1,0);% 2行目1列目から書き込み
エクセルのセルA1は、0,0となります。
1-3 dlmwrite(非推奨)
dlmwriteは、csvwriteと比較してデリミタがカンマだけでなく、スペース、タブなどを使えること、数値の精度(桁数)を指定できる点がcsvwriteよりも優れています(でも非推奨)。
%dlmreadによるCSVファイル読み込み
dlmwrite('atest.txt',M,'delimiter','\t','precision',5,1,2); %行列Mを、
% ファイルatest.txtの2行目3列目から書き込み、精度は5桁、デリミタはタブ
1-4 xlswrite(非推奨)
古くからの書き出し関数でスプレッドシートに書けるのが、xlswriteです。
excel拡張子のxls, xlsx, xlsm, xlsbなどに書き込めます。エクセルへの書き込みにおけるニーズは、1)ファイル名の指定、2)シート名の指定、3)セル位置の指定、4)文字、数値の混在の4つだと思いますが、xlswriteは、この全てが可能です。
(ただし、文字・数値の混在はcell配列を使う必要があります)
xlswrite('abc.xlsx',A);
% 行列Aをファイル名abc.xlsxに書き込む
% 書き込む場所はセルA1から
xlswrite('abc.xlsx',A,'sheetA')
% 行列Aをファイル名abc.xlsxのシートsheetAに書く
% sheetAがないと、sheetAを作って書き込む(警告が出るがエラーではない)
A=ones(10,3);
xlswrite('abc.xlsx',A,'A2:C11');
% 行列Aをファイル名abc.xlsxの'A2:C11'に書く
% ちなみに、領域を小さく設定した場合('A2:C10'のように)は、
% 領域からはみ出した部分が書き込まれません。
% 逆に領域を大きく設定した場合('A2:C12'のように)は、
% 書き込むデータがないところは、#N/Aとなります。
% 書き込む配列の大きさに自信がないとき(!)は、書き込み領域の
% 左上の位置のみを指定することができます。ただし、この場合は、
% シート名の指定が必須です。
xlswrite('abc.xlsx',A,'Sheet1','A2');
xlswrite('abc.xlsx',A,'A2');% NG例
% これをやるとA2というシートができてしまいます。
さて、データ列のタイトルをつけて、エクセルに書き込む場合です。
前述のとおり、cell配列を用意して、書き込みをする必要があります。
% データ名ありの配列を作成して保存する
C{11,3}=[];
% Cという名の空のcell配列を用意する
C(1,:)={'Case1','Case2','Case3'};
% データ名CaseXをヘッダにつける
a=1:10;% 1列目のデータ
a=a';% 向きを縦に変える
C(2:11,1)=num2cell(a);
% セルに入力。num2cellが重要。無いと各セルにaが丸ごと入ってしまう
xlswrite('xyz.xlsx',C);
% ファイル'xyz.xlsx'にCを書き込む
こんな感じです。
ちなみにエクセルで良く行われる手法に(エクセルの)セルの結合がありますが、既にやり方が書かれていました。
excelのセル結合(やらないほうがいい!)
(私は、これをMATLAB AnswersのHernia Babyさんの回答で知りました)。
2.新しい世代の読み込み関数
2-1 writecell
cell配列をファイルに書き込むものです。
ここまで読まれた方は、cell配列の作成はバッチリですね。
% 配列Cは、既につくられているとします。
writecell(C);% C.txtというコンマ区切りのテキストファイルを生成します。
writecell(C,'xyz2.xlsx'); %スプレッドシートへの書き込み
% これは先ほどの xlswrite('xyz.xlsx',C) と同じ機能です。エクセルのセル幅が
% 自動で調整されます。
% 注意点は、writecellでは1番目の引数がcell配列となることです。
writecell(C,'xyz3.xlsx','Sheet','Sheet1','Range','A2:C11');% フル装備
% シート名、書き込みするセル位置を指定する場合です。
writecell(C,'xyz3.xlsx','Sheet','Sheet1','Range','A2:C11');% NG例
% 'Sheet','シート名','Range','書き込み領域' ときちんと記述する必要があります。
2-2 writematrix
同種配列のみをカンマ区切りで書き込みます。cell配列が扱えないxlswriteといった風情です。
A=rand(5);% 5x5配列
writematrix(A,'Adata.xlsx','Sheet','sheet2','Range','A2:E7');
% ファイル名Adata.xlsxのシートsheet2の領域A2:E7に配列Aを書き込む
ここで注意すべきは、既に存在するシート名を指定するとエラーになることです。
2-3 writetable
tableの説明をどうしようかと悩んでいたところ、本アドベントカレンダー10日目に丁寧な解説が登場しましたので、そちらを参考にしていただくということで。
ちなみに、私がtableで面白いと思ったところは次の点です。
% テーブルpatientsのためのデータを用意する。
LastName = ["Sanchez";"Johnson";"Zhang";"Diaz";"Brown"];
Age = [38;43;38;40;49];
Smoker = [true;false;true;false;true];
Height = [71;69;64;67;64];
Weight = [176;163;131;133;119];
BloodPressure = [124 93; 109 77; 125 83; 117 75; 122 80];
% テーブルpatientsを作成する
patients = table(LastName,Age,Smoker,Height,Weight,BloodPressure)
patients =
5×6 table
LastName Age Smoker Height Weight BloodPressure
_________ ___ ______ ______ ______ _____________
"Sanchez" 38 true 71 176 124 93
"Johnson" 43 false 69 163 109 77
"Zhang" 38 true 64 131 125 83
"Diaz" 40 false 67 133 117 75
"井上" 49 true 64 119 122 80
この状態において、更にtableの数値から計算した値を追加できることです。
MathWorksのページにもあるように、BMI(Body Mass Index)を追加する場合には、
patients.BMI = (patients.Weight*0.453592)./(patients.Height*0.0254).^2
これで、下記のようにBMIの値が計算され、追加されます。
「表計算ソフト」という感じですね。"昇順"、"降順"にも対応しています。
もう1つ、メタデータを用いたテーブル内のデータへのアクセスができる点が優れています。例えば、Sanchezさんの身長(Height)を求めることができます。
ですが、上記の状態で、
patients(["Sanchez"],["Height"]) % NGの例
% この状態では、 Rownameのプロパティがないのでエラーが出ます。
% 解決するには、
patients.Properties.RowNames = LastName;% LastNameをRowNameに指定する
patients = removevars(patients, 'LastName');% LastName列を削除する
patients(["Sanchez"],["Height"])% これで出力されます
patients("Sanchez","Height")% 項目が1つなら、[]は不要です
さて、それでは本題のテーブルをwritetableで書き出す方法です。
% テーブルTは既にあるものとします。
writetable(T);% テーブルTをカンマ区切りのテキストファイルに書き出す。
% ファイル名はT.txtとなる
writetable(T,filename);% テーブルTをファイル名filenameに書き出す。
% ファイル名には拡張子をつける。,txt, .dat, .csv, .xls他のエクセル系の
% 拡張子のほか.xmlが使える
文字データと数値データ混在のデータを扱う人にとっては、かなり使えるのではないかと思います。
3.終わりに
以上、MATLABから外部ファイルに書き出す場合の関数と用例をまとめてみました。半分は自分の備忘録になってしまっております。
間違いとか、「こんな使い方もある」がありましたら、お知らせいただけると幸いです。