SPSS Modelerでカスタムノードを作る

SPSS ModelerのPythonやRで書いた拡張ノードをあまりプログラミング言語に詳しくない方にでも簡単に使ってもらいたいことがあります。

そういう場合にはカスタムノードを作ることができます。

ここでは以下の記事で作った正規表現を行うPython拡張ノードをカスタムノード化してみたいと思います。
SPSS Modelerで正規表現を使う（Python拡張ノード版） - Qiita 

https://qiita.com/kawada2017/items/c0480bc2b3dcc9cd7be6

この拡張ノードでは以下のようにNAMEという列の文字列から前株の「株式会社」という文字列を「（株）」に置き換えるNAME＿REという列を追加しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/5a488744-b704-ece9-885c-ccdadb5a6737.png)

プログラムの以下の部分で正規表現の対象となる列や置換したい文字列を指定しています。

```python:正規表現
#対象とする列名
target_col_name='NAME'
#追加する列名
res_col_name=target_col_name+'_RE'
#検索対象文字列
old='^株式会社'
#置換対象文字列
new=r'(株)'
```

カスタムノードではこの部分をGUIから指定できるようにします。

* サンプルストリームとカスタムノード
サンプルストリーム

https://github.com/hkwd/spsssample/raw/master/20210419%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BE/220601%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BEpythoncn.str

regexppy.mpe 

https://github.com/hkwd/spsssample/blob/master/20210419%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BE/regexppy.mpe

* テスト環境
    * Modeler 18.3
    * Windows 10 64bit
    * Python 3.8.10
    
# 1 カスタムノードを作り始める

メニューの「拡張機能」の「カスタムノードダイアログビルダー」を起動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/d5d94983-9572-9ecf-b499-68674023584c.png)

「ダイアログ名」と「表題」に一意になるノードの名前を入力します。
「スクリプトタイプ」は今回のスクリプトはPythonなので「Spark用Python」を選びます。
列のデータ加工なので「ノードタイプ」に「プロセス」を選び、「パレット」に「フィールド操作」を選びます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/d4700821-e34c-ff20-551f-0bbd49e69f09.png)

# 2 コントロールの配置
GUIのコントロールをダイアログに配置します。

まず、正規表現適用の「対象フィールド」を指定する「フィールドピッカー」を配置します。
識別子、表題を指定します。識別子は「item_+変数名」のようにプログラムで呼び出す際にわかりやすい名前をつけることをお勧めします。
「ピッカータイプ」はここでは「単一」を選びます。これは一列だけを選ぶという意味です。「多重」にして複数列を対象できるようにしてもよいのですが、今回は元のプログラムが単一列を対象にしているので、その場合はプログラムの修正が必要です。
入力必須項目なので「実行に必要」は「True」を指定します。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/e18156e8-2e10-c35d-2e9c-9c8fbf79906d.png)


次に正規表現適用後の値を書き出す派生フィールド名に付ける接尾辞を入力する「テキストコントロール」を配置します。
識別子、表題を指定します。
「デフォルト値」には「_RE」を入れておきます。つまり派生フィールド名は「＜対象フィールド＞_RE」になります。
入力必須項目なので「実行に必要」は「True」を指定します。
「引用符の処理」は「Python」にしておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/0bf8b678-8be5-8284-1195-276763c010d3.png)


そして、検索対象文字列を入力する「テキストコントロール」を配置します。
識別子、表題を指定します。
入力必須項目なので「実行に必要」は「True」を指定します。
「引用符の処理」は「Python」にしておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/400b9e17-ecc7-9efd-4306-2d82fbbd5d30.png)

最後に、置換対象文字列を入力する「テキストコントロール」を配置します。
識別子、表題を指定します。
「引用符の処理」は「Python」にしておきます。
入力必須項目ではないので、「実行に必要」は「False」のままにしておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/0257a013-3f3f-9444-694a-6637c36adaa7.png)

# 3 スクリプトの記述
「拡張ノード」に書いたスクリプトをスクリプトの入力欄にコピーペーストします。
そして、各コントロールと対応する変数に「%%識別子%%」を入力します（コントロール上で右クリックをして「スクリプトテンプレートに追加」を選ぶと簡単に入力できます）。余計な空白などが入らないように気を付けてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/f2b712e0-ad2c-3f4b-5605-f5a01e5a1d77.png)


