「変数が多いんですけど」・・・”変数まとめ” や ”グループわけ”がデータ分析の助けに

### ～複数の評価尺度で点数づけされたデータをシンプルにしたり、グルーピングしたりする（Pythonによる特異値分解とクラスタリング分析）～

***
### はじめに
Pythonでデータ分析・・・私にとってはマダマダ夢物語に近いが、そんな状況の私でも「見よう見まねでできるようにはなるかも？！」という気にさせてくれるのが、『データサイエンス塾!!』さんだ。

https://www.youtube.com/c/nishimaki

多くの動画をアップしておられるが、再生リストのなかに「とてもよくわかる機械学習の基本」（全11の動画）というタイトルの再生リストがあり、

https://www.youtube.com/playlist?list=PL_tQOEJCWOE5ScOzRwyTGEAAZg_RQhM_r

同じデータが扱われている動画#1～#3について、私が倣って実施した備忘として記録するもの。

　
***
### 実行条件など
>・**Google colab**で実行
・**一部のコードだけ変更**させていただいています
※あるグラフをseaborn+層別で描きたかった等によるものです。**データサイエンス塾!!さん、すいません**。
　

https://toukei-lab.com/google-colaboratory


***
### 概要説明
私たちは「いくつかの尺度をあげ、対象候補を比較する」ということを日常的に行っています。
- 携帯電話なら、尺度は『サイズ，容量，機能，…』等となり、対象候補はA社，B社…。
- 店舗なら、尺度は『サービス，立地，品揃え，…』等となり、対象候補はA店，B店…。


尺度、対象候補ともに限られたデータ数の場合は把握できそうですが、データ数が多くなると簡単ではありません。
>① 尺度が多い場合は、限られた要素にギュッとまとめて表現できるとよいし、
② 対象候補が多い場合は、限られたグループに分けられるといいですね。

上記①②のようなケースでは、以下の手法が利用されます。
>① **尺度をギュッとまとめる**　⇒　SVD（特異値分解）という手法を利用
② **対象候補を限られたグループに分ける**　⇒　k-means法 及び クラスター分析を利用


この記事では、
k-means法でグループ分けを行い ⇒ SVD（特異値分解）で尺度を単純化 ⇒ その後、k-means法とSVDの合わせ技の分析結果 を行い、最後にクラスター分析も行います。


***
### 「とてもよくわかる機械学習の基本（動画#1～#3）」の例題について
例題は「人のスキル」データとされています。
尺度は『コミュニケーション，リーダーシップ，プログラミング，ネットワーク知識，セキュリティ知識』、対象候補はAさん～Oさん（全15名）。

データ分析により、よりシンプルな尺度で、対象候補をいくつかのグループに分けることができ、その結果、**”「人のスキル」全体を眺めるとこんな傾向があるといえるね！” などといえる・・・が目指したいゴール**となります。

　　
***
### ライブラリのインストールおよびインポート
Google colabでグラフ描画すると、日本語が文字化けするので、まず以下をインストール。
※文字化けしない方は無視していい

```:日本語化
pip install japanize-matplotlib
```

ライブラリのインポート。私はどのライブラリが必要かを逐次判断するのは面倒なので、基本以下をデフォルトでインポートしています。（雑ですいません）

```:ライブラリインポート
# Import required libraries 
%matplotlib inline 
import pandas as pd 
import numpy as np 
import matplotlib.pyplot as plt 
import japanize_matplotlib 
import seaborn as sns 
sns.set(font="IPAexGothic")  
from scipy.stats import norm
```

　
***
### データの読み込み
1. 例題ファイル（CSV）をダウンロードし、Google colabにアップロード

https://analysis-navi.com/wp-content/uploads/2021/07/4-2_skill_level.csv

※Google Colabへのアップロードの方法はここでは述べません。


2. CSVファイルの読み込み。
以下コードの実行により、「df」というデータフレームにcsvデータが納められます。

