GIS奮闘記

元GISエンジニアの技術紹介ブログ。主にPythonを使用。

スポンサーリンク

FIFA 20 のプレイヤーをクラスタリングしてみよう!

さて、本日は機械学習のクラスタリングという手法を使ってFIFA 20 のプレイヤーをグルーピングしてみようと思います。

FIFA20 とは

『FIFA 20』は、EAスポーツが開発し、エレクトロニック・アーツより2019年9月27日に世界同時発売されたサッカーゲーム。(出典:Wikipedia)

使用するデータデータ

Kaggle の「FIFA 20 complete player dataset」にある「players_20.csv」を使用します。

www.kaggle.com

Kaggle とは

Kaggleは企業や研究者がデータを投稿し、世界中の統計家やデータ分析家がその最適モデルを競い合う、予測モデリング及び分析手法関連プラットフォーム及びその運営会社である。 (出典:Wikipedia)

クラスタリングとは

(統計学)データ解析手法の1つ。「クラスタ解析」、「クラスター分析」とも。機械学習やデータマイニング、パターン認識、イメージ解析やバイオインフォマティックスなど多くの分野で用いられる(データ・クラスタリングを参照)。クラスタリングではデータの集合を部分集合(クラスタ)に切り分けて、それぞれの部分集合に含まれるデータが(理想的には)ある共通の特徴を持つようにする。この特徴は多くの場合、類似性や、ある定められた距離尺度に基づく近さで示される。(出典:Wikipedia)

使用するクラスタリング手法

k-meansクラスタリングを使用します。

関連エントリー

機械学習関連のエントリーです。興味のある方はぜひ読んでみてください。

www.gis-py.com

www.gis-py.com

www.gis-py.com

環境

Windows10 64bit
Python3.8.5

手順

  1. データ分析
  2. クラスタリング
  3. 可視化

1.データ分析

データ読込

プレイヤーのデータを読み込みます。

import pandas as pd
data = pd.read_csv(r"players_20.csv")
data.head()

なんと104カラムもありますね。

f:id:sanvarie:20210725054512p:plain

データ探索

カラムの型や基本統計量などを確認します。

data.shape

data.info()

data.describe()

f:id:sanvarie:20210725055033p:plain

カラム選定

クラスタリングを行うにあたって必要なカラムを選択します。また、フィールドプレイヤーとゴールキーパーによって使用するカラムを分けます。

################################################
# フィールドプレイヤーの分析に必要なカラムをセット #
################################################

data_field_players = data[data.player_positions != "GK"]
field_players_features = ['short_name','overall','potential', 'skill_moves',
                          'pace','shooting','passing','dribbling','defending','physic']

# 名前ありデータフレーム
data_field_players = data_field_players[field_players_features]                       

# 名前なしデータフレーム
data_field_players_drop = data_field_players.drop(columns = ['short_name'])

################################################
# ゴールキーパーの分析に必要なカラムをセット       #
################################################
data_goal_keepers = data[data.player_positions == "GK"]
goal_keepers_features = ['short_name','overall','potential', 'skill_moves',
                         'gk_diving','gk_handling','gk_kicking','gk_reflexes','gk_speed','gk_positioning']

# 名前ありデータフレーム
data_goal_keepers = data_goal_keepers[goal_keepers_features]

# 名前なしデータフレーム
data_goal_keepers_drop = data_goal_keepers.drop(columns = ['short_name'])

2.クラスタリング

まずはスケーリングをします。

スケーリング(フィールドプレイヤー)
# スケーリング(フィールドプレイヤー)
from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler()
scaler.fit(data_field_players_drop)

data_field_players_drop_scaled = scaler.transform(data_field_players_drop)

field_players_df = pd.DataFrame(data_field_players_drop_scaled)

field_players_features.remove('short_name')

field_players_df.columns = field_players_features
field_players_df

f:id:sanvarie:20210725084637p:plain

スケーリング(ゴールキーパー)
# スケーリング(ゴールキーパー)
from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler()
scaler.fit(data_goal_keepers_drop)

data_goal_keepers_drop_scaled = scaler.transform(data_goal_keepers_drop)

goal_keepers_df = pd.DataFrame(data_goal_keepers_drop_scaled)

goal_keepers_features.remove('short_name')

goal_keepers_df.columns = field_players_features
goal_keepers_df

f:id:sanvarie:20210725084703p:plain

エルボー法(フィールドプレイヤー)

エルボー法で最適なクラスター数を検討します。

import matplotlib.pyplot as plt
distortions = []
for i  in range(1,11):                
    km = KMeans(n_clusters=i,
                init='k-means++',     
                n_init=10,
                max_iter=16242,
                random_state=0)
    km.fit(field_players_df)                         
    distortions.append(km.inertia_)   

plt.plot(range(1,11),distortions,marker='o')
plt.xlabel('Number of clusters')
plt.ylabel('SSE')
plt.show()

