Masser’s Blog

データサイエンティスト/Kaggle/Python/ADHD/旅行/サンフレ 等語る予定。

FIFA18のデータからサンフレッチェの選手の価格を予想してみた

データ分析のコンペティションで有名なKaggleはさまざまなデータが公開されています。例えば EA SPORTSからFIFA18のデータが公開されています。そこで今回はこのデータを使用して何か分析をしてみようと思います。(私はFIFA18をしないですが。。。)

https://www.kaggle.com/thec03u5/fifa-18-demo-player-dataset

FIFA18のデータは海外の有名選手やJリーグの選手含めて登録されています。また、データの項目として選手の能力や価格があります。そこでサンフレッチェの選手が他の選手の能力と比較してどのぐらいの価格が妥当か予測してみました。分析するデータはFIFA18に登録されているデータを元にしてますので、結果が悪くてもEA SPORTSに言ってくださいw。また、FIFA18といいつつも選手のデータは2017年のデータですので今年度のデータはありません。

FIFA18に登録されているサンフレッチェ広島の価格について

フィールドプレーヤーはごらんのとおりです。単位はEUROです。DF陣が低いですね。そして、皆川が思ったより高いのが

name eur_value
4689 Felipe Silva 2500000
4877 T. Aoyama 1500000
5053 Y. Kashiwa 1700000
5397 Patric 1800000
5436 K. Shibasaki 1400000
5965 Anderson Lopes 1400000
6269 D. Niwa 700000
6477 M. Mikić 350000
7871 M. Kudo 900000
8072 K. Chiba 450000
8961 H. Mizumoto 425000
10110 K. Morisaki 70000
10651 T. Miyayoshi 725000
10877 N. Burns 550000
11074 S. Inagaki 575000
12756 K. Mukuhara 300000
13090 Y. Chajima 375000
13429 Y. Minagawa 375000
14240 T. Morishima 375000
14859 T. Marutani 190000
15740 S. Sasaki 110000
15769 Y. Nogami 130000
16312 S. Takahashi 140000
16706 O. Iyoha 180000
17938 T. Matsumoto 60000

GKはこちらの通りです。

name eur_value
10100 T. Hayashi 220000
14571 R. Hironaga 160000
15826 H. Nakabayashi 80000

データの解析

まず、データを読み込みます。気を付けたい点は文字コードUTF-8で読み込むことです。そうしないとクロアチア人のデータが読み込めません。

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from scipy.stats import norm
from sklearn.preprocessing import StandardScaler
from scipy import stats
%matplotlib inline
base = pd.read_csv('complete.csv', encoding='utf_8')

サンフレとそれ以外のデータに分けます。また、フィールドプレーヤーとGKに分けます。

base2 = base.copy()
sanf_base = base2[base2['club'].isin(['Sanfrecce Hiroshima'])]
base3 = base2[(base2['club'] != 'Sanfrecce Hiroshima')]
sanf_f = sanf_base[sanf_base['gk'].isnull()]
sanf_g = sanf_base[sanf_base['cf'].isnull()]
base_f = base3[base3['gk'].isnull()]
base_g = base3[base3['cf'].isnull()]

サンフレの選手はこんな感じです。