```:csv読込み
#　csvファイル読込み（indexとして使いたい列の列番号がある場合は指定：今回は index_col=0）
Path = 'https://analysis-navi.com/wp-content/uploads/2021/07/4-2_skill_level.csv'
df = pd.read_csv(Path, index_col=0)
```

　　
***
### k-meansの実行
k-meansは、k平均法と呼ばれるクラスタリング手法のひとつです。
読込んだデータは、対象候補 Aさん～Oさん（全15名）のスキル（コミュニケーション，リーダーシップ，プログラミング，ネットワーク知識，セキュリティ知識）が10段階数値で示されたデータです。

**「クラスタ数（いくつのグループに分けたいか？）」を設定**し、以下のコード実行するだけで、対象候補 Aさん～Oさん（全15名）を設定したグループ数に分けてくれます。

```:k-means実行
#　k-means実行
from sklearn.cluster import KMeans
```

```:クラスター数の指定
#　クラスター数を任意に指定する
vec = KMeans(n_clusters=4)
group_num = vec.fit_predict(df)
```

分けられたグループが「group_num」というデータフレームに入ったかを見てみます。

```:グループ分けの確認
#　グループ分けの結果（k-meansは計算の度に結果変わる）
group_num
```


<details><summary>出力結果</summary>
array([3, 2, 1, 3, 0, 3, 2, 3, 1, 2, 1, 0, 1, 1, 0], dtype=int32)
</details>
　

グラフ化などを進めるため、上記グループ分けの結果をデータフレームに反映する処理を行います。
まずは、新たにデータフレーム「df_calc」を用意し、

```:グループ分けの結果をデータフレームに反映①
# df_calcというデータフレームにdfをコピー
df_calc = df.copy()
```

　
次に、「df_calc」にグループ分けの結果「group_num」を反映します。

```:グループ分けの結果をデータフレームに反映②
# df_calcに['グループ名']を追加し、group_numのデータを登録
df_calc['グループ名'] = group_num
df_calc
```

以下が反映後の結果。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/08ed28b3-6825-593d-ced3-4e4f1bc8f311.png)
　　
***
### データ傾向を見てみる
**平均値**
グループ毎の集計が可能となったので、グループ毎の平均値を算出します。

```:グループ平均値
#グループ毎に平均値を出す
df_calc.groupby('グループ名').mean()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/4ccf748f-059b-1ebc-5b83-b72078bdd9c4.png)

**散布図行列**
グループ分けした傾向を散布図行列（ペアプロット）で眺めるということを実行されていたサイトがありましたので、同様にやってみました。ただ、これは参考程度かと思います。

```:ペアプロット描画
sns.pairplot(data=df_calc,hue="グループ名",palette='Blues')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/dbbfb856-b51c-f924-e22a-3cc3409730c8.png)

***
### SVD（特異値分解）の実行
以下のコードでSVD（特異値分解）が実行できます。

```:#SVDによる各サンプルの座標計算
from sklearn.decomposition import TruncatedSVD

model_svd = TruncatedSVD(n_components=2)
vecs_list = model_svd.fit_transform(df)
```

配置された座標を確認してみます。

```:
vecs_list
```
<details><summary>出力結果</summary>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/353faf1d-3fe4-45f3-5689-1b0ea48d578a.png)

</details>


```:SVDで軸の意味を検討
#　評価尺度を平面に配置（※X軸,Y軸がどのような軸かは人が読み取らないといけない）
X_comp,Y_comp = model_svd.components_

plt.figure(figsize=(6,6))

sns.scatterplot(x=X_comp,y=Y_comp)

plt.title('SVD', fontsize=18)   # グラフタイトル
for i,(annot_x,annot_y) in enumerate(zip(X_comp,Y_comp)):
  plt.annotate(df.columns[i],(annot_x,annot_y))
```

以下の通り、平面上に「尺度」が配置されました。
例えば、右方向「スキルレベル」、上方向「ヒューマンスキル」、下方向「ITスキル」となるな・・・等、これは人が読み取って名付けるしかありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/8ac786da-d109-bd60-a89c-2f4812b8acbd.png)
　　
今度は、各サンプル（Aさん～Oさん）を平面座標に配置する手続きになります。
この座標「vecs_list」を元のデータフレーム（df_calc）に結合します。

