MATLABからファイルデータを書き出す関数

## はじめに
　昨年の今日、[『MATLABにファイルデータを読み込む関数』](https://qiita.com/Monzo_N/items/ddcb5d80bed3a3ceb66c)を投稿したわけですが、１年後、ついに本記事『MATLABからファイルデータを書き出す関数』を投稿することになり、まさに胸熱の展開であります。
　本記事は、古くからある（そして非推奨となってしまった）書き出し関数、代わりにお勧めされている書き出し関数についてまとめてみました。

## １．古くからある関数
### 1-1 save
　saveは古くからある関数ですが、この先もなくなることはないと思われます。昨年も書きましたが、データが数値のみで、asciiコードで書かれているならば、saveが使えます。
　matlabを使っていて、作業を中断したいが、変数などは取っておきたい場合には、

```matlab
save 
```

をやると、今ある変数が取りこまれたmatlab.matという構造体ファイルが生成されます。
復活させたいときは、

```matlab
load
```
をやると、matlab.matに取りこまれていた変数が戻ってきます。これさえ知っていればMATLABを終了しても、PCをシャットダウンしても、明日、また続きができるという美味しさがあります。

また、計算途中でテキストデータとして数値データxxをファイルに落としたい場合は、コマンドウインドウで、

```matlab
save abc.dat xx -ascii;
```
とやると、abc.datのテキストファイルが生成され、メモ帳などでも開くことができます。
（abc.datは、abc.txtでもＯＫです）
-ascii のオプションをつけるのを忘れると、バイナリーデータで保存されてしまい、テキストとして開けなくなるので注意しましょう。

スクリプトの中で、saveコマンドを使うときは、

```matlab
p = rand(1,10);
save('pqfile.txt','p','-ascii')
```

こうなるのですが、大事なことは変数ｐは、'p'とクォーテーションをつけないとエラーになることです。変数なのに？と思うのですが、そういうことになっているようです。
（おそらく数値変数ではダメで、文字変数でないといけないのだと思われます）

ちなみに、数値変数 --> 文字変数の変換手段があり、それを使うと、

```matlab
a=1:10;
vname=getname(a);
fname='adata.txt';
save(fname,vname,'-ascii');
```
と、なります。このgetname関数を使うことで、数値変数aが文字変数aとして変数vnameに入力されます。ですが、あまり楽しい感じがしないのはなぜでしょうか。ちなみにファイル名（'adata.txt'）は、この例のように指定することが可能です。
ファイル名を次々と変えて、複数のファイル保存することがあるけれど、変数名を次々と変えることは、あまりないからでしょうか。

付け足しですが、スプリプトの中で（カッコ）なしのsaveを書いた場合です。
```matlab
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ではｍ×ｎ行列が書き出しできるところが勝っています。
フォーマットに関しては、save；浮動小数点、csvwrite：５桁までの有効数字、となっており、変更は効きません。融通のなさが古い関数という感じでしょうか。

```matlab
csvwrite('testsignalA.csv',A); % Aは書き出す変数
```

変数のヘッダーに文字をつける、ということもできません。扱えるのは数値のみです。

エクセルのセルを意識した書き方もできます。
```matlab
A = csvwrite('testsignalA.csv',A,1,0);% 2行目１列目から書き込み
```
エクセルのセルA1は、0,0となります。

### 1-3 dlmwrite（非推奨）
　dlmwriteは、csvwriteと比較してデリミタがカンマだけでなく、スペース、タブなどを使えること、数値の精度（桁数）を指定できる点がcsvwriteよりも優れています（でも非推奨）。

```matlab
%dlmreadによるCSVファイル読み込み
dlmwrite('atest.txt',M,'delimiter','\t','precision',5,1,2); %行列Mを、
% ファイルatest.txtの2行目3列目から書き込み、精度は５桁、デリミタはタブ

```


### 1-4 xlswrite（非推奨）
　古くからの書き出し関数でスプレッドシートに書けるのが、xlswriteです。
　excel拡張子のxls, xlsx, xlsm, xlsbなどに書き込めます。エクセルへの書き込みにおけるニーズは、１）ファイル名の指定、２）シート名の指定、３）セル位置の指定、４）文字、数値の混在の４つだと思いますが、xlswriteは、この全てが可能です。
（ただし、文字・数値の混在はcell配列を使う必要があります）

```matlab
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配列を用意して、書き込みをする必要があります。

```matlab
% データ名ありの配列を作成して保存する
C{11,3}=[];
% Cという名の空のcell配列を用意する

C(1,:)={'Case1','Case2','Case3'};
% データ名CaseXをヘッダにつける

a=1:10;% １列目のデータ
a=a';% 向きを縦に変える

C(2:11,1)=num2cell(a); 
% セルに入力。num2cellが重要。無いと各セルにaが丸ごと入ってしまう

xlswrite('xyz.xlsx',C); 
% ファイル'xyz.xlsx'にCを書き込む
```
こんな感じです。
ちなみにエクセルで良く行われる手法に（エクセルの）セルの結合がありますが、既にやり方が書かれていました。
[excelのセル結合(やらないほうがいい！)](https://twitter.com/michio_mwj/status/1452834527076704256?s=21&t=yk75yt3NBvOtEm-c4sVN7Q)
（私は、これをMATLAB Answersの[Hernia Babyさんの回答](https://jp.mathworks.com/matlabcentral/answers/1730400-)で知りました）。


## ２．新しい世代の読み込み関数

### 2-1 writecell
cell配列をファイルに書き込むものです。
ここまで読まれた方は、cell配列の作成はバッチリですね。

```matlab
% 配列Cは、既につくられているとします。
writecell(C);% C.txtというコンマ区切りのテキストファイルを生成します。

writecell(C,'xyz2.xlsx'); %スプレッドシートへの書き込み
% これは先ほどの xlswrite('xyz.xlsx',C) と同じ機能です。エクセルのセル幅が
% 自動で調整されます。
% 注意点は、writecellでは１番目の引数が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といった風情です。

```matlab
A=rand(5);% 5x5配列
writematrix(A,'Adata.xlsx','Sheet','sheet2','Range','A2:E7');
% ファイル名Adata.xlsxのシートsheet2の領域A2:E7に配列Aを書き込む
```
ここで注意すべきは、既に存在するシート名を指定するとエラーになることです。


### 2-3 writetable
tableの説明をどうしようかと悩んでいたところ、[本アドベントカレンダー10日目](https://tagebuchvonspargel.hatenablog.com/entry/2022/12/10/003007?_ga=2.92141400.377847550.1670586176-1081187512.1668271819)に丁寧な解説が登場しましたので、そちらを参考にしていただくということで。
ちなみに、私がtableで面白いと思ったところは次の点です。

```matlab
% テーブル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)を追加する場合には、

```matlab
patients.BMI = (patients.Weight*0.453592)./(patients.Height*0.0254).^2
```
これで、下記のようにBMIの値が計算され、追加されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434947/60c0ba29-6c37-4c83-cf0f-d69839d9d8e2.png)
「表計算ソフト」という感じですね。"昇順"、"降順"にも対応しています。

もう１つ、メタデータを用いたテーブル内のデータへのアクセスができる点が優れています。例えば、Sanchezさんの身長（Height）を求めることができます。
ですが、上記の状態で、

```matlab
patients(["Sanchez"],["Height"]) % NGの例
% この状態では、　Rownameのプロパティがないのでエラーが出ます。

% 解決するには、
patients.Properties.RowNames = LastName;% LastNameをRowNameに指定する
patients = removevars(patients, 'LastName');% LastName列を削除する

patients(["Sanchez"],["Height"])% これで出力されます
patients("Sanchez","Height")% 項目が１つなら、[]は不要です
```

さて、それでは本題のテーブルをwritetableで書き出す方法です。

```matlab
% テーブルTは既にあるものとします。
writetable(T);% テーブルTをカンマ区切りのテキストファイルに書き出す。
% ファイル名はT.txtとなる

writetable(T,filename);% テーブルTをファイル名filenameに書き出す。
% ファイル名には拡張子をつける。,txt, .dat, .csv, .xls他のエクセル系の
% 拡張子のほか.xmlが使える

```
文字データと数値データ混在のデータを扱う人にとっては、かなり使えるのではないかと思います。


## ３．終わりに
以上、MATLABから外部ファイルに書き出す場合の関数と用例をまとめてみました。半分は自分の備忘録になってしまっております。
間違いとか、「こんな使い方もある」がありましたら、お知らせいただけると幸いです。