「クラスター数3が最適なクラスター数」と判断します。

f:id:sanvarie:20210725084352p:plain

エルボー法(ゴールキーパー)
import matplotlib.pyplot as plt
distortions = []
for i  in range(1,11):                
    km = KMeans(n_clusters=i,
                init='k-means++',     
                n_init=10,
                max_iter=2036,
                random_state=0)
    km.fit(data_goal_keepers_drop)                         
    distortions.append(km.inertia_)   

plt.plot(range(1,11),distortions,marker='o')
plt.xlabel('Number of clusters')
plt.ylabel('Distortion')
plt.show()

こちらも「クラスター数3が最適なクラスター数」と判断します。

f:id:sanvarie:20210725084413p:plain

さて、いよいよクラスタリングですね。

クラスタリング(フィールドプレイヤー)
from sklearn.cluster import KMeans

kmeans = KMeans(n_clusters = 3)
kmeans.fit(field_players_df)
result = kmeans.predict(field_players_df)

# 名前ありデータフレームにクラスタリングの結果を格納
data_field_players['cluster_id'] = result

# 名前なし、かつ、スケーリング済みのデータフレームにクラスタリングの結果を格納
field_players_df['cluster_id'] = result

field_players_df.head()

f:id:sanvarie:20210725065946p:plain

クラスタリング(ゴールキーパー)
from sklearn.cluster import KMeans

kmeans = KMeans(n_clusters = 3)
kmeans.fit(goal_keepers_df)
result = kmeans.predict(goal_keepers_df)

# 名前ありデータフレームにクラスタリングの結果を格納
data_goal_keepers['cluster_id'] = result

# 名前なし、かつ、スケーリング済みのデータフレームにクラスタリングの結果を格納
goal_keepers_df['cluster_id'] = result

goal_keepers_df.head()

f:id:sanvarie:20210725093222p:plain

3.可視化

クラスタリングの結果を可視化します。

フィールドプレイヤー
# 可視化
import matplotlib.pyplot as plt

clusterinfo = pd.DataFrame()
for i in range(3):
    clusterinfo['cluster' + str(i)] = field_players_df[field_players_df['cluster_id'] == i].mean()
clusterinfo = clusterinfo.drop('cluster_id')
 
my_plot = clusterinfo.T.plot(kind='bar', stacked=True, title="Mean Value of 3 Clusters")
my_plot.set_xticklabels(my_plot.xaxis.get_majorticklabels(), rotation=0)
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0, fontsize=18)

結果が出ました。以下のような傾向があることがわかります。

  • cluster = 0 のプレイヤー:全体的に能力が高い。スタープレイヤーと考えられる。
  • cluster = 1 のプレイヤー:スピードとドリブル能力が高い。ウイングのようなタイプと考えられる。
  • cluster = 2 のプレイヤー:フィジカルとディフェンス能力が高い。ディフェンダーと考えられる。

f:id:sanvarie:20210725090022p:plain

それぞれのクラスターに属する選手です。cluster = 0 はメッシやロナウドなどが所属しており、スタープレイヤーのグループという推測はあってそうですね。

f:id:sanvarie:20210725090755p:plain

f:id:sanvarie:20210725090811p:plain

f:id:sanvarie:20210725090827p:plain

ゴールキーパー
# 可視化
import matplotlib.pyplot as plt

clusterinfo = pd.DataFrame()
for i in range(3):
    clusterinfo['cluster' + str(i)] = goal_keepers_df[goal_keepers_df['cluster_id'] == i].mean()
clusterinfo = clusterinfo.drop('cluster_id')
 
my_plot = clusterinfo.T.plot(kind='bar', stacked=True, title="Mean Value of 3 Clusters")
my_plot.set_xticklabels(my_plot.xaxis.get_majorticklabels(), rotation=0)
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0, fontsize=18)

結果が出ました。以下のような傾向があることがわかります。

  • cluster = 0 のプレイヤー:全体的に能力が低い。若手やレベルの低いリーグのキーパーと考えられる。
  • cluster = 1 のプレイヤー:全体的に能力が中くらい。中堅レベルのキーパーと考えられる。
  • cluster = 2 のプレイヤー:全体的に能力が高い。スタープレイヤーと考えられる。

f:id:sanvarie:20210725091224p:plain

それぞれのクラスターに属する選手です。知っている選手がいないのですが、能力を見る限り推測は正しそうな気がします。

f:id:sanvarie:20210725091419p:plain

f:id:sanvarie:20210725091440p:plain

f:id:sanvarie:20210725091456p:plain

さいごに

いかがでしたでしょうか?クラスタリングを使用するとデータセットの中から類似データごとにグループ分けをしてくれます。とても便利ですね。大量のデータの中から何かしらの法則を見つける方法はクラスタリング以外にもありますので、今後はそういったものも紹介していこうと思います。本日は以上です。