SPSS Modelerで、様々な説明変数とアルゴリズムでモデルを自動作成・自動スコアリングをする。

説明変数の様々な組み合わせとアルゴリズムでモデルを作りたいことがあります。

説明変数とアルゴリズムの組み合わせをあらかじめ定義したファイルを用意して、モデルまで一括で作成し、さらに自動でアンサンブルしてスコアリングするスクリプトを作りました。

以下の記事のスクリプトを改変したものです。

SPSS Modelerで、様々な説明変数や目的変数の設定パターンでモデルを自動作成する。 - Qiita 

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

* テスト環境
    * Modeler 18.4
    * Windows 11 64bit

* サンプルストリーム

https://github.com/hkwd/spssmodelerscript/raw/master/221125automodelingscoring/automodelingscoring.str

* サンプル学習データ

https://raw.githubusercontent.com/hkwd/spssmodelerscript/master/221125automodelingscoring/%E5%AD%A6%E7%BF%92.csv

* サンプルスコアリングデータ

https://raw.githubusercontent.com/hkwd/spssmodelerscript/master/221125automodelingscoring/%E3%82%B9%E3%82%B3%E3%82%A2%E3%83%AA%E3%83%B3%E3%82%B0.csv

* サンプル定義データ

https://raw.githubusercontent.com/hkwd/spssmodelerscript/master/221125automodelingscoring/%E3%82%BB%E3%83%83%E3%83%88%E5%AE%9A%E7%BE%A92.csv

# サンプルストリームの実行
学習データは以下のイメージです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/2943041e-93a7-8cb7-b3cb-bf4a932758b1.png)

以下のような定義ファイルを用意します。
1列目は説明変数と目的変数の組み合わせのセット名で、任意の文字列になります。
2列目以降は各変数をI（説明変数）、T（目的変数）、N（対象外）にするかの設定です。自動スコアリングを行うために、目的変数は同じ列を対象にする必要があります。この例では「KPI_15」をT（目的変数）に設定しています。
最終列がモデル作成のアルゴリズム名の設定です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/44d2b557-2661-183d-7f26-952f03f2d46f.png)

以下のようなにスコアリングデータも用意します。目的変数の「KPI_15」はスコアリング対象なのでもっていないデータです（あっても動作はします）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/adb0154c-ba12-a225-6285-433ea2f23679.png)


ファイルができたら、以下のように定義ファイルに「テーブル」ノードに接続し、学習データとスコアリングデータを単独で配置します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/f3e8a68a-2821-1a9f-3239-a99f1fffe588.png)


以下のスクリプトをストリームのプロパティに入れます。

```python:automodelingscoring.py
import modeler.api
stream = modeler.script.stream()

#説明変数定義のテーブルを読み出す
tablenode=stream.findByID("id8DAW4GC9YSD")
results = []
tablenode.run(results)
tableoutput = results[0]
# Access the table output's content model
tablecontent = tableoutput.getContentModel("table")

#学習データ読み込みノード
startnode   = stream.findByID("id2GDWUBHR75Z")

#スコアリングデータ読み込みノード
scorenode   = stream.findByID("id65PW6K9Q17R")
scorebranch = scorenode

for row in range(tablecontent.getRowCount()):
    #説明変数と目的変数のセット名
    setname=tablecontent.getValueAt(row, 0)
    #データ型ノードの配置
    typenode  = stream.createAt("type",setname, startnode.getXPosition() + 120, startnode.getYPosition()+row*50)
    stream.link(startnode, typenode)

    #説明変数と目的変数の設定情報の取得
    for col in range(tablecontent.getColumnCount()):
        colname=tablecontent.getColumnName(col)
        #print tablecontent.getColumnName(col), tablecontent.getStorageType(col),  tablecontent.getValueAt(row, col)
        if colname=='algorithm':
            #モデル作成ノードの配置
            modelnode  = stream.createAt(tablecontent.getValueAt(row, col),'model', typenode.getXPosition() +120, typenode.getYPosition())
            stream.link(typenode, modelnode)
        else:
            #目的変数
            if tablecontent.getValueAt(row, col)=='T':
                typenode.setKeyedPropertyValue("direction", colname, "Target")
            #説明変数
            elif tablecontent.getValueAt(row, col)=='I':
                typenode.setKeyedPropertyValue("direction", colname, "Input")
            #対象外
            else:
                typenode.setKeyedPropertyValue("direction", colname, "None")

    #モデル作成と配置
    modelnode.run(results)
    stream.successors(typenode)[1].setXYPosition(modelnode.getXPosition() +120, modelnode.getYPosition())
    #スコアリング枝へのモデル接続
    scoremodel=stream.createModelApplier(results[0],'')
    scoremodel.setXYPosition(scorebranch.getXPosition() +120, scorebranch.getYPosition())
    stream.link(scorebranch, scoremodel)
    scorebranch=scoremodel

#アンサンブルノードの配置
ensemblenode  = stream.createAt("ensemble","ensemble", scorebranch.getXPosition() + 120, scorebranch.getYPosition())
stream.link(scorebranch, ensemblenode)
scorebranch=ensemblenode
#テーブルノードの配置
tablenode  = stream.createAt("table","table", scorebranch.getXPosition() + 120, scorebranch.getYPosition())
stream.link(scorebranch, tablenode)
tablenode.run([])
```
「このスクリプトを実行」で実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/9080decb-dd83-d41f-8606-f5a5c331db46.png)

