SPSS Modelerで最適なクラスター数(のヒント)を探してみる その２

前回の記事からの続きです。

https://qiita.com/harrowharrow16/items/57b6696193aeb9faf546

さて、今回は予告通りに、SPSS Modelerの拡張ノードを利用してシルエットスコアや誤差平方和（SSE）を計算して最適なクラスター数を推定してみます。

**※. 2025/03/11 GitHubに記事で使用したストリームとデータをアップしました。**

https://github.com/harrow16/Qiita/tree/main/Clustering_Part2_0731



## シルエットスコアとは（再掲）
シルエットスコア（Silhouette Score）は、クラスタリングの効果を評価するために使用される指標の一つです。このスコアは、各データポイントがどの程度適切にクラスタリングされているかを数値で示します。具体的には、クラスタ内の凝集度とクラスタ間の分離度を用いて計算されます。
シルエットスコアは -1 から 1 の範囲で、1 に近いほどクラスタリングの品質が高いことを示します。スコアが 0 に近い場合は、クラスタの境界が曖昧であることを示し、負の値はクラスタリングが不適切である可能性があります。
引用 :

https://note.com/united_code/n/n8a15ae4063b3

※.引用元のそのままの内容を記載させていただいております。


## エルボー法（再掲）

エルボー法は、それぞれのクラスター内の誤差平方和（SSE）を計算し、クラスター数とそれぞれの誤差平方和の和をプロットして適切なクラスター数を判断する手法です。エルボー（肘）のように曲線が折れ曲がる点が適切なクラスター数と考えます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3839172/25e8ecca-3a1a-c865-dbbb-8d35774e8a48.png)

## SPSS Modelerの拡張ノード 
　
 さて今回は、SPSS ModelerでRやPythonのシンタックスを記述してデータ加工やモデル作成が行える拡張ノードを利用します。以下のように5種類あります。本記事では、拡張の出力ノードを利用します。あれ？拡張モデルではないの？と思われる思いますが、スコアリングなどしないなら、出力ノードでもモデル作成と精度確認などができます。（次回、スコアリング用に拡張モデルの利用方法を紹介しようと思います。）
 

 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3839172/0e6c586a-c8f1-852b-4840-8b0b750047ff.png)


記述できるシンタックスは R、Python for Spark、Pythonの3種類。v18.5でPythonが追加されました。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3839172/66724bfb-0a06-b53f-0d80-fd9decc3abd0.png)


Python for SparkとPythonの違いは何のか？v18.4以前のバージョンの拡張ノードでPythonを使う場合は、「Python for Spark」のみの選択が可能であり、SparkのDataFrameにデータが読みこまれていましたが、新しくネイティブの「Python」が選択可能になり、pandasのDataFrameに読み込むことが可能になりました。 ネイティブ Python APIを使うと、Python for Sparkに比較して、初回の実行のオーバーヘッドが大幅に削減されます。


https://qiita.com/spssfun2017/items/b619d5e3db8bb1ff816e


ということでパフォーマンスなどが向上しています。但し利用するPythonをModelerに同梱されているものから変更できないなど不便な部分もあります。Python for Sparkでは、自身で導入したPythonを指定して利用することができます。ご利用は計画的に。

今回はv18.4以前のユーザーでも参考になるように、Python for Sparkでやってみます。


## 拡張の出力ノードでシルエットスコア・SSEを計算する 
前回に引き続き、使用するデータは、みんな大好きirisのデータです。

データダウンロード先 :

 https://archive.ics.uci.edu/dataset/53/iris

SPSS Modelerは v18.5を利用します。（本記事の内容はバージョン18.5でなくても問題なく実行できます。）


### 0. 準備

今回導入する環境は以下となります。
今回は、v18.4以前のユーザーの方でも利用できるように、Python for Sparkのシンタックスを利用します。

|ソフトウェア等    |バージョン                |
|----------|--------------------------|
|OS|Windows 11 Pro |
|SPSS Modeler  |18.5 (windows)         |
|Python|3.10.11 (新規に導入)|
|scikit-learn|1.6.1 |
|numpy|**1.26.4** |
|pandas|2.2.3|


#### ①. Python環境の準備 - Python 3.10.11
Windows版のpythonをインストールしましょう。
v18.5は3.10系をサポートしていて、Windowsのインストーラーが提供されている3.10.11 を導入します。

https://www.python.org/downloads/release/python-31011/

