SPSS Modelerのユーザー入力ノードをPythonで書き換える。


SPSS Modelerでテスト用データやシミュレーション用データをつくるユーザー入力ノードをPythonのpandasで書き換えてみます。

以下の@416nishimaki さんの記事でModelerでの使い方は解説されていますので、この内容をpandasで行ってみます。

SPSS Modeler ノードリファレンス 1-3 ユーザー入力 - Qiita 

https://qiita.com/416nishimaki/items/7cac454b4484f79d3587


# 1．連番データ作成

Modelerでは以下のように「1;13」という指定で１から13までの連続データを生成できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/7590a14c-d0b6-e180-b909-ff60288d33ae.png)

pandasではrangeオブジェクトで同じことができます。
ちょっとトリッキーなのは、出力結果にはstop値は含まないことで、出力したい値+1を指定する必要があることです。以下では1から13のデータが欲しいので14を指定しています。

```python
pd.DataFrame(range(1,14),columns=['番号'])
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/a096e7d8-96a8-b84a-3d5b-bff45ff0f898.png)

* 参考
Python | range関数の使い方(開始から終了までの連続した数値を要素として持つrange型オブジェクトを作成する) 

https://www.javadrive.jp/python/function/index6.html

# 2．デカルト積データ作成
Modelerでは以下の指定で13ある番号に4つの絵柄の総当たりで52種類のレコードができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/ea084f3e-8d53-ce07-f22d-98b02d8c7805.png)

番号と絵柄の総当たりデータを生成しています。このようなデータ集合をデカルト積、または直積と呼びます。

pandasではitertools.productでデカルト積のデータを作ります。
ややこしいのは、itertools.productはイテレータなので、list化してからDataFrameのdataとして入力する必要があることです。

```python
import itertools
no=range(1,14)
card=['スペード','クラブ','ハート','ダイヤ']
pd.DataFrame(list(itertools.product(no,card)),columns=['番号','絵柄'])
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/fbc4d7cc-2ffa-6c67-c582-6a34d95802a4.png)

* 参考
Pythonで複数のリストの直積（デカルト積）を生成するitertools.product | note.nkmk.me 

https://note.nkmk.me/python-itertools-product/

# 3. 予測モデルのスコアリングデータを作成（1レコード）
作ったモデルでスコアリングするためのデータを任意に作りたいことがあります。
Modelerでは、スコアリングしたいデータを入力します。右クリックでユーザー入力ノードを生成できるので、フィールド名などの入力が不要で便利です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/74b9b346-f4b0-4985-5e66-70d4adf0f637.png)

pandasで、行データを生成する場合は2次元リストを与えます。
```python
df_scoring=pd.DataFrame([[21.9,86,2841,12.5]],
                        columns=['燃費', 'エンジン', '車体重量', '加速性'])
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/76f34505-a3c0-24dd-97e6-aef6728c0f39.png)

スコアリングはpredictの引数にDataFrameを入れることで可能です。（lrは別途作成した線形回帰のモデルです）
```python
df_scoring['$-馬力']=lr.predict(df_scoring)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/f2ab2121-718d-8b0a-3055-1c160f6b3bb9.png)

* 参考
Scikit-learn で線形回帰 - Qiita 

https://qiita.com/0NE_shoT_/items/08376b08783cd554b02e

# 4. 予測モデルのスコアリングデータを作成（複数レコード）
作ったモデルに様々なデータをいれて一番いい組み合わせなどを探すシミュレーションを行うことがあります。
ユーザー入力ノードはデカルト積のデータを簡単につくれるので、こういったシミュレーション用のデータが簡単に作れます。
以下のように240パターンのデータを作っています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/96d354d3-231e-8368-a33a-7b3ed0917b8f.png)

ちなみに以下の「燃費」と「加速性」のように0.1ずつ増えるデータを生成することもできます。
21.9, 22.1, 0.1で[21.9,22.0,22.1]が生成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/bea3f8f8-36fe-f7c0-b06d-af8dddf06ee1.png)

