RのWeb制作

Webサービス制作のための技術情報を。

Web制作 Python データサイエンス

野球ゲームデータで遊ぶデータサイエンス

投稿日:2019年2月5日 更新日:

名将と呼ばれた者達のデータを使って、データサイエンスを学んでみましょう!

生きたゲームデータ&整えられたデータは中々公開されていないので、いろいろするのにおすすめかもしれない。(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%の間違いを許容して予測しているわけだし。

考えることはいっぱいありますね。
つづく。

-Web制作, Python, データサイエンス

執筆者:


comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

関連記事

[Meisyo]ソースコードから学ぶ試合の基礎設計1

ワンボタンで監督始点の高校野球の試合を楽しめる名将と呼ばれた者達。その試合中には、多くのアルゴリズムが活躍しています。 ここでは、試合の設計を理解することで強いチームを作る指針を記載しておきます。 ソ …

久々にWeb更新

ミニ四駆ブログまとめ http://mini4wd.rei-farms.jp/ とかを更新してました。珍しいですね。 仕事でいろいろとあってるので、疲労感が半端ないです。 上司が壊し屋(多数実績あり) …

[Python] tensorflowが「ImportError: DLL load failed」で困った件について

ImportError: DLL load failed: The specified module could not be found おおん・・・? 今回Tensorflowの新バージョン2.0 …

no image

独自ドメイン取得

ねんがんの どくじどめいんを しゅとくしたぞ!( ^^) 「http://rei-farms.jp/」を独自ドメインとして取得しました。 ブログは「http://rei-farms.jp/webmak …

[Meisyo]ビッグデータから学ぶ試合の基礎設計2

名将と呼ばれた者達、めちゃくちゃソースコードが長い(この記事) ここでは、「どのパラメータが打率等にどれくらい関係するのか」を理解することで強いチームを作る指針を記載しておきます。 というわけで27- …