Edited at

Python × Bradley-Terry's Modelでセ・パ両リーグの戦力値遷移を可視化

More than 3 years have passed since last update.


Bradley-Terry's Model

これは、「強さ」を図るモデルです。

簡単にいうと、試合の結果から各々の「強さ」を数値化できるよっていう概念です。

以下の論文が参考になりましたので、詳しくはこちらをご覧ください。

「強さ」に対する定量的評価法とその応用


n個の要素 (チームや個人) があり、何らかの対戦を行うものとする。

対戦は 1要素 対 1要素 のマッチで行われ、その結果は片方の要素の対して勝利、または敗北しか生じないとする。

何回か対戦した結果から各要素の「強さ」をはかるとする。

ここで要素 i が要素 j に勝利する確率を Pij としたとき、すべての組み合わせに対して、

Pij = πi / πi + πj (1)

となるπi を導入する。

式 (1) の関係式を Bradley-Terry(BT)モデルという。

BT モデルにおいて πi は,要素 i の強さを表すと考えることができる。



参考にしたもの

先日参加した、統計処理及び機械学習に基づくデータマイニング勉強会 #03での講義内容を参考にしています。

ここでBradley-Terry's ModelをPythonで実装したものが紹介されています。

(wsに代入されている数字は、交流戦の成績を除いた2014年度のセ・リーグ6球団の総勝数のリストです。便宜上、引き分けを0.5でカウントしています。)

ここで得られたthetaが各チームの「強さ」の数値です。

import numpy as np

theta = np.ones(6)/6.0 # \theta 初期値
t = np.zeros(6)
r = 24.0 # 対戦数
ws = np.array([66.5, 66.5, 66, 56.5, 55, 50.5]) # 総勝数
for iloop in range(0, 100):
for i in range(0,6):
acc = 0 # 総和用のアキュムレータ
for j in range(0,6):
if (j == i):
pass
else:
acc += r / (theta[i]+theta[j])
t[i] = ws[i]/acc
s = sum(t)
for i in range(0, 6):
theta[i] = t[i] / s


過去5年間の戦力値遷移

上記のBradley-Terry's Modelを参考に、セ・リーグの5年間の移り変わりをmatplotlibでグラフ化してみました。

#!/usr/bin/env python

# -*- coding: utf-8 -*-

import numpy as np
import matplotlib.pyplot as plt
import os

theta = np.ones(6) / 6.0
t = np.zeros(6)
r = 24.0

teams = [u"巨人", u"阪神", u"広島", u"中日", u"横浜", u"ヤクルト"]
colors = ["#f27b00", "#ffff00", "#ff0000", "#002468", "#044a90", "#111c3c"]
scores = [[67.5, 68, 48, 68.5, 42.5, 65.5], [66, 61, 57, 66, 43.5, 66.5], [76.5, 51.5, 55.5, 69, 43, 64.5], [74, 62.5, 58.5, 57.5, 55, 51.5], [66.5, 66.5, 66, 56.5, 55, 50.5]]
years = ["2010", "2011", "2012", "2013", "2014"]
points = [[], [], [], [], [], []]

def reset():
global theta
theta = np.ones(6) / 6.0

def bradley_terry(ws):
for iloop in range(0, 100):
for i in range(0, 6):
acc = 0
for j in range(0, 6):
if i == j:
pass
else:
acc += r / (theta[i] + theta[j])
t[i] = ws[i] / acc
s = sum(t)
for i in range(0, 6):
theta[i] = t[i] / s

if __name__ == '__main__':
for score in scores:
reset()
bradley_terry(score)
for i in range(0, 6):
points[i].append(theta[i])

for i in range(0, 6):
data = np.loadtxt(points[i])
plt.plot(
data,
linestyle="-",
color=colors[i],
label=teams[i],
linewidth=2
)

plt.ylim(0, 0.5)
X = np.arange(len(years))
plt.xticks(X, years)
plt.tick_params(labelleft="off")
plt.xlabel("Year")
plt.ylabel("BradleyTerry Rate")
plt.title("Central League")
plt.grid(True)
plt.legend(loc="best")
plt.rcParams.update({"font.size": 20})
os.chdir("graph/")
plt.savefig("central.eps")

plt.show()

こんな感じになりました。

・巨人の安定的な強さ

・中日、ヤクルトの右肩下がり

・広島、横浜の右肩上がり

などが読み取れるのではないでしょうか…

パ・リーグでも同様にやってみると、以下のようなグラフとなります。

セ・リーグのグラフと比較すると6本の折れ線がごちゃごちゃしてるのが特徴的です。

2013年と2014年でABクラスがまるっと入れ替わるということが起きちゃう「混パ」をよく表現できてますね!