MATLAB - Python 連携: DataFrame 変数を Table 変数に変換

# 直接 *DataFrame* を *Table* 変数に変換したい
近年、MATLAB のヴァージョンが上がる毎に Python との連携が強化されつつあります。
従来は、*Parquet* や *csv* ファイルに保存してから再度読み込む方法などが推奨されていましたが、ファイルに保存なんて野暮ったい感じもするので、直接受け渡しができる方法をご紹介します。
*DataFrame* を *Table* に変換して、MATLAB のデータ解析環境で直ぐに使う方法を備忘録的に記しておきます。

:::note alert
注意
この記事は、MATLAB から Python を呼び出す場合を想定しています。
Python から MATLAB を呼び出す方法は、[Call MATLAB Functions from Python](https://www.mathworks.com/help/matlab/matlab_external/call-matlab-functions-from-python.html) などを参照してください。
:::

# 0. 基本設定の確認
利用する MATLAB ヴァージョンを確認しておきます。
```matlab:MATLAB Code
version
```
```text:Output
ans = '9.13.0.2049777 (R2022b)'
```
R2022b で実行します。

:::note warn
R2022b 以前のバージョンでエラーが出たら
これより古いヴァージョンの方は、Python から MATLAB に戻ってくる変数を *cell* 型で受けたりすることで、エラーを解消できると思います。例えば、下でご紹介する:
```diff_matlab:MATLAB Code
- columnNames = string(pyDataFrame.columns.tolist()) % R2019b ではエラー発生
+ columnNames = string(cell(pyDataFrame.columns.tolist())) % cell を使って解決
```
などがあります。
:::


Python 連携する際は、最初にマシンにインストールされている MATLAB と連携させる Python の情報を確認します:

```matlab:MATLAB Code
pyenv
```

```text:Output
ans = 
  PythonEnvironment のプロパティ:

          Version: "3.9"
       Executable: "C:\Users\myName\AppData\Local\WPy64-39100\python-3.9.10.amd64\python.exe"
          Library: "C:\Users\myName\AppData\Local\WPy64-39100\python-3.9.10.amd64\python39.dll"
             Home: "C:\Users\myName\AppData\Local\WPy64-39100\python-3.9.10.amd64"
           Status: NotLoaded
    ExecutionMode: InProcess
```

:::note warn
注意
*myName* の部分は私のユーザー名元々入っています
:::


# 1. サンプルデータ作成

*numpy array* を作成します

```matlab:MATLAB Code
pynumpy = py.numpy.array([1,2,3;4,5,6;7,8,9])
```

```text:Output
pynumpy = 
  Python ndarray:

     1     2     3
     4     5     6
     7     8     9
    Python オブジェクトのプロパティを表示するには、関数 details を使用します。

    MATLAB 配列に変換するには、関数 double を使用します。
```


:::note info
Python と比較
numpy.array は Python だと行列は [ ] で囲みますが、MATLAB では ";" で改行できます
```python:Python Code
pynumpy = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
```
:::

コラム名を指定し、先ほど作成した ```pynumpy``` を渡して、*DataFrame* を作成します。

```matlab:MATLAB Code
pyDataFrame = py.pandas.DataFrame(pynumpy,columns={'a','b','c'})
```

```text:Output
pyDataFrame = 
  Python DataFrame とそのプロパティ:

          T: [1x1 py.pandas.core.frame.DataFrame]
         at: [1x1 py.pandas.core.indexing._AtIndexer]
      attrs: [1x1 py.dict]
       axes: [1x2 py.list]
    columns: [1x1 py.pandas.core.indexes.base.Index]
     dtypes: [1x1 py.pandas.core.series.Series]
      empty: 0
      flags: [1x1 py.pandas.core.flags.Flags]
        iat: [1x1 py.pandas.core.indexing._iAtIndexer]
       iloc: [1x1 py.pandas.core.indexing._iLocIndexer]
      index: [1x1 py.pandas.core.indexes.range.RangeIndex]
        loc: [1x1 py.pandas.core.indexing._LocIndexer]
       ndim: [1x1 py.int]
      shape: [1x2 py.tuple]
       size: [1x1 py.numpy.int32]
     values: [1x1 py.numpy.ndarray]
         a    b    c
    0  1.0  2.0  3.0
    1  4.0  5.0  6.0
    2  7.0  8.0  9.0
```

:::note info
Python と比較
Python だと以下のように *DataFrame* 作成時に *columns* に *list* 型 ```['a','b','c']``` を渡します。
MATLAB だと ```{'a','b','c'}``` の *cell* 型で渡しています。
```python:Python Code
df = pd.DataFrame(np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]),columns=['a', 'b', 'c'])
```
:::


# 2. MATLAB *Table* 型に *DataFrame* を変換
3 ステップで変換できます。

## Step 1: *DataFrame* Value を MATLAB *array* にします

```matlab:MATLAB Code
value = double(pyDataFrame.values)
```
```text:Output
value = 3x3    
     1     2     3
     4     5     6
     7     8     9

```

:::note info
原理
```pyDataFrame.values``` の中身は numpy *ndarray*。
MATLAB の数値配列型に *double* 関数で一発で変換できます。
```matlab:MATLAB Code
pyDataFrame.values
```

```text:Output
ans = 
  Python ndarray:

     1     2     3
     4     5     6
     7     8     9
    Python オブジェクトのプロパティを表示するには、関数 details を使用します。

    MATLAB 配列に変換するには、関数 double を使用します。
```
:::

Python - MATLAB 間のデータ型のマッピングは
[MATLAB to Python Data Type Mapping](https://www.mathworks.com/help/matlab/matlab_external/passing-data-to-python.html)
にまとまっています。


## Step 2: *DataFrame* column 名を取り出します
1.   column 名 を Python *list* 型に変換
1.   *list* 型変数を、MATLAB *string* に変換

```matlab:MATLAB Code
columnNames = string(pyDataFrame.columns.tolist())
```

```text:Output
columnNames = 1x3 string    
"a"          "b"          "c"          

```

## Step 3: *array2table* で MATLAB *Talbe* 型を作成

```matlab:MATLAB Code
tbl = array2table(value,VariableNames=columnNames)
```
output:

| |a|b|c|
|:--:|:--:|:--:|:--:|
|1|1|2|3|
|2|4|5|6|
|3|7|8|9|

# まとめ
*DataFrame* は Python のデータ解析では必須のデータ型です。一方、MATLAB のそれは、*Table* や *Timetable* 型ですが、Python 連携を考えるとこの2つの受け渡しはスムースにしたいところ。Parquet や csv ファイルでのやり取りも MathWorks から推奨もされていますが、ファイルに吐き出さずに優雅に実現する方法を今回はご紹介しました。