logistic regressionを使ってみる。classifyとprobabilityを出すあたりまで。
野球で7回までの得点を入れると、勝敗の確率が出るようなものでも作ってみる。
こちらのMLBのデータを利用
http://www.retrosheet.org/gamelogs/index.html
上記ページの2013年データを利用した。MLBはデータが落とせるサイトがいろいろあってありがたい。
sklearnは、データ入れて、fitして、出来上がった分類器でpredictするだけで動く。簡単で便利。
落としてきたファイルを適当に変換して、下記のような形式にする。
0,7,2 0,1,0 1,2,3 1,2,5 1,0,2
左から、勝ち負け(0がホーム勝ち、1がビジター勝ち), ホームチームの7回までの得点, ビジターチームの7回までの得点。使うのはこの3つの数値だけ。引き分けは無視。
コードは下記。
# -*- coding: utf-8 -*- import numpy as np import pylab as pl import csv from sklearn.linear_model import LogisticRegression # 元ファイルを適当に整形しておく with open('GL2013.TXT') as f: with open('GL2013.TXT.tmp', 'wb') as writer: reader = csv.reader( f, delimiter=',' ) for line in reader: toint = lambda d : int(d) if d.isdigit() else 0 visitor7 = sum( map( toint, list( line[19] )[0:7] ) ) visitor = sum( map( toint, list( line[19] ) ) ) home7 = sum( map( toint, list( line[20] )[0:7] ) ) home = sum( map( toint, list( line[20] ) ) ) # home勝ち=0, visitor勝ち=1 if home != visitor: win = 0 if home > visitor else 1 writer.write( "{0},{1},{2}\n".format(win, home7, visitor7 ) ) # 整形したデータを読み込む data = np.genfromtxt( 'GL2013.TXT.tmp', delimiter=',', dtype=np.uint8 ) cls = data[:, 0] features = data[:, 1:3] # l2 logistict legression で fit する classifier = LogisticRegression(C=1.0, penalty='l2') classifier.fit( features, cls ) # 確率を確認 print( "0-0 : {0}".format( classifier.predict_proba( [0, 0] ) ) ) print( "3-2 : {0}".format( classifier.predict_proba( [3, 2] ) ) ) print( "2-3 : {0}".format( classifier.predict_proba( [2, 3] ) ) ) print( "1-9 : {0}".format( classifier.predict_proba( [1, 9] ) ) ) print( "3-3 : {0}".format( classifier.predict_proba( [3, 3] ) ) ) print( "4-3 : {0}".format( classifier.predict_proba( [4, 3] ) ) )
整形後の処理は、classifier用意して、fitして、predictしてるだけ。実に簡単。
これを実行すると、下記のような結果になる。
0-0 : [[ 0.52235099 0.47764901]] 3-2 : [[ 0.75421393 0.24578607]] 2-3 : [[ 0.28452784 0.71547216]] 1-9 : [[ 3.14671968e-04 9.99685328e-01]] 3-3 : [[ 0.52537046 0.47462954]] 4-3 : [[ 0.75496118 0.24503882]]
0-0や3-3などの同点の際はほぼ互角だけど若干ホーム寄り。1-9とかだとほぼ試合終了。3-2や4-3のような1点差で7回を終えた場合は、ホームチームは7割5分勝てる。ビジター(2-3)は7割1分5厘と少し差がある。全体的にホーム補正が存在するのか、それとも適用するデータを増やすとなだからになるのか。
ちなみに対象データでは、ホームチームの勝率は5割3分7厘だった。
hist = np.histogram( cls, bins=[0, 1, 2] )[0] win_home = hist[0] win_visi = hist[1] win_home = float(win_home + win_visi)
0.537638831756
fitした結果がどうなっているのかも見てみる。coef_とintercept_で見れるらしい。
classifier.coef_ classifier.intercept_
結果
[[-1.02368064 1.01964548]] [-0.0894636]
試しにて入力で計算してみる。
>>> x = -1.02368064 >>> y = 1.01964548 >>> z = -0.0894636 >>> 3 * x + 4 * y + z 0.9180764000000006 >>> 4 * x + 4 * y + z -0.10560423999999971
これで分類できるのはわかるけど、probabilityはどうやって出せばいいのか。scikit-learnのソースコード(githubにいる)を見たら、1. / (1. + np.exp(-self.decision_function(X))) と書いてあった。
ということは、こんなコードで良いわけか。
def prob( h, v ): prob = h * -1.02368064 + v * 1.01964548 - 0.0894636 print( "{0}-{1} : {2}".format( h, v, 1. / (1. + np.exp(-prob) ) ) ) prob( 0, 0 ) prob( 3, 2 ) prob( 2, 3 )
実行してみたところ、predict_probaを使った時と同じ結果になった。
0-0 : 0.477649005633 3-2 : 0.245786064646 2-3 : 0.715472155171
save/loadのコードも書いておく。compressを付けないとファイルがたくさん出力されたりする。
# 保存 from sklearn.externals import joblib joblib.dump(classifier, 'mlb_stats.pkl', compress=9) # ロード clf = joblib.load('mlb_stats.pkl') print( "0-0 : {0}".format( clf.predict_proba( [0, 0] ) ) )