NBA選手のクラスタリング

#やること
・アメリカのプロバスケットボールリーグNBAのプレイヤーデータをスクレイピングして
k-meansを使ってプレイスタイル別に選手をクラスタリングする。
・クラスタリングしたデータをPCAで次元削減して可視化した後、コサイン類似度で選手同士の類似度を求める。
#動機
動機としては[こちら](https://towardsdatascience.com/clustering-nba-playstyles-using-machine-learning-8c7e8e23c90c)の方の記事を見て再現してみたいと思ったからです。
内容としては、チームのエースKevin Durant選手がトレードされ、代わりに入ってきたD’Angelo Russell選手がチームにどれくらいフィットするかの指標としてプレイヤーをプレイスタイル別にクラスタリングし、トレードされたプレイヤーと同じクラスタに所属しているプレイヤーならチームの中で機能するのではないかというものです。
#データ
NBAには大量のデータを提供している[サイト](https://stats.nba.com/)があり、こちらでSeleniumを使って2018-19期のプレイヤー約500人の中から出場試合が40以上、出場時間1000分以上を満たした272人の選手の中から下記のデータをスクレイピングします。
下記の項目一覧の中で得点や精度に関するデータがないことによってプレイスタイル別にクラスタリングしやすくなっているのかなと思います。
(いくつかの項目には最大値で割って正規化？しています)

※使用した項目一覧
![1_8eeOvnU294Su68vMmdKB2g.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325334/6f97b3cc-14a5-f373-86d1-ff842b971d99.png)
引用元　[https://miro.medium.com/max/2464/1*8eeOvnU294Su68vMmdKB2g.png](https://miro.medium.com/max/2464/1*8eeOvnU294Su68vMmdKB2g.png)

#クラスタリング
これらのデータを使ってsklearnのk-meansでクラスタリングしていきます。
参考記事ではクラスターの数は10となっていましたが、私の場合14クラスターの結果が一番良いと思ったのでクラスターの数は14にしました。
playersはDataframeで選手のIDや名前を除いています。
また、可視化したいのでPCAも準備しておきます。

```python:
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
from IPython.display import display
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
%matplotlib inline

kmeans_model = KMeans(n_clusters=14, random_state=1).fit(players.iloc[:, 3:])
labels = kmeans_model.labels_
#主成分分析の実行
pca = PCA()
pca.fit(players.iloc[:, 3:])
feature = pca.transform(players.iloc[:, 3:])
color_codes = {0:'#00FF00', 1:'#FF0000', 2:'#0000FF',3:'#FD7E00',4:'#000000',5:"#008b8b",6:"#ff00ff",7:"#800000",8:"#fa8072",9:"#ffdab9",10:'#66cdaa',11:"#ffff00",12:"#a9a9a9",13:"#6a5acd"}
colors = [color_codes[x] for x in labels]
plt.figure(figsize=(6, 6))
for x, y, name in zip(feature[:, 0], feature[:, 1], ""):
    plt.text(x, y, name, alpha=0.8, size=10)
plt.scatter(feature[:, 0], feature[:, 1], alpha=0.8, color=colors)
plt.title("Principal Component Analysis")
plt.xlabel("The first principal component score")
plt.ylabel("The second principal component score")
plt.show()
```
![cluster.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325334/7f464e06-001a-f0bf-79ab-a8e986cc4d09.png)
評価が難しいですが、個人的には結構良いのでは？と思います。。。

#検証
本題ですが、Kevin Durant選手とD’Angelo Russell選手が同じクラスタにいるかどうかを知りたいです。なので

```python:
players["label"] = labels
#Kevin Durantのクラスタのラベル
kd_label = players[players.PLAYER_NAME=="Kevin Durant"].label.values[0]
display(players[players.label==kd_label])
```
<img width="826" alt="1c9d1070bcb43864f15530df7f3872d5.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325334/092a82ed-cf71-84a1-92dc-c278802674ca.png">
同じクラスタに所属していました！！

#コサイン類似度
最後にコサイン類似度でKevin Durantと類似度が一番近い選手をみてみたいと思います。

```python:
def cosine_similarity_matrix (vectors):
    unit_vectors = vectors / np.linalg.norm(vectors, axis=1, keepdims=True)
    return np.matmul(unit_vectors, unit_vectors.T)

players = players.drop("label", axis=1)
np_players = players.values[:,3:]
np_players = np_players.astype(float)
player_names = ['{}'.format(players[i:i+1].PLAYER_NAME.values[0]) for i in range(272)]
matrix = cosine_similarity_matrix(np_players)
df_players = pd.DataFrame(matrix, index=player_names, columns=player_names)
search_name = "Kevin Durant"
result = df_players[search_name].sort_values(ascending=False).drop(index=search_name)
print(result)
```
<img width="255" alt="8e445f4502ffc7248ebc166f7fa9f6cc.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325334/04cdee22-6d5d-bbf7-a34b-14b0ef0a00c1.png">
Khris Middletonという選手がもっとも類似度が高いという結果になりました。
正直、私は知りませんでしたがYouTubeで見てみるとだいぶプレイスタイルが似ていました。
あながち精度は悪くないかもしれません。
#最後に
自分の好きなものでデータを分析してみると楽しいものですね。
来年の八村塁選手が誰と類似度が高いのか楽しみです。