本記事では、**"C:\Python\310"** へインストールしたPythonを利用しています。

#### ②. SPSS ModelerとPythonの連携設定
Python for Sparkシンタックスを利用する時は、ユーザー自身でPython環境を指定することができます。
もちろん、指定をせずにデフォルト環境を利用することも可能です。

コンフィグファイルを編集します。（アクセス権限が必要です。）

コンフィグファイルの場所 : **"C:\Program Files\IBM\SPSS\Modeler\18.5\config"**
コンフィグファイル名 : **"options.cfg"**


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3839172/c5151f12-7604-4b35-b811-9aa96aec577f.png)


コンフィグファイルをテキストエディタで開いて編集します。

デフォルトでは、以下のようにeas_pyspark_python_pathへパスが設定されています。ネイティブPythonシンタックスの環境と同じパスですね。
eas_pyspark_python_path, "C:/Program Files/IBM/SPSS/Modeler/18.6/python_venv/Scripts/python.exe"

今回は、
eas_pyspark_python_path, **"C:/Python/310/python.exe"** 
と変更しています。
※.これは、ユーザーごとに任意のパスを指定してください。（デフォルトのままOK）


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3839172/7db274f0-718a-407b-9137-56045f4bd051.png)



#### ③. scikit-learnの導入
K-Meansを使うために、Scikit-Learnを導入しましょう。
下記コマンドは、"C:/Python/310/"環境へ導入する際の例となります。

```
C:\Python\310\>python.exe -m pip install Scikit-Learn
```

また、念のためにNumpyをアンインストールしてから、**Numpy v1.26.4 を再導入しましょう。**

scikit-learnを導入する時に、自動でnumpy 2.2.3がインストールされました。
ですが、2.2.3の場合、以下のエラーがでてうまく動作しませんでした。
```
ValueError: numpy.dtype size changed, may indicate binary incompatibility. 
```
そのため、v1.26.4を再導入しました。

### 1.	作ったストリーム　その2
入力データもirisなので特に加工などせずシンプルなストリームです。
各ノードの設定については、入力ノードとデータ型ノードは前回と同じ設定ですので、説明は割愛します。
また、拡張モデルノードとナゲットがありますが、今回は割愛します。(一応、クラスタリングの結果をModelerで確認できるようにモデルも作成してみました。)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3839172/8f7b67d3-22a3-086c-1bf3-6087f3b072ba.png)



## 2.	拡張の出力ノード シンタックス
### ①	シンタックス全体


```python:k-meansシンタックス

#---------------------------------------------------------
# 必要なライブラリインポート
#---------------------------------------------------------
#SPSS用ランタイム
import spss.pyspark.runtime
#K-Means
from sklearn.cluster import KMeans
#シルエットスコア計算
from sklearn.metrics import silhouette_score

#---------------------------------------------------------
# データ読込処理
#---------------------------------------------------------
#コンテキスト取得
ascontext = spss.pyspark.runtime.getContext()

#データフレーム形式によるデータ読込
sdf = ascontext.getSparkInputData()
#Pandasに変換
pdf = sdf.toPandas()

#---------------------------------------------------------
# 説明変数、目的変数の設定
#---------------------------------------------------------
#入力データのスキーマ読み込み
inputSchema = ascontext.getSparkInputSchema()

#スキーマから変数名、ロールを参照して説明変数を設定
#変数準備
predictors =[]
#カラム分ループ
for col in inputSchema:

    #説明変数ロール判定 - 入力
    if col.metadata['role']=='input':
        #説明変数配列に追加
        predictors.append(col.name)
 
print("説明変数 = ")
print( predictors )
#---------------------------------------------------------
# シルエットスコア算出
#---------------------------------------------------------
#For文で繰り返す
#クラスタ数を指定
for k in range(2,10):
    # k-meansクラスタリング
    model = KMeans(n_clusters=k, random_state=0)
    #結果を取得
    KM_Predict = model.fit_predict(pdf[predictors])

    # シルエットスコアの計算
    score = silhouette_score(pdf[predictors], KM_Predict)

    #結果出力
    print("With K = " + str( k ) )
    print("Silhouette = " + str(score))
    print ('SSE: %.2f'% model.inertia_)

```

### ②. シンタックス詳細
　では、パート毎に説明していきます。
#### a.	ライブラリの読み込み

　ここでは、SPSS Modeler上でPythonを利用する時に必要なライブラリと、K-meansモデル、シルエットスコア計算用のライブラリをインポートしています。
 ※.事前にscikit-learn（sklearn）をPython環境にインストールしておいてください。
 

```python:k-meansシンタックス ライブラリ読み込み

#---------------------------------------------------------
# 必要なライブラリインポート
#---------------------------------------------------------
#SPSS用ランタイム
import spss.pyspark.runtime
#K-Means
from sklearn.cluster import KMeans
#シルエットスコア計算
from sklearn.metrics import silhouette_score

```


#### b.	データの読み込み
　SPSS ModelerからSparkデータフレーム形式でデータを受け取り、Pandasデータフレーム形式に変換します。


```python:k-meansシンタックス データ読み込み

#---------------------------------------------------------
# データ読込処理
#---------------------------------------------------------
#コンテキスト取得
ascontext = spss.pyspark.runtime.getContext()

#Sparkデータフレーム形式によるデータ読込
sdf = ascontext.getSparkInputData()
#Pandasデータフレームに変換
pdf = sdf.toPandas()

```


#### c.	説明変数の設定
　データ型ノードで設定したロールを参照して、K-meansに入力するデータを定義します。K-meansには説明変数のみでいいので、ロールが”入力”のデータのみ::選択しています。


```python:k-meansシンタックス 説明変数定義

#---------------------------------------------------------
# 説明変数、目的変数の設定
#---------------------------------------------------------
#入力データのスキーマ読み込み
inputSchema = ascontext.getSparkInputSchema()

#スキーマから変数名、ロールを参照して説明変数を設定
#変数準備
predictors =[]
#カラム分ループ
for col in inputSchema:

    #説明変数ロール判定 - 入力
    if col.metadata['role']=='input':
        #説明変数配列に追加
        predictors.append(col.name)


```



#### d.	K-meansモデルの作成、シルエットスコア・SSE計算
　ループしてクラスター数を変えながらK-meansモデル作成、シルエットスコア算出、SSE算出をしています。
　SSEは、inertia_関数で計算できます。


```python:k-meansシンタックス 説明変数定義
#---------------------------------------------------------
# シルエットスコア算出
#---------------------------------------------------------
#For文で繰り返す
#クラスタ数を指定
for k in range(2,10):
    # k-meansクラスタリング
    model = KMeans(n_clusters=k, random_state=0)
    # 結果を取得
    KM_Predict = model.fit_predict(pdf[predictors])

    # シルエットスコアの計算
    score = silhouette_score(pdf[predictors], KM_Predict)

    #結果出力
    print("With K = " + str( k ) )
    print("Silhouette = " + str(score))
    print('SSE: %.2f'% model.inertia_)

```


### 3. シルエットスコア・SSEを確認する 
拡張の出力ノードを実行して、結果を確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3839172/9823d901-9e52-96c1-7701-d098c3e3dbbc.png)

With K = 2　←クラスター数
Silhouette = 0.6808136202936816　←シルエットスコア
SSE: 152.3　←SSE

上記のようにクラスター数が9まで結果が出力されています。

また、SSEとクラスター数の関係をグラフ化すると下図のようになります。クラスター数3のあたりで肘が曲がったようなグラフになっていますね？？（まぁ、実際の分析できれいに特徴がでることは稀だと思います)。シルエットスコアも考慮するとひとまず、クラスター数は3を目安にしていいのではないでしょうか。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3839172/4c027af4-3298-3532-a1c0-c7a613bbe578.png)


## まとめ
前回と今回の記事で、SPSS Modelerで最適なクラスター数を探す指標となるシルエットスコア・SSEの確認方法を紹介しました。
最終的には各クラスターの特徴を確認して分析者がクラスター数やパラメータを調整することになります。が、きちんした指標をもとにしてクラスタリングをしたというひとつの根拠にもなりますので、ぜひ活用してみてください。

次回はせっかくなので、拡張モデルノードを使ってクラスタリング結果を確認してみます。

https://qiita.com/harrowharrow16/items/16d79059d33d03e48ab9


## 参考情報
### SPSS Modeler ノードリファレンス目次

https://qiita.com/416nishimaki/items/503037195081d3323eee

### SPSS Modeler 逆引きストリーム集（データ加工）

https://qiita.com/416nishimaki/items/4bf813e6cba84b2a1008

### SPSS funさん記事

https://qiita.com/spssfun2017

### SPSS連載ブログバックナンバー

https://qiita.com/416nishimaki/items/24c054defbe7280bcf0e