ネイティブ Python拡張のインポートノード：CSVファイルの読み込み

SPSS Modeler18.5の新機能の [ネイティブ Python API](https://www.ibm.com/docs/ja/spss-modeler/18.5.0?topic=spark-native-python-apis)のテストをしてみました。これは、「拡張ノード」のなかでpandasを使ってModelerのデータを読んだり書いたりすることができる機能です。

この記事ではネイティブ Python APIで「拡張のインポートノード」が、どのように利用可能かをテストしてみました。
マニュアルに[サンプル・ストリーム](https://www.ibm.com/docs/ja/SS3RA7_18.5.0/modeler_r_nodes_ddita/clementine/python-extension-demo.zip)がついていましたので、これを修正してテストしてみています。

サンプルストリームは以下です。

https://github.com/hkwd/231229NativePythonExtension/raw/master/231229NativePythonExtensionSample/python-extension-demo_hkwd.str

* テスト環境
    * Windows 11 64bit
    * Modeler 18.5
    * Python 3.10.7


# 拡張のインポート
## CSVファイルの読み込み
「Python」を選択し、「Python Syntax」の欄に以下のスクリプトを記入します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/b704bab9-2fe5-bccc-585a-0ff52757c90e.png)



```python:CSVファイルの読み込み
###Use this code to export data from Python to Modeler
# ネイティブ Python APIのパッケージ
import modelerpy

import pandas

#データモデルの参照時
if modelerpy.isComputeDataModelOnly():
    outputDataModel = None
    ###Compute output data model here
    ###出力データモデルの設定
    outputDataModel = modelerpy.DataModel([
            modelerpy.Field('Age', 'integer', 'continuous'),
            modelerpy.Field('Sex', 'string', 'flag'),
            modelerpy.Field('BP', 'string', 'nominal'),
            modelerpy.Field('Choles', 'string', 'nominal'),
            modelerpy.Field('Na', 'real', 'continuous'),
            modelerpy.Field('K', 'real', 'continuous'),
            modelerpy.Field('Drug', 'string', 'nominal'),
        ])
    modelerpy.setOutputDataModel(outputDataModel)
#データ出力時
else:
    outputData = None
    ###Compute output data here
    ###ファイルからデータの読み込み
    outputData = pandas.read_csv('C:/Program Files/IBM/SPSS/Modeler/18.5/Demos/DRUG1n')
    ###データの出力
    modelerpy.writePandasDataframe(outputData)
```
## コードの内容
大きくはデータモデルの処理とデータ自体の処理の二つに分かれています。
### 処理の分岐
「データモデルの処理」と「データ自体の処理」の分岐を以下のif文で行っています。
```python:
if modelerpy.isComputeDataModelOnly()
```
[modelerpy.isComputeDataModelOnly()](https://www.ibm.com/docs/ja/spss-modeler/18.5.0?topic=spark-native-python-apis#extension_nativepython_api__title__2):のif文は、後続のノードがデータモデルを参照する場合に実データを用意しなくていいための分岐になります。Modelerは、後続のノードを接続した際に前のノードのデータモデルを参照します。例えば「フィルター」ノードを接続した場合にはデータは必要ありませんが、「列名」の情報は必要になります。このif文をなくしても動作はしますが、列名などのデータモデルを参照したいだけなのにデータの読み込みが行われてしまうため、無駄な処理が動いてしまいます。
なお、このif文は、「拡張の変換」ノードと「拡張モデル」ノードといった後続ノードが必要なノードでも、同様に使われます。

### データモデルの処理

[modelerpy.DataModel](https://www.ibm.com/docs/ja/spss-modeler/18.5.0?topic=spark-native-python-apis#extension_nativepython_api__title__6)の配列に[modelerpy.Field](https://www.ibm.com/docs/ja/spss-modeler/18.5.0?topic=spark-native-python-apis#extension_nativepython_api__title__6)で各列のデータモデルを定義しています。「フィールド名」、「データ型」、「尺度」を指定しています。これは後続のノードで利用されます。

```python:
outputDataModel = modelerpy.DataModel([
            modelerpy.Field('Age', 'integer', 'continuous'),
            modelerpy.Field('Sex', 'string', 'flag'),
            modelerpy.Field('BP', 'string', 'nominal'),
            modelerpy.Field('Choles', 'string', 'nominal'),
            modelerpy.Field('Na', 'real', 'continuous'),
            modelerpy.Field('K', 'real', 'continuous'),
            modelerpy.Field('Drug', 'string', 'nominal'),
        ])
```
「データ型」タブで確認してみると、以下の部分の定義になっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/434543aa-6a77-77c1-7904-dfdca53e8802.png)


### データの処理
以下でCSVファイルからデータをpandas DataFrameに読み込んでいます。
```python:
pandas.read_csv('C:/Program Files/IBM/SPSS/Modeler/18.5/Demos/DRUG1n')
```

以下の[modelerpy.writePandasDataframe(outputData)](https://www.ibm.com/docs/ja/spss-modeler/18.5.0?topic=spark-native-python-apis#extension_nativepython_api__title__4)で後続のノードにデータを出力しています。

```python:
modelerpy.writePandasDataframe(outputData)
```
### 結果
以下のように、データが読み込めました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/747d1ac9-800f-064b-5a07-0def5e026bcc.png)

# 参考

#### SPSS Modeler18.5の新機能の ネイティブ Python API #SPSS - Qiita 
https://qiita.com/kawada2017/items/0ae6f0974788b372dc4f
#### ネイティブ Python拡張のインポートノード：CSVファイルの読み込み 
https://qiita.com/kawada2017/items/72a15c121d196b7f5b0a
#### ネイティブ Python拡張の変換ノード：置き換え 
https://qiita.com/kawada2017/items/5c58017404ddd08a60eb
#### ネイティブ Python拡張モデルノード：モデル作成OneClassSVM 
https://qiita.com/kawada2017/items/965b5bd6fff90d2cfc02
#### ネイティブ Python拡張の出力ノード：データとペアプロット図（散布図行列）の表示 
https://qiita.com/kawada2017/items/be3fddcdf7ed11d80b4c
#### ネイティブ Python拡張のエクスポートノード：CSVファイルの出力 
https://qiita.com/kawada2017/items/7a4a00c8a0f77efb6f79
#### ネイティブpythonの拡張ノードでつかうpyhtonパッケージを導入する 
https://qiita.com/kawada2017/items/9252f93cf59948dd2810