pandasでは、それぞれの列のリストデータをつくって、やはりitertools.productでデカルト積を作ります。
ちなみにnumpy.arrangeをつかうとfloat型の小数点刻みの連続データも簡単に作れます。
np.arange(21.9, 22.1, 0.1)で[21.9,22.0,22.1]が作れます。
```python
import numpy as np
#fueleco=[21.9,22.0,22.1]
fueleco=np.arange(21.9, 22.1, 0.1)
engine=list(range(86,90))
weight=list(range(2841,2846))
#accel=[12.5,12.6,12.7,12.8]
accel=np.arange(12.5, 12.8, 0.1)
df_scoring2=pd.DataFrame(list(itertools.product(fueleco,engine,weight,accel)),
                         columns=['燃費', 'エンジン', '車体重量', '加速性'])
```

スコアリングして、スコアリング結果で並べ替えると一番馬力の大きくなる説明変数を見つけることができます。

```python
df_scoring2['$-馬力']=lr.predict(df_scoring2)
df_scoring2.sort_values(by=['$-馬力'],ascending=False)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/bc46a5f8-192a-5ae7-fe2c-ab1462fa73f0.png)

* 参考
Python♪小数の繰り返し処理の落とし穴。range()、arange()、linspace() | Snow Tree in June 

https://snowtree-injune.com/2019/07/07/arange-linspace/

# 5. 各列のデータ型を指定したDataFrame生成
Modelerでは以下のように各データのデータ型を指定して、データを生成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/63dff8d9-9ebd-47ae-7763-48622f8211dd.png)

pandasでは、型を自動判定するのが基本的な動きです。
あえて指定する場合はpd.Series([＜データ＞], dtype=＜データ型＞)で指定する必要があります。
整数データをFloat型やstr型で保存する場合は指定が必要です。また、同じ整数型でもint8,int16,int32,int64のようなサイズが異なる型があります。メモリを最適化したい場合はこのサイズもなるべく小さいものを選びます。

いったんDataFrameをつくったあとにastypeで変換することも可能です。
日付型と時間型のデータはdatetime.dateとdatetime.timeのオブジェクトにしてから入れます。pandasには専用の型がないのでobject型で保存されます。
タイムスタンプ型はpd.to_datetimeで変換してから入れます。

```python
import datetime
df=pd.DataFrame({'INT': pd.Series([1], dtype=int),
                 'FLOAT': pd.Series([1], dtype=float),
                 'STR': pd.Series([1], dtype=str),
                 'DAY': [datetime.date(2000, 1, 31)],
                 'TS': [pd.to_datetime('2021-01-31 23:59:59')],
                 'TIME': [datetime.time(13,20,58)]
                })
```

pandasの各列のデータタイプはdyptesで調べます。
```python
print(df.dtypes)
```
```
INT               int32
FLOAT           float64
STR              object
DAY              object
TS       datetime64[ns]
TIME             object
dtype: object
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/c16344a3-8e2f-b29b-9e3d-f5e7ad4fcf1a.png)


ただ、これだとobject型のデータ型がわからないので、それを調べるためには以下のように調べます。
```python
for col in df.columns:
    print(type(df[col][0]))
```
```
<class 'numpy.int32'>
<class 'numpy.float64'>
<class 'str'>
<class 'datetime.date'>
<class 'pandas._libs.tslibs.timestamps.Timestamp'>
<class 'datetime.time'>
```

* 参考
datetimeモジュールとdate型/time型 | Python学習講座 

https://www.python.ambitious-engineer.com/archives/473

pandasは遅い！大量データの高速化、最適化のノウハウを公開！│ペン太ブルBlog 

https://propen.dream-target.jp/blog/pandas

# 6. サンプル
サンプルは以下に置きました。

ストリーム

https://github.com/hkwd/200611Modeler2Python/raw/master/userinput/userinput.str

notebook

https://github.com/hkwd/200611Modeler2Python/blob/master/userinput/userinput.ipynb

データ

https://raw.githubusercontent.com/yoichiro0903n/blue/main/cars.csv

■テスト環境
Modeler 18.3
Windows 10 64bit
Python 3.8.10
pandas 1.4.1