ID name full_name club special age league
4689 192982 Felipe Silva Felipe de Oliveira Silva Sanfrecce Hiroshima 1659 27 Japanese J1 League
4877 222988 T. Aoyama Toshihiro Aoyama Sanfrecce Hiroshima 1879 31 Japanese J1 League
5053 232512 Y. Kashiwa Yoshifumi Kashiwa Sanfrecce Hiroshima 1714 29 Japanese J1 League
5397 202401 Patric Anderson Patric Aguiar Oliveira Sanfrecce Hiroshima 1670 29 Japanese J1 League
5436 232619 K. Shibasaki Kosei Shibasaki Sanfrecce Hiroshima 1812 32 Japanese J1 League
5965 229949 Anderson Lopes Anderson José Lopes de Souza Sanfrecce Hiroshima 1722 23 Japanese J1 League
6269 232598 D. Niwa Daiki Niwa Sanfrecce Hiroshima 1459 31 Japanese J1 League
6477 141265 M. Mikić Mihael Mikić Sanfrecce Hiroshima 1792 37 Japanese J1 League
7871 231972 M. Kudo Masato Kudo Sanfrecce Hiroshima 1603 27 Japanese J1 League
8072 162388 K. Chiba Kazuhiko Chiba Sanfrecce Hiroshima 1541 32 Japanese J1 League
8961 197422 H. Mizumoto Hiroki Mizumoto Sanfrecce Hiroshima 1568 31 Japanese J1 League
10110 232509 K. Morisaki Kazuyuki Morisaki Sanfrecce Hiroshima 1557 36 Japanese J1 League
10651 232896 T. Miyayoshi Takumi Miyayoshi Sanfrecce Hiroshima 1522 24 Japanese J1 League
10877 181491 N. Burns Nathan Burns Sanfrecce Hiroshima 1777 29 Japanese J1 League
11074 232991 S. Inagaki Sho Inagaki Sanfrecce Hiroshima 1747 25 Japanese J1 League
12756 237788 K. Mukuhara Kenta Mukuhara Sanfrecce Hiroshima 1478 27 Japanese J1 League
13090 232513 Y. Chajima Yusuke Chajima Sanfrecce Hiroshima 1649 25 Japanese J1 League
13429 232614 Y. Minagawa Yusuke Minagawa Sanfrecce Hiroshima 1400 25 Japanese J1 League
14240 232898 T. Morishima Tsukasa Morishima Sanfrecce Hiroshima 1453 20 Japanese J1 League
14859 232613 T. Marutani Takuya Marutani Sanfrecce Hiroshima 1509 28 Japanese J1 League
15740 232511 S. Sasaki Sho Sasaki Sanfrecce Hiroshima 1402 27 Japanese J1 League
15769 235089 Y. Nogami Yuki Nogami Sanfrecce Hiroshima 1341 26 Japanese J1 League
16312 232621 S. Takahashi Soya Takahashi Sanfrecce Hiroshima 1458 21 Japanese J1 League
16706 236761 O. Iyoha Osamu Henry Iyoha Sanfrecce Hiroshima 1306 19 Japanese J1 League
17938 237535 T. Matsumoto Taishi Matsumoto Sanfrecce Hiroshima 1345 18 Japanese J1 League

ちなみに他のデータはこんな感じです。なぜメッシよりR.ロナウドが上なのか分りません。

ID name full_name club special age
0 20801 Cristiano Ronaldo C. Ronaldo dos Santos Aveiro Real Madrid CF 2228 32 Spanish Primera División
1 158023 L. Messi Lionel Messi FC Barcelona 2158 30 Spanish Primera División
2 190871 Neymar Neymar da Silva Santos Jr. Paris Saint-Germain 2100 25 French Ligue 1
3 176580 L. Suárez Luis Suárez FC Barcelona 2291 30 Spanish Primera División
5 188545 R. Lewandowski Robert Lewandowski FC Bayern Munich 2146 28 German Bundesliga

選手の価格帯のを調べます。明らかに正規分布にはなってないですね。。こちらはフィールドプレーヤーですがGKも同じような分布になりました。

sns.distplot(base_f['eur_value']);

f:id:masser199:20180308172459p:plain

次に欠損値について調べます。フィールドプレーヤーで調べたのでGKが100%となっています。eur_release_clauseは今回使用しないので列ごと削除します。league、clubのデータが0ですが、これは未所属であり価格が0であり、データ分析に有用とはならないので削除します。また価格が0の選手もすべて削除します。

base_f_na = (base_f.isnull().sum() / len(base_f)) * 100
base_f_na = base_f_na.drop(base_f_na[base_f_na == 0].index).sort_values(ascending=False)[:30]
missing_data = pd.DataFrame({'Missing Ratio' :base_f_na})
missing_data.head(20)
Missing Ratio
gk 100.000000
eur_release_clause 8.590419
league 1.348131
club 1.348131

分析用にデータを分けます。

