2020年Jリーグの登録選手を年齢分析してみたが、外れ値は52歳のキングカズ以外にもいた。

## 概要
* 2020年1月31日に、２０２０明治安田生命Jリーグの登録選手が公開されました。
* 公式サイトやデータサイトから選手リストを抽出し、その生年月日から年齢を算出する。
* そのデータを`matplotlib`の`boxplot`から年齢分布を読み取る。
* その`boxplot`の外れ値(outlier)について調べた。

## データ
* [公式サイト＞例：北海道コンサドーレ札幌](https://www.jleague.jp/club/sapporo/day/#player)
* [データサイト＞選手・監督・審判＞例：北海道コンサドーレ札幌](https://data.j-league.or.jp/SFIX02/search?displayId=SFIX02&selectValue=1&displayId=SFIX02&selectValueTeam=14&displayName=%EF%BC%AA%EF%BC%91%E3%83%AA%E3%83%BC%E3%82%B0&displayNameTeam=%E5%8C%97%E6%B5%B7%E9%81%93%E3%82%B3%E3%83%B3%E3%82%B5%E3%83%89%E3%83%BC%E3%83%AC%E6%9C%AD%E5%B9%8C)
* 今回はスクレイピングの解説は省略します。以下のようなデータ形式で進めます。

![basic_data.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126042/e9f99345-434c-943f-2c9c-97b3b2ba7817.png)
* ローカルデータは以下の項目になります。
![club.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126042/91bb3a44-688e-bdcd-417e-bba4d699c710.png)


## コード
```box_plot.py
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline

# スクレイピングしたデータ
data = pd.read_csv('./csv/all_player_mst_2020.csv', sep=',', encoding='utf-8')

# ''表示順'、'カテゴリーID'、'カテゴリー名'、クラブ名'のローカルファイルを読み込む
club = pd.read_csv('./csv/mst_club_2020.csv', sep=',', encoding='utf-8')

# 生年月日から年齢を算出する関数
def get_age(birthday):
    today    = int(pd.to_datetime('today').strftime('%Y%m%d'))
    birthday = int(pd.to_datetime(birthday).strftime('%Y%m%d'))
    return int((today - birthday) / 10000)

data['年齢'] = data['生年月日'].apply(lambda date: get_age(date))
```
* 生年月日から年齢を算出し、年齢列を追加する。[参考サイト](https://qiita.com/gesutaka/items/42f15ca6f224c290625b)
![basic_data_age.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126042/8c6a50fa-3ff7-08fc-8147-1ec6167e3336.png)

```box_plot.py
# J1,J2,J3の年齢をリスト化する
J1 = club[club['category'] == 'J1']['club_name']

J1_age = {}
for c_name in J1:
    tmp_age = list(data[data['クラブ名'] == c_name]['年齢'])
    J1_age[c_name] = tmp_age
J2 = club[club['category'] == 'J2']['club_name']

J2_age = {}
for c_name in J2:
    tmp_age = list(data[data['クラブ名'] == c_name]['年齢'])
    J2_age[c_name] = tmp_age 
J3 = club[club['category'] == 'J3']['club_name']

J3_age = {}
for c_name in J3:
    tmp_age = list(data[data['クラブ名'] == c_name]['年齢'])
    J3_age[c_name] = tmp_age 
```
各カテゴリーでクラブ単位の年齢データをリスト形式に作成する。

```box_plot.py

fig = plt.figure(figsize=(16,16),dpi=144)
fig.suptitle('2020明治安田生命Jリーグ', x=0.5, y=0.9, fontsize=18)
fig.subplots_adjust(hspace=0.1)

plt.rcParams["font.family"] = "IPAexGothic"

red_circle = dict(markerfacecolor='r', marker='o')

# J1
ax1 = fig.add_subplot(311)
ax1.boxplot(J1_age.values(), labels=J1_age.keys(), vert=False,  flierprops=red_circle)
ax1.invert_yaxis()
ax1.set_ylabel('J1', fontsize=16)
ax1.set_xlim(15,55)

# J2
ax2 = fig.add_subplot(312, sharex=ax1)
ax2.boxplot(J2_age.values(), labels=J2_age.keys(), vert=False,  flierprops=red_circle)
ax2.invert_yaxis()
ax2.set_ylabel('J2', fontsize=16)

# J3
ax3 = fig.add_subplot(313 ,sharex=ax1)
ax3.boxplot(J3_age.values(), labels=J3_age.keys(), vert=False,  flierprops=red_circle)
ax3.invert_yaxis()
ax3.set_ylabel('J3', fontsize=16)

plt.show()

#グラフの画像保存
plt.savefig('./img/2020_jleague_age_boxplot.png')
```
箱ひげ図のポイント
* J1・J2・J3の3つのグラフのタイトル　`suptitle`
* それぞれの上下の間隔を少なくする　`subplots_adjust`
* 外れ値のマーカーをカスタマイズ　`red_circle = dict(markerfacecolor='r', marker='o')`
* クラブ名が日本語なので横向きにする　`boxplot(x, labels=label, vert=False,...)`

<img width="1152" alt="2020_jleague_age_boxplot.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126042/f1c80141-abb9-2cce-cfa1-7a15d2b1311b.png">

## グラフから読み取れる内容
* ああ、やっぱりキングカズ（三浦 知良選手)は外れているよなー！
* 北海道コンサドーレは3つも外れ値があるじゃないか！
* 川崎は中村 憲剛選手だろうね。
* J2,J3とカテゴリーが下がるほど、外れ値が多くないか？
* 外れ値は、どう計算されているの？疑問？

## ここからが本題
>外れ値（はずれち、英: outlier）は、統計学において、他の値から大きく外れた値のこと。測定ミス・記録ミス等に起因する異常値とは概念的には異なるが、実用上は区別できないこともある。
[出典: フリー百科事典『ウィキペディア（Wikipedia）』](https://qiita.com/drafts/adc9a8a4ad6bf03bf5f3/edit)

で、計算方法は？
>工学のためのデータサイエンス入門
フリーな統計環境Rを用いたデータ解析
瀬間 茂・神保 雅一・鎌倉 稔成・金藤 浩司共著
[数理工学社：2004年初版2刷](https://www.saiensu.co.jp/search/?isbn=978-4-901683-12-8&y=2004)

以前買った上記書籍から箱型図（箱ひげ図）の作成について解説があり、この投稿のために作図しました。
 ![box_plot.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126042/69ea44ec-421c-359e-5614-077975701120.jpeg)

## 検証
```box_ploy.py
# 北海道コンサドーレ札幌を対象に検証
sapporo = pd.DataFrame(J1_age[J1[0]])

q1 = sapporo.describe().loc['25%']
q3 = sapporo.describe().loc['75%']
iqr = q3 - q1

outlier_min = q1 - (iqr) * 1.5
outlier_max = q3 + (iqr) * 1.5

outer_min = sapporo[sapporo < outlier_min[0]]
outer_max = sapporo[sapporo > outlier_max[0]]
print( outer_min, outer_max)
```

* [(参考サイト)pandasのデータフレームから外れ値を含む行を取り除く] (https://qiita.com/mizutaku17/items/6bd8e778b6e9ec7b162d)

## 結果
```box_plot.py
     0
0  NaN
1  NaN
2  NaN
3  NaN
4  NaN
5  NaN
6  NaN
7  NaN
8  NaN
9  NaN
10 NaN
11 NaN
12 NaN
13 NaN
14 NaN
15 NaN
16 NaN
17 NaN
18 NaN
19 NaN
20 NaN
21 NaN
22 NaN
23 NaN
24 NaN
25 NaN        0
0   35.0
1    NaN
2    NaN
3   34.0
4    NaN
5    NaN
6    NaN
7    NaN
8    NaN
9    NaN
10   NaN
11   NaN
12   NaN
13   NaN
14   NaN
15   NaN
16   NaN
17  34.0
18   NaN
19   NaN
20   NaN
21   NaN
22   NaN
23   NaN
24   NaN
25  37.0
```
J1のグラフで北海道コンサドーレ札幌は、3つの外れ値がありましたが、実際は同年齢が2名いましたので4名が外れ値です。
実際の外れ値の選手名一覧（一部)

|category|クラブ名|選手名|生年月日|年齢|
|:-------|:------|:----------|:------|--:|
|J1|北海道コンサドーレ札幌|菅野　孝憲|1984/5/3|35|
|J1|北海道コンサドーレ札幌|石川　直樹|1985/9/13|34|
|J1|北海道コンサドーレ札幌|早坂　良太|1985/9/19|34|
|J1|北海道コンサドーレ札幌|ジェイ|1982/5/7|37|
|J1|川崎フロンターレ|中村　憲剛|1980/10/31|39|
|J1|横浜ＦＣ|三浦　知良|1967/2/26|52|
|J2|水戸ホーリーホック|木村　祐志|1987/10/5|32|
|・・・|・・・|・・・|・・・|・・・|

## まとめ
* 外れ値はIQR(3/4四分位数-1/4四分位数)の範囲に影響される。
* 箱ひげ図は知っていたが使ったことなく、作図の深堀りができた。
* テーマ＞データ取得＞データ整形＞可視化＞検証までができた。
* [課題]発表から4日間かかったので、これくらい1日で出来ないといかんと反省！
* 引用の仕方などが適切でない場合、ご指摘ください。
 
## 参考サイト
* [誕生日から年齢を計算する(pandas編)](https://qiita.com/gesutaka/items/42f15ca6f224c290625b)
* [pandasのデータフレームから外れ値を含む行を取り除く](https://qiita.com/mizutaku17/items/6bd8e778b6e9ec7b162d)