ネイティブ Python拡張の変換ノード：置き換え

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

# 拡張の変換
## データの置換
「Python」を選択し、「Python Syntax」の欄に以下のスクリプトを記入します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/159d9f69-d6ce-16b5-a72c-f6b981e0c180.png)


```python:# バージョンなどの確認
#データの置換
###Use this code to transfer data between Modeler and Python
# ネイティブ Python APIのパッケージ
import modelerpy

#データモデルの参照時
if modelerpy.isComputeDataModelOnly():
    modelerDataModel = modelerpy.getDataModel()
    outputDataModel = None
    #Compute output data model here
    #出力データモデルの設定
    outputDataModel = modelerDataModel
    modelerpy.setOutputDataModel(outputDataModel)

#データ出力時
else:
    #前のノードからデータの読み込み
    modelerData = modelerpy.readPandasDataframe()
    outputData = modelerData
    #Compute output data here
    #Ageに100を加算
    outputData['Age']=outputData['Age']+100
    #データの出力
    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.getDataModel()](https://www.ibm.com/docs/ja/spss-modeler/18.5.0?topic=spark-native-python-apis#extension_nativepython_api__title__2)で前のノードから「フィールド名」、「データ型」、「尺度」などのデータモデルを取得しています。
```python:
modelerDataModel = modelerpy.getDataModel()
```
以下の[modelerpy.setOutputDataModel(outputDataModel)](https://www.ibm.com/docs/ja/spss-modeler/18.5.0?topic=spark-native-python-apis#extension_nativepython_api__title__2)で後続ノードに渡すデータモデルを設定しています。
```python:
modelerpy.setOutputDataModel(outputDataModel)
```
ここでの処理は既存データの置換なので、前のノードのデータモデルをそのままコピーしています。列が追加されたり、内容が変更される場合は「拡張のインポート」でやったような列の定義が必要です。

### データの処理
[modelerpy.readPandasDataframe()](https://www.ibm.com/docs/ja/spss-modeler/18.5.0?topic=spark-native-python-apis#extension_nativepython_api__title__4)で前ノードからのデータを読み込んでいます。
outputData['Age']=outputData['Age']+100でAgeに100を足しています。通常のpandasの処理です。
[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:
    modelerData = modelerpy.readPandasDataframe()
    outputData = modelerData
    #Compute output data here
    #Ageに100を加算
    outputData['Age']=outputData['Age']+100
    #データの出力
    modelerpy.writePandasDataframe(outputData)
```

## 結果
以下のように、Ageに100が加算されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/7d1aa0fb-b8da-519d-76d8-68b96fbafb48.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