完成したスクリプトは以下です。

```python:正規表現カスタムノード
#ライブラリのインポート
import spss.pyspark.runtime
from pyspark.sql.types import *


#コンテキストオブジェクト定義
asContext = spss.pyspark.runtime.getContext()
sqlContext = asContext.getSparkSQLContext()

target_col_name='%%item_target_col_name%%'
res_col_name=target_col_name+'%%item_col_postfix%%'
old='%%item_old%%'
new=r'%%item_new%%'

if asContext.isComputeDataModelOnly():
    #スキーマ設定
    inputSchema = asContext.getSparkInputSchema()
    outputSchema = inputSchema
    outputSchema.fields.append(StructField(res_col_name, StringType(), nullable=True))
    asContext.setSparkOutputSchema(outputSchema)

else:
    #データ読込
    indf  = asContext.getSparkInputData()
    df = indf.toPandas()

    #正規表現加工
    df[res_col_name]=df[target_col_name].str.replace(old, new, regex=True)

    #データ書出し
    outdf = sqlContext.createDataFrame(df)
    asContext.setSparkOutputData(outdf)

```


# 4 拡張機能のプロパティ設定
「拡張機能」メニューの「プロパティ」を開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/c29b7ad5-d7fe-3648-60b0-2cd102d56a82.png)

「名前」にAsciiで名前をつけ、「要約」に入力をし、「続行」ボタンで閉じます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/1ebb84e8-48a0-8464-907d-ee6b70e0644c.png)

# 5 カスタムノードの保存とインストール

ファイルメニューから「名前を付けて保存」を選びます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/2e0220d5-df9a-33e3-c05d-a7f113c4c63d.png)

適当なところに保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/1564c1d0-2ec0-4e44-be7c-31c89cd9ad62.png)

次に「インストール」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/943fc300-ec64-c4d8-6f69-e058dcbf1bcf.png)

インストールされたというメッセージが出ます。
「ノードタイプ」に「プロセス」を選び、「パレット」に「フィールド操作」を選んだので、フィールド設定のパレットに導入されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/0e09b90c-98ed-c338-11ef-a228b75d88d8.png)

ノードをキャンバスに置いて接続します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/ecb44a49-5bba-b187-c7fb-7ab697069f29.png)


「対象フィールド名」に「NAME」
「検索対象文字列」には「^株式会社」
「置換対象文字列」には「（株）」
を入力します。
NAME列の前株を「（株）」に置き換えるという設定です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/8864e733-27b1-afd6-7899-b121406388aa.png)

正しく変換できています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/cec279da-d09c-1eb8-9038-19cd928bd2e0.png)

このようにカスタムノードにすることで、プログラムを読めなくてもPythonを利用することができるようになります。

# 6 カスタムノードのアンインストール、再インストール、配布、上書きなど
まず、カスタムノードを使っているストリームを閉じておきます。
それからファイルメニューから「アンインストール」できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/01a15c1f-9e56-1af2-84b9-76a3ed1a63e0.png)
アンインストールするカスタムノードを選びます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/34899d8a-5143-fcc2-77bf-3db3a33c2f7d.png)
アンインストール前に保存をすることもできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/027066a1-b64a-cddd-3322-f26967726a44.png)
パレットからなくなりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/7bc05f90-e70c-9451-07ee-f9fb581c33d7.png)

再インストールしたり、他の人のModelerに導入する場合には、「開く」で保存した.mpeファイルを開きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/c2e3e0b8-e92f-ea35-1b55-498d8eb5d1c8.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/27a2eabc-c550-2f40-b1d4-c8527ac618fe.png)

そして、「インストール」ボタンでインストールします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/6a170c95-d203-c6ea-9f6d-e0aede019452.png)


また、すでにインストールしたカスタムノードを開く場合には「インストールされた項目を開く」で開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/0f49c7ab-0eef-d7d9-da33-128c78a052e5.png)

修正を行って上書きをする場合には「インストール」ボタンを押し、上書きします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/eb9aa340-67f2-5fc7-ec74-b39053953133.png)


# 参照
カスタム・ノードの作成と管理 - IBM Documentation 

https://www.ibm.com/docs/ja/spss-modeler/SaaS?topic=extensions-creating-managing-custom-nodes