名将と呼ばれた者達のデータを使って、データサイエンスを学んでみましょう!
生きたゲームデータ&整えられたデータは中々公開されていないので、いろいろするのにおすすめかもしれない。(Kaggle等除く)
ゲームデータ(試合結果)
野手データ 20190204.csv
投手データ 20190204.csv
コマンドプロンプトを開いて、まずは基本的なパッケージをインポートしておきましょう。
import os, sys, csv import pandas as pd import numpy as np import matplotlib.pyplot as plt
次に、ゲームデータがあるディレクトリ直下まで行って、データを読み込ませておきます。
dfB = pd.read_csv('meisyo_b_20190204.csv', encoding='utf-16') dfP = pd.read_csv('meisyo_p_20190204.csv', encoding='utf-16')
* 文字化け防止のためcsvはutf-16で出力しています。
とりあえず状況把握のため、info()を表示。
dfB.info() dfP.info()
野手(dfB)のデータをメインで見ていきましょう。
Lvのデータをグラフ化しましょう。
plt.hist(dfB['lv'], bins=100) plt.show()
* bins=100にしているのは、Lvは1~100に設定されているからです。
Lv0が多すぎるので、クエリでLv > 10と制限しちゃいましょう。
plt.hist(dfB.query('lv > 10')['lv'], bins=10) plt.show()
「Lv > 10 & Lv <= 100」などSQLみたいに書けるので、応用がいろいろできそうですね。
* 次回以降、plt.show()を省略。
それでは、ミート(b_mt)を解析します。
まずはshapeとmean(平均値)、std(標準偏差)を計算します。
dfB.shape dfB['b_mt'].mean() dfB['b_mt'].std()
(7893, 96) 32.214493855314835 11.483961920286394
7893データ(n = 7893)、96カラムあり。
これだけでは、どんなデータ分布をしているのかいまいちわかりませんね。
正規分布してるかな?ということが気になるので、b_mtをヒストグラムにしてみます。
plt.hist(dfB['b_mt'], bins=30)
右裾が伸びてる感じ、これは正規分布していないですね。
正規分布しているかどうかで、使える統計手法が変わってくるので注意を。
では正規分布であるかどうかの検定を行います。
正規Q-Qプロット
import scipy.stats as stats stats.probplot(dfB['b_mt'], dist="norm", plot=plt) plt.show()
線に乗ってる=正規分布と考えてください。
低い値の範囲が正規分布していないですね。
シャピロウィルク検定
他にも正規分布の検定方法はありますが、簡単なのでこれで。
stats.shapiro(dfB['b_mt'])
C:\Users\saito\AppData\Local\Programs\Python\Python37-32\lib\site-packages\scipy\stats\morestats.py:1653: UserWarning: p-value may not be accurate for N > 5000. warnings.warn("p-value may not be accurate for N > 5000.") (0.9833155870437622, 1.85650483425366e-29)
データがでかすぎたか N>5000はやめてくれって言ってる
検定統計量(W)が0.9833…
p値<0.05のため、正規分布であることが棄却されている(=正規分布ではない)。
歪度と尖度
歪度と尖度が正規分布に近い値か確認すると、
>>> pd.Series(dfB['b_mt']).skew() #歪度 0.364141699620992 >>> pd.Series(dfB['b_mt']).kurtosis() #尖度 -0.3620100293286428
ないですね。正規分布ではない。
その他データ加工方法を試す
stats.probplot(dfB.query('b_mt > 0 & b_mt < 60')['b_mt'], dist="norm", plot=plt)
b_pwも見てみる・・・
plt.hist(dfB.query('b_pw >= 10 & b_pw <= 60')['b_pw'], bins=50)
検定してみよう!
>>> stats.shapiro(dfB['b_pw']) (0.9832108020782471, 1.5380268896554336e-29)
だめですね!正規分布ではない。(‘A`)ンアー
なぜ正規分布していないか
結論から言うと2点理由があり、
1点目が「投手と野手では計算式が違うから」
2点目が「値が低い選手は削除されるから」です。
1点目については、ミートもパワーも高い投手が出てきたら野手要らなくね?ってことで、
「野手能力は野手>投手」、「投手能力は投手>野手」になるように制限をかけています。
それは下記のグラフで見えます。
2点目については、プレイヤー側の都合ですね。
弱い選手は淘汰される。
そのため、正規Q-Qプロットで低い値の範囲が正規分布していなかったことが予想されます。
さて、実際にどう分布してるのか?というのを見ていきましょう。
plt.hist(dfB.query('type == "P"')['b_mt'], bins=10, alpha=0.3, histtype='stepfilled', color='r') # 投手 plt.hist(dfB.query('type == "B"')['b_mt'], bins=10, alpha=0.3, histtype='stepfilled', color='b') # 野手 plt.hist(dfB['b_mt'], bins=10, alpha=0.3, histtype='stepfilled', color='g') # 全体
透明にして、緑=全体、青=野手、赤=投手で表示しています。
んーわかりやすい。
とはいえ、こういった手法を深く進めないと効果的な洞察は得られません。
マン・ホイットニーのU検定(中央値の検定)では、投手群と野手群ではやはり中央値は異なると出る。
stats.mannwhitneyu(dfB.query('type == "P"')['b_mt'], dfB.query('type == "B"')['b_mt']) MannwhitneyuResult(statistic=3865093.0, pvalue=4.898308872342808e-180)
その他の手法を試してみよう。
one-way ANOVA(1元配置分散分析)では、それぞれの集団が正規分布であることを前提にする検定。
平均値が同じであるかどうか?を分散の違いで検定している。
t検定を3群に対してそれぞれ行う(A=B、A=C、B=CだからOK!)という暴挙はしてはならない。(戒め)
まずは、3群(全体、野手群、投手群)が正規分布であるかどうかの検定を行う。
>>> stats.shapiro(dfB['b_mt']) (0.9833155870437622, 1.85650483425366e-29) >>> stats.shapiro(dfB.query('type == "P"')['b_mt']) (0.984963059425354, 4.804016817054439e-15) >>> stats.shapiro(dfB.query('type == "B"')['b_mt']) (0.9849120378494263, 6.929926115617653e-24)
全然正規分布ではない・・・。
今回は3群とも正規分布ではないので使い方がおかしいのはわかっているが、one-way ANOVAに入れてみる。
>>> stats.f_oneway(dfB['b_mt'],dfB.query('type == "P"')['b_mt'],dfB.query('type == "B"')['b_mt']) F_onewayResult(statistic=447.17232845769377, pvalue=1.25435911612848e-189)
うん、そうだね。一致するわけがない。
正規性を検定するシャピロウィルク検定(stats.shapiro())で、
標準正規分布を生成するnumpy.random.randn()を検定してみる。
(あまりにもp値が低いので不安になってきた)
>>> for i in range(1, 10): ... temp = np.random.randn(10 ** i) ... print("10^" + str(i) + ":" + str(stats.shapiro(temp))) ... 10^1:(0.9447452425956726, 0.6069143414497375) 10^2:(0.994074285030365, 0.9436165690422058) 10^3:(0.9983760118484497, 0.4763585329055786) 10^4:(0.9998458623886108, 0.7608375549316406) 10^5:(0.9999829530715942, 0.9663644433021545) 10^6:(0.999995768070221, 0.9852367043495178) 10^7:(0.9999997615814209, 1.0) Traceback (most recent call last): File "<stdin>", line 3, in <module> File "C:\Users\saito\AppData\Local\Programs\Python\Python37-32\lib\site-packages\scipy\stats\morestats.py", line 1644, in shapiro a = zeros(N, 'f') MemoryError
10^n個の標準正規分布を作らせて検定してみた。
数が多くなればなるほど、p値は1.0に近づく。
ただし、統計学では「正規分布していることを棄却できない(判定を保留している)」だけであって、積極的に「正規分布している」とは言えないことに注意が必要。
なぜなら有意水準を0.05に置いてるということは、5%の間違いを許容して予測しているわけだし。
考えることはいっぱいありますね。
つづく。