base_f =base_f.drop(['eur_release_clause','gk'], axis=1)
base_f = base_f.dropna(how='any')
base_f = base_f.drop(base_f[(base_f['eur_value']==0)].index)
base_g = base_g.drop(base_g[(base_g['eur_value']==0)].index)                          
base_g =base_g.drop(['eur_release_clause','rs','rw','rf','ram','rcm','rm','rdm','rcb','rb','rwb','st','lw','cf','cam','cm','lm','cdm','cb','lb','lwb','ls','lf','lam','lcm','ldm','lcb'], axis=1)
base_g = base_g.dropna(how='any')
train_f_ID =base_f.loc[:,['ID','name','full_name']]
train_g_ID =base_g.loc[:,['ID','name','full_name']]
test_f_ID =sanf_f.loc[:,['ID','name','full_name']]
test_g_ID =sanf_g.loc[:,['ID','name','full_name']]
y_f_train = base_f['eur_value']
y_g_train = base_g['eur_value']
y_f_test = sanf_f['eur_value']
y_g_test = sanf_g['eur_value']
X_f_train =base_f.drop(['ID','name','full_name','eur_value','eur_wage'], axis=1)
X_g_train =base_g.drop(['ID','name','full_name','eur_value','eur_wage'], axis=1)
X_f_test =sanf_f.drop(['ID','name','full_name','eur_value','eur_wage','eur_release_clause','gk'], axis=1)
X_g_test =sanf_g.drop(['ID','name','full_name','eur_value','eur_wage','eur_release_clause','rs','rw','rf','ram','rcm','rm','rdm','rcb','rb','rwb','st','lw','cf','cam','cm','lm','cdm','cb','lb','lwb','ls','lf','lam','lcm','ldm','lcb'], axis=1)

文字列、ブール型になっている値を LabelEncoder使って実数に変換します。

from sklearn.preprocessing import LabelEncoder

for i in range(X_f_train.shape[1]):
    if X_f_train.iloc[:,i].dtypes == object:
        lbl = LabelEncoder()
        lbl.fit(list(X_f_train.iloc[:,i].values) + list(X_f_test.iloc[:,i].values))
        X_f_train.iloc[:,i] = lbl.transform(list(X_f_train.iloc[:,i].values))
        X_f_test.iloc[:,i] = lbl.transform(list(X_f_test.iloc[:,i].values))

for i in range(X_f_train.shape[1]):
    if X_f_train.iloc[:,i].dtypes == bool:
        lbl = LabelEncoder()
        lbl.fit(list(X_f_train.iloc[:,i].values) + list(X_f_test.iloc[:,i].values))
        X_f_train.iloc[:,i] = lbl.transform(list(X_f_train.iloc[:,i].values))
        X_f_test.iloc[:,i] = lbl.transform(list(X_f_test.iloc[:,i].values))

for i in range(X_g_train.shape[1]):
    if X_g_train.iloc[:,i].dtypes == bool:
        lbl = LabelEncoder()
        lbl.fit(list(X_g_train.iloc[:,i].values) + list(X_g_test.iloc[:,i].values))
        X_g_train.iloc[:,i] = lbl.transform(list(X_g_train.iloc[:,i].values))
        X_g_test.iloc[:,i] = lbl.transform(list(X_g_test.iloc[:,i].values))

for i in range(X_g_train.shape[1]):
    if X_g_train.iloc[:,i].dtypes == object:
        lbl = LabelEncoder()
        lbl.fit(list(X_g_train.iloc[:,i].values) + list(X_g_test.iloc[:,i].values))
        X_g_train.iloc[:,i] = lbl.transform(list(X_g_train.iloc[:,i].values))
        X_g_test.iloc[:,i] = lbl.transform(list(X_g_test.iloc[:,i].values))

価格をlogを使用して擬似的に正規分布にします。

y_f_train = np.log(y_f_train)
y_g_train = np.log(y_g_train)

重要な項目について累乗します。

X_f_train['overall2'] = X_f_train['overall'] ** 2
X_f_train['potential2'] = X_f_train['potential'] ** 2
X_f_test['overall2'] = X_f_test['overall'] ** 2
X_f_test['potential2'] = X_f_test['potential'] ** 2
X_g_train['gk2'] = X_g_train['gk'] ** 2
X_g_train['overall2'] = X_g_train['overall'] ** 2
X_g_train['potential2'] = X_g_train['potential'] ** 2
X_g_test['gk2'] = X_g_test['gk'] ** 2
X_g_test['overall2'] = X_g_test['overall'] ** 2
X_g_test['potential2'] = X_g_test['potential'] ** 2

