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


##Bradley-Terry's Model
これは、「強さ」を図るモデルです。
簡単にいうと、試合の結果から各々の「強さ」を数値化できるよっていう概念です。
以下の論文が参考になりましたので、詳しくはこちらをご覧ください。
[「強さ」に対する定量的評価法とその応用](http://www-mcs.c.oka-pu.ac.jp/pdf/abstract/2006_SatoshiNishimoto_MasterAbstract.pdf)

>n個の要素 (チームや個人) があり、何らかの対戦を行うものとする。
対戦は 1要素 対 1要素 のマッチで行われ、その結果は片方の要素の対して勝利、または敗北しか生じないとする。
何回か対戦した結果から各要素の「強さ」をはかるとする。
ここで要素 i が要素 j に勝利する確率を Pij としたとき、すべての組み合わせに対して、
Pij =　πi / πi + πj (1)
となるπi を導入する。
式 (1) の関係式を Bradley-Terry（BT）モデルという。
BT モデルにおいて πi は，要素 i の強さを表すと考えることができる。

##参考にしたもの
先日参加した、[統計処理及び機械学習に基づくデータマイニング勉強会 #03](http://spml4dm.connpass.com/event/13133/)での講義内容を参考にしています。
ここで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
```

##過去５年間の戦力値遷移
上記のBradley-Terry's Modelを参考に、セ・リーグの５年間の移り変わりを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()
```
![central.png](https://qiita-image-store.s3.amazonaws.com/0/19111/7493182c-768b-f0f3-3b61-82fb59d04246.png "central.png")

こんな感じになりました。
・巨人の安定的な強さ
・中日、ヤクルトの右肩下がり
・広島、横浜の右肩上がり
などが読み取れるのではないでしょうか…

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

![pacific.png](https://qiita-image-store.s3.amazonaws.com/0/19111/fd9ada2c-c7df-716b-e8ea-42e7151eb478.png "pacific.png")

セ・リーグのグラフと比較すると6本の折れ線がごちゃごちゃしてるのが特徴的です。
2013年と2014年でABクラスがまるっと入れ替わるということが起きちゃう「混パ」をよく表現できてますね！