面白いデータを探して

適当に書く。間違えていたら教えてください。

プロ野球についての考察と分析① チームの実力と相性

私の趣味は野球観戦です。
私の贔屓のチームは特定の球団にめちゃくちゃ強いです。一方で、特定の球団にはボッコボコにされていたりします。
そんな球団の「実力」とか「相性」みたいなものを分析してみたいなーという話。
正直、ちゃんとデータを集めたわけでもないですし、仮定も雑ですがなんとなく納得できるような結果が出たので書いてみます。

分析結果

数式の証明とか読むのだるい人もいると思ったので、今回は分析をした結果から書きます。交流戦はサンプル数が少ないので省いています。
それぞれの数値がなにを表すかは次節以降を見てもらえればと思います。

セ・リーグ 2019年8/31までの結果

各球団の実力を表す数値は下です。大きいほど強いと思われる球団です。

巨人 DeNA 広島 阪神 中日 ヤクルト
1.1621 1.0429 1.0946 1.0124 0.8588 0.7768

巨人がやや抜けて強く、広島、DeNA阪神と続きます。中日とヤクルトは上4つと比べるとやや劣ります。
広島DeNAが実際の順位と逆になっているのは交流戦の影響であると考えられます。広島の交流戦の成績がちょっとひどすぎる感じはありました。

次に相性を表す数値です。

巨人 DeNA 広島 阪神 中日 ヤクルト
巨人 1.0 0.8886 0.7725 1.2267 1.1996 1.0013
DeNA 1.1002 1.0 1.0239 0.6553 1.1541 1.0207
広島 1.1847 0.9755 1.0 0.9101 1.0777 0.9267
阪神 0.7042 1.2536 1.0824 1.0 0.5878 1.1850
中日 0.7493 0.8175 0.9157 1.2868 1.0 1.0400
ヤクルト 0.9986 0.9788 1.0683 0.7722 0.9582 1.0

なんとなく印象に合う気がします。
特に、巨人広島、巨人阪神DeNA阪神などはネットで言われているイメージと一致します。
個人的に意外なのは阪神ヤクルトですかね。

パ・リーグ 2019年8/31までの結果

各球団の実力を表す数値は下です。

ソフトバンク 西武 楽天 ロッテ オリックス 日ハム
1.1197 1.0901 1.0149 0.9692 0.8506 0.9303

ソフトバンク西武が拮抗しており、楽天ロッテ日ハムが続きます。オリックスはやや小さい数字になりました。
順位はオリックスのほうが日ハムより上なのですが、交流戦を抜いているためこのような結果になったと思われます。

相性は下です。

ソフトバンク 西武 楽天 ロッテ オリックス 日ハム
ソフトバンク 1.0 1.0296 0.9497 0.4630 1.2088 1.2365
西武 0.9694 1.0 0.7722 1.1826 1.1106 1.0042
楽天 1.0478 1.1849 1.0 0.7801 0.9085 1.0464
ロッテ 1.3372 0.7759 1.1797 1.0 0.6722 0.8735
オリックス 0.7345 0.8758 1.0838 1.2446 1.0 0.8597
日ハム 0.6869 0.9956 0.9512 1.1122 1.1229 1.0

ソフトバンクとロッテが異次元の数字を出しています。
また、ロッテは非常にムラが大きいです。

実際にやったこと

実力と相性による勝率

あるチームaの実力がp_aであるとする。また、あるチームaのあるチームbに対する相性をs_{ab}とする。
このとき、チーム aとチームbが試合をしてaが勝つ確率を
\[
p(a\rightarrow b) = \frac{s_{ab}p_a}{s_{ab}p_a+s_{ba}p_b}
\]
と仮定する。

損失関数

ここでは、実力pの値と相性s_{ab}の値を推定したい。そこで、損失関数を次のように定義してみる。
実際のチームaのチームbに対する勝率をt(a \rightarrow b)とする。ただし、t(a \rightarrow a) = \frac{1}{2} とし、s_{aa} = 1 とする。
このとき、損失関数は
\[
L(p, s|t) = \sum_{(a, b)\in T\times T} \{p(a\rightarrow b) - t(a \rightarrow b)\}^2
\]
とする。ただし、Tはリーグに所属するチームの集合で、\timesは集合の直積。
つまり、推定された勝率と実際の勝率の二乗誤差。

pの更新式

ここで、あるチームxの実力p_xについて偏微分する
\begin{eqnarray}
&&\frac{\partial L}{\partial p_x} \\
&=& \sum_{b\in T} 2\{p(x\rightarrow b) - t(x \rightarrow b)\}\frac{\partial p(x\rightarrow b)}{\partial p_x}+ \sum_{a\in T} 2\{p(a\rightarrow x) - t(a \rightarrow x)\}\frac{\partial p(a\rightarrow x)}{\partial p_x}
\end{eqnarray}
ところで
\[
p(a\rightarrow x) = 1 - p(x\rightarrow a),\ t(a\rightarrow x) = 1 - t(x \rightarrow a)
\]
であり、
\[
\frac{\partial p(a\rightarrow x)}{\partial p_x} = - \frac{\partial p(x\rightarrow a)}{\partial p_x}
\]
であるから、
\begin{eqnarray*}
&&\frac{\partial L}{\partial p_x}\\
&=&\sum_{b\in T} 2\{p(x\rightarrow b) - t(x \rightarrow b)\}\frac{\partial p(x\rightarrow b)}{\partial p_x}+ \sum_{a\in T} 2\{p(x\rightarrow a) - t(x \rightarrow a)\}\frac{\partial p(x\rightarrow a)}{\partial p_x} \\
&=& 4\sum_{b\in T} \{p(x\rightarrow b) - t(x \rightarrow b)\}\frac{\partial p(x\rightarrow b)}{\partial p_x}
\end{eqnarray*}
ここで、
\[
\frac{\partial p(x\rightarrow b)}{\partial p_x} = \frac{s_{xb}(s_{xb}*p_x+s_{bx}p_b) - s_{xb}s_{xb}p_x}{(s_{xb}p_x+s_{bx}p_b)^2} = \frac{s_{xb}s_{bx}p_b}{(s_{xb}p_x+s_{bx}p_b)^2}
\]
よって、
\begin{eqnarray*}
&&\frac{\partial L}{\partial p_x} \\
&=& 4\sum_{b\in T} \left\{\frac{s_{xb}p_x}{s_{xb}p_a+s_{bx}p_b} - t(x \rightarrow b)\right\}\frac{s_{xb}s_{bx}p_b}{(s_{xb}p_x+s_{bx}p_b)^2} \\
&=& 4\sum_{b\in T} \frac{\{(1- t(x\rightarrow b))s_{xb}p_x- t(x\rightarrow b)p_b\}s_{xb}s_{bx}p_b}{(s_{xb}p_a+s_{bx}p_b)^3}
\end{eqnarray*}
これを踏まえて、pの更新式は
\[
p = p - \alpha\frac{\partial L}{\partial p_x}
\]