あとはXGBoostとLightGBMを使用して価格を予測します。こちらはフィールドプレーヤーですがGKもおんなじような感じです。

reg = xgb.XGBRegressor(max_depth=2,n_estimators=300,min_child_weight=1,gamma=0.1,subsample=0.2,colsample_bytree=0.4,learning_rate=0.1,reg_alpha=0.0001)
reg.fit(X_f_train, y_f_train)

import lightgbm as lgb
lgb_train = lgb.Dataset(X_f_train, y_f_train)
lgb_eval  = lgb.Dataset(X_f_test, y_f_test, reference=lgb_train)
params = {
    'task': 'train',
    'boosting_type': 'gbdt',
    'objective': 'regression',
    'metric': {'l2'},
    'num_leaves': 128,
    'learning_rate': 0.01,
    'num_iterations':100,
    'feature_fraction': 0.38,
    'bagging_fraction': 0.68,
    'bagging_freq': 5,
    'verbose': 0
}


gbm = lgb.train(params,
                lgb_train,
                num_boost_round=1500,
                valid_sets=lgb_eval,
                early_stopping_rounds=15)

 
X_f_train2 = pd.DataFrame( {'XGB': reg.predict(X_f_train),
     'LGB': gbm.predict(np.array(X_f_train), num_iteration=gbm.best_iteration),
    })

from sklearn import linear_model

reg_all = linear_model.LinearRegression()
reg_all.fit(X_f_train2, y_f_train)

X_f_test2 = pd.DataFrame( {'XGB': reg.predict(X_f_test),
     'LGB': gbm.predict(np.array(X_f_test), num_iteration=gbm.best_iteration),
    })

predictions = np.exp(reg_all.predict(X_f_test2))
 
submission = pd.DataFrame({ 'ID': test_f_ID['ID'],
                           'name' : test_f_ID['name'],
                           'full_name' : test_f_ID['full_name'],
                            'eur_value<200b>' : predictions})
submission

結果はこのようになりました。皆川が高いのが違和感あるなあ。

ID eur_value? full_name name
0 192982 2376986 Felipe de Oliveira Silva Felipe Silva
1 222988 1324306 Toshihiro Aoyama T. Aoyama
2 232512 1634586 Yoshifumi Kashiwa Y. Kashiwa
3 202401 1693254 Anderson Patric Aguiar Oliveira Patric
4 232619 1366923 Kosei Shibasaki K. Shibasaki
5 229949 1548074 Anderson Jos? Lopes de Souza Anderson Lopes
6 232598 685995 Daiki Niwa D. Niwa
7 141265 329592 Mihael Miki? M. Miki?
8 231972 842412 Masato Kudo M. Kudo
9 162388 463991 Kazuhiko Chiba K. Chiba
10 197422 439531 Hiroki Mizumoto H. Mizumoto
11 232509 99654 Kazuyuki Morisaki K. Morisaki
12 232896 694564 Takumi Miyayoshi T. Miyayoshi
13 181491 537812 Nathan Burns N. Burns
14 232991 641852 Sho Inagaki S. Inagaki
15 237788 311987 Kenta Mukuhara K. Mukuhara
16 232513 356286 Yusuke Chajima Y. Chajima
17 232614 374843 Yusuke Minagawa Y. Minagawa
18 232898 367593 Tsukasa Morishima T. Morishima
19 232613 182939 Takuya Marutani T. Marutani
20 232511 108015 Sho Sasaki S. Sasaki
21 235089 131221 Yuki Nogami Y. Nogami
22 232621 152696 Soya Takahashi S. Takahashi
23 236761 198621 Osamu Henry Iyoha O. Iyoha
24 237535 65290 Taishi Matsumoto T. Matsumoto

GKはこのような結果となりました。廣永のほうが高いのが意外ですね。

ID eur_value full_name name
10100 232505 233405 Takuto Hayashi T. Hayashi
14571 232507 169154 Ryotaro Hironaga R. Hironaga
15826 237426 78575 Hirotsugu Nakabayashi H. Nakabayashi