以下のように、データ型ノード、モデル作成ノード、モデルノード、そしてスコアリングモデルノード、アンサンブルノード、テーブルノードが作成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/d90eff10-2d33-6774-dbd5-929406ebe824.png)

設定ファイルには以下の3行の設定があったので、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/44d2b557-2661-183d-7f26-952f03f2d46f.png)

以下の３つのデータ型ノードの設定を生成しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/927fe168-cb6d-d233-b6fc-9a3463f75c5a.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/71c30dea-e384-29db-f366-d8e67fca0052.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/54a48782-113f-11ac-e6e0-05d94e9827ef.png)

スコアリング結果はアンサンブルされ、平均と標準誤差が計算されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/1ec00415-d68c-5d4b-2400-a3564fb2a13d.png)


# スクリプトの解説

説明変数目的変数定義のテーブルは以下で読みだしています。
tablenode=stream.findByID("id8DAW4GC9YSD")には「テーブル」ノードのIDを記入します。


```python:説明変数目的変数定義のテーブルを読み出す
tablenode=stream.findByID("id8DAW4GC9YSD")
results = []
tablenode.run(results)
tableoutput = results[0]
# Access the table output's content model
tablecontent = tableoutput.getContentModel("table")
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/0ac8c4df-25ce-869f-c273-ea152eb38cab.png)

学習データのノードのIDは以下で取得して記入します。
```python:学習データ読み込みノード
startnode   = stream.findByID("id2GDWUBHR75Z")
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/718579bf-28c5-efdc-41c2-38dc6c54c503.png)

スコアリングデータのノードのIDは以下で取得して記入します。
```python:スコアリングデータ読み込みノード
scorenode   = stream.findByID("id65PW6K9Q17R")
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/29af0f09-0346-0922-febc-4ba25306d834.png)


定義ファイルを読んで、algorithmで指定されたアルゴリズムのモデルノードを配置しています。
```python:モデル作成ノードの配置
if colname=='algorithm':
    #モデル作成ノードの配置
    modelnode  = stream.createAt(tablecontent.getValueAt(row, col),'model', typenode.getXPosition() +120, typenode.getYPosition())
```

データ型ノードのロールの説明変数（Input）、目的変数(Target)、対象外(None）を設定していきます。
```python:データ型のロール設定
#目的変数
if tablecontent.getValueAt(row, col)=='T':
    typenode.setKeyedPropertyValue("direction", colname, "Target")
#説明変数
elif tablecontent.getValueAt(row, col)=='I':
    typenode.setKeyedPropertyValue("direction", colname, "Input")
#対象外
else:
    typenode.setKeyedPropertyValue("direction", colname, "None")
```

モデル作成ノードを実行して、出来上がったモデルノードを並べます。モデルはスコアリングにも使うのでresultsを取っておきます。
```python:モデル作成と配置
modelnode.run(results)
stream.successors(typenode)[1].setXYPosition(modelnode.getXPosition() +120, modelnode.getYPosition())

```

さらにスコアリング枝にも出来上がったモデルノードを並べます。
createModelApplierで作成されたモデルをキャンバスに配置しています。
```python:スコアリング枝へのモデル接続
scoremodel=stream.createModelApplier(results[0],'')
scoremodel.setXYPosition(scorebranch.getXPosition() +120, scorebranch.getYPosition())
stream.link(scorebranch, scoremodel)
scorebranch=scoremodel
```
最後にアンサンブルノードとテーブルノードをつないで実行しています。
```python:アンサンブルノードとテーブルノード
#アンサンブルノードの配置
ensemblenode  = stream.createAt("ensemble","ensemble", scorebranch.getXPosition() + 120, scorebranch.getYPosition())
stream.link(scorebranch, ensemblenode)
scorebranch=ensemblenode
#テーブルノードの配置
tablenode  = stream.createAt("table","table", scorebranch.getXPosition() + 120, scorebranch.getYPosition())
stream.link(scorebranch, tablenode)
tablenode.run([])
```
# 参考
### テーブル コンテンツ モデル getContentModel- IBM Documentation 

https://www.ibm.com/docs/ja/spss-modeler/18.4.0?topic=results-table-content-model

### typenode プロパティー - IBM Documentation 

https://www.ibm.com/docs/ja/spss-modeler/18.4.0?topic=properties-typenode

### Nodeオブジェクト - IBM Documentation 

https://www.ibm.com/docs/ja/spss-modeler/18.4.0?topic=supernodes-node-objects

### ストリーム内のノードのトラバース successors- IBM Documentation 

https://www.ibm.com/docs/ja/spss-modeler/18.4.0?topic=streams-traversing-through-nodes-in-stream

### ノードの作成 -createModelApplier IBM Documentation 

https://www.ibm.com/docs/ja/spss-modeler/18.4.0?topic=streams-creating-nodes


### ストリーム実行結果へのアクセス modeloutput - IBM Documentation 

https://www.ibm.com/docs/ja/spss-modeler/18.4.0?topic=tips-accessing-stream-execution-results