sの更新式

損失関数をあるチームabに対して、s_{ab}偏微分する。
s_{ab}が現れる項のみ考えると
\begin{eqnarray}
&& \frac{\partial L}{\partial s_{ab}} \\
&=&2\{p(a\rightarrow b) - t(a \rightarrow b)\}\frac{\partial p(a\rightarrow b)}{\partial s_{ab}}+2\{p(b\rightarrow a) - t(b \rightarrow a)\}\frac{\partial p(b\rightarrow a)}{\partial s_{ab}}
\end{eqnarray}

ここで、上と同様の議論をすると
\[
\frac{\partial L}{\partial s_{ab}} = 4\{p(a\rightarrow b) - t(a \rightarrow b)\}\frac{\partial p(a\rightarrow b)}{\partial s_{ab}}
\]
ここで、
\[
\frac{\partial p(a\rightarrow b)}{\partial s_{ab}} = \frac{p_a(s_{ab}p_a+s_{ba}p_b)-s_{ab}p^2_a}{(s_{ab}p_a+s_{ba}p_b)^2} = \frac{s_{ba}p_bp_a}{(s_{ab}p_a+s_{ba}p_b)^2}
\]
よって、
\begin{eqnarray}
&&\frac{\partial L}{\partial s_{ab}}
\\&=& 4 \left\{\frac{s_{ab}p_a}{s_{ab}p_a+s_{ba}p_b} - t(a \rightarrow b)\right\} \frac{s_{ba}p_bp_a}{(s_{ab}p_a+s_{ba}p_b)^2}
\\&=& 4\frac{\{(1-t(a \rightarrow b))s_{ab}p_a- t(a \rightarrow b)s_{ba}p_b\}s_{ba}p_bp_a}{(s_{ab}p_a+s_{ba}p_b)^3}
\end{eqnarray}
これらを踏まえて
\[
s = s - \alpha\frac{\partial L}{\partial s}
\]
で値を更新する。

コード

実際に分析に使ったコードです。
セリーグについて行ったものをのせておきます。

import numpy as np
import matplotlib.pyplot as plt
# 実際の勝率(セリーグ2019)
t = np.array([[1/2, 9/19, 9/22, 14/21, 13/19, 12/20],
             [1-9/19, 1/2, 11/22, 7/20, 12/19, 14/24],
             [1-9/22, 1-11/22, 1/2, 10/21, 12/20, 11/20],
             [1-14/21, 1-7/20, 1-10/21, 1/2, 7/20, 12/18],
             [1-13/19, 1-12/19, 1-12/20, 1-7/20, 1/2, 12/22],
             [1-12/20, 1-14/24, 1-11/20, 1-12/18, 1-12/22, 1/2]])

# pとsの初期化
s = np.array([[1]*6 for _ in range(6)])
p = np.array([1]*6)

# パラメータ
# 学習率
alpha = 0.2
# 繰り返し回数
iteration = 1000


l = []
for i in range(iteration):
  # pによる偏微分の計算
  partial_p = np.array([sum([(((1-t[x][b])*s[x][b]*p[x] - t[x][b]*p[b]*s[b][x])*s[b][x]*s[x][b]*p[b])/((p[x]+p[b])**3) for b in range(6)]) for x in range(6)])
  # sによる偏微分の計算
  partial_s = np.array([[(((1-t[a][b])*s[a][b]*p[a] - t[a][b]*s[b][a]*p[b])*s[b][a]*p[a]*p[b])/((s[a][b]*p[a]+s[b][a]*p[b])**3) for b in range(6)] for a in range(6)])
  # 現在の損失関数の値
  loss = sum([((s[a][b])*p[a]/(s[a][b]*p[a]+s[b][a]*p[b]) - t[a][b])**2 for a in range(6) for b in range(6)])
  # pの更新
  p = p - alpha*partial_p
  # sの更新
  s = s - alpha*partial_s
  # 損失関数の保存
  l.append(loss)
  # 学習率の減衰(雑です)
  if i%10 == 0 and i != 0:
    alpha *= 0.9
# 損失関数の値の表示
plt.plot(list(range(iteration)), l)
plt.show()

実行結果(loss)

f:id:netres:20190901231314p:plain
縦軸:loss 横軸:epoch

まとめ

NPBの各球団の「実力」と「相性」を測る方法を考えてみました。
それを使って実際に分析をしてみたところ、印象と近い結果が得られたと思います。
今後は交流戦やホームとアウェイの関係なども考慮した分析が出来るようなものを考えてみたいと思います。