```:データフレーム（df_calc）にSVDで導いた座標（X,Y）を結合
# df_calcに['X']['Y']列を追加、それぞれにvecs_listの0列目と1列目データを登録
df_calc['X'] = vecs_list[:,0]
df_calc['Y'] = vecs_list[:,1]
```

　　
結合した結果を見てみます。

```:結合結果の確認
# df_calcへのX,Yデータ追加を確認
df_calc
```

X,Yデータが結合されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/9a75c0b2-70f1-acba-cbcf-a712386be1f5.png)

　
***
### グループ化した結果を平面上に可視化
最後に、SVDで尺度を要約した（平面座標X,Y）に、k-meansでグループ分けしたサンプル（Aさん～Oさん）を配置します。

```:SVDで導いた座標にクラスター分けしたサンプルを配置
# SVD（特異値分解）した平面にk-meansでクラスター分け（グループ名）したサンプルをグラフに配置
plt.figure(figsize=(6,6))

sns.scatterplot(data=df_calc,x='X',y='Y',hue="グループ名")

plt.title('k-means + SVD', fontsize=18)   # グラフタイトル

for i,(x_name,y_name) in enumerate(zip(df_calc['X'],df_calc['Y'])):
  plt.annotate(df.index[i],(x_name,y_name))

plt.show()
```

以下が配置の結果です。
グループ0は中央下に、グループ1は右下に、グループ2に右上に、グループ4は左下に配置されています。
軸の意味合いは、右方向「スキルレベル」、上方向「ヒューマンスキル」、下方向「ITスキル」ということでしたので、『グループ1はスキルレベルが高く、特にヒューマンスキルが顕著』、『グループ2もスキルレベルが高く、特にITスキルが顕著』・・・等、グループの特徴の違い、各グループにどのサンプルが属すか？等が把握できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/06b9cb35-0cb3-b74d-0454-b510af48f88b.png)

***
### クラスター分析
例題データの尺度の単純化とグループ分けは、上記までの流れで一旦完了。
SVDで尺度を単純化、k-meansでグループ分け・・・という内容でしたが、グループ分けの方法は以下に示す「クラスター分析」による方法もあります。
分析の結果、出力される「デンドログラム（樹形図）」をみるとわかりますが、サンプルがトーナメント表のような形で表現されます。
似たサンプルほど近くに配置されるのは、k-meansと同じですが、平面に打点されるk-meansよりもデンドログラムの方がサンプル間の関係がより細かく把握できるという特徴があります。
データサイエンス塾!!さん曰く、サンプル数が少ない場合はクラスター分析＝デンドログラムでもよいが、サンプルが多い場合は（デンドログラムは見にくくなるので）k-meansのほうがよいとのことでした。

```:クラスター分析実行
#　クラスター分析実行（methodでクラスター分析手法[ward法が一般]、metricで分類に用いる対象間の距離計算方法を指定[euclidean=ユークリッド距離]）
from scipy.cluster.hierarchy import linkage,dendrogram,fcluster

linkage_result = linkage(df, method = "ward", metric = "euclidean")
linkage_result
```

<details><summary>出力結果</summary>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/c246b7b6-7545-6d62-82d1-d9d29fd1e9dd.png)

</details>
　
以下コードの実行により、デンドログラムが描画できます。

```:デンドログラム（樹形図）描画
#　デンドログラムの描画
dendrogram(linkage_result, labels = df.index)

plt.show()
```

描けました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1275001/cd138e82-25eb-7bf5-68ef-01bbd78b2c2e.png)


***
### 最後に
Pythonで、データを読込み、グラフ表示・・・くらいのことしかできなかった私に希望を与えてくれたのは、まちがいなくデータサイエンス塾!!さんです。
マダマダ自力で何とかできるレベルではないことは自覚していますが、なんかできそうな気にさせていただいた。
あたらめて感謝いたします。

***
### 参考サイト

https://www.youtube.com/c/nishimaki

https://www.sejuku.net/blog/60630

https://chayarokurokuro.hatenablog.com/entry/2021/07/02/064751