ランキングを考える
J1の勝ち点の表を見ていて「上位チームに勝つのも下位チームに勝つのも、同じ勝ち点3って何だかなぁ」とふと思いました。リーグ内のチーム/競技者数が多い対戦スポーツほど(たとえば相撲)、こういった格差というものを感じます。
お仕事の関係で読み進めていた本で、強さを考慮したランキングの考え方が紹介されていたので、J1の結果を使ってチームの強さを考えてみました。
OD手法
今回参考にしたのは以下の本です。
レイティング・ランキングの数理 ―No.1は誰か?―
難しい証明は流し読みしてしまえば、アルゴリズム系の書籍としては読みやすい部類です。
惜しむらくは、ランキングの例として多用されているのがNFL等のデータで、「ラッシングヤード数が~」等と書かれてもいまいちイメージがつかないところ。
この本では色々なランキングの考え方・手法が紹介されていますが、今回考えたいJリーグのランキングに一番ハマりそうな手法として「OD手法」というものを選んでみました。
OD手法では、チームのランキングを攻撃力(Offensive rating)と守備力(Defensive rating)を定義して評価するのですが、
(第7章 攻撃力・守備力レイティング手法 より)
攻撃力と守備力の間には循環的な関係があり、攻撃力と守備力を個別にレイティングしようとする場合、各々は他を考慮に入れなければならないのである。
という辺りをどう解決するか、が第7章に書かれています。
Rで実装(というほどのものでもない...)
OD手法の細かい説明は書籍に譲るとして、実際のデータに当てはめてRで実行してみます。
適当に書いているので汚いコードで恐縮ですが、とてもシンプルな方法というのは分かってもらえるかと思います。
- Jリーグ2015年セカンドステージの第1~16戦までの結果を利用(Jリーグ公式より拝借)
- 実行環境は R 3.1.3
# 得失点のマトリックスを作る
a <- matrix(
c(
0, 2, 3, 0, 1, 1, 0, 1, 1, 1, 3, 3, 1, 3, 0, 0, 1, 0,
1, 0, 2, 2, 0, 2, 0, 2, 1, 1, 2, 2, 1, 3, 3, 1, 3, 2,
4, 1, 0, 0, 1, 1, 0, 2, 2, 1, 2, 0, 4, 3, 1, 1, 1, 1,
1, 2, 4, 0, 0, 2, 1, 0, 0, 1, 1, 2, 1, 3, 1, 2, 0, 3,
0, 2, 2, 2, 0, 2, 3, 0, 6, 0, 2, 4, 5, 4, 2, 0, 0, 5,
0, 1, 1, 0, 0, 0, 1, 0, 0, 2, 0, 1, 0, 0, 1, 1, 2, 2,
0, 0, 0, 1, 1, 0, 0, 0, 2, 1, 1, 1, 1, 1, 0, 1, 0, 0,
2, 1, 1, 2, 1, 1, 3, 0, 0, 1, 3, 2, 0, 3, 3, 3, 3, 0,
0, 1, 1, 0, 0, 1, 2, 2, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0,
2, 0, 0, 1, 0, 0, 0, 2, 1, 0, 2, 1, 2, 1, 2, 1, 3, 2,
1, 2, 1, 1, 0, 0, 3, 2, 2, 0, 0, 2, 1, 1, 1, 1, 0, 2,
0, 1, 0, 1, 0, 0, 3, 0, 2, 1, 1, 0, 5, 2, 2, 7, 0, 0,
1, 0, 1, 2, 1, 0, 2, 0, 0, 1, 1, 0, 0, 0, 2, 0, 0, 2,
1, 1, 1, 1, 3, 0, 1, 2, 3, 1, 0, 1, 1, 0, 0, 0, 0, 1,
2, 5, 1, 0, 1, 3, 0, 1, 3, 1, 2, 0, 3, 0, 0, 1, 0, 6,
0, 1, 1, 1, 0, 0, 3, 0, 2, 1, 0, 1, 0, 1, 1, 0, 3, 0,
0, 1, 0, 1, 3, 2, 0, 2, 2, 0, 0, 2, 3, 1, 1, 2, 0, 3,
0, 3, 2, 0, 2, 4, 3, 0, 1, 1, 4, 2, 2, 0, 1, 0, 1, 0
), 18, 18
)
a_label <- c("Tokyo", "Osaka", "Urawa", "Yokohama", "Hiroshima", "Ko-fu", "Yamagata", "Kashima", "Matsumoto",
"Sho-nan", "Niigata", "Ko-be", "Shimizu", "Sendai", "Kawasaki", "Tosu", "Kashiwa", "Nagoya")
# 初期ベクトル
d1 <- as.vector(rep(1:1, length = 18))
d2 <- as.vector(rep(0:0, length = 18))
# 交互精緻化プロセス
iterate_func <- function(a_mat, d_cur) {
d_next <- a_mat %*% (1/(t(a_mat) %*% (1/d_cur)))
return(d_next)
}
i <- 0
repeat{
d2 <- iterate_func(a, d1)
i <- i + 1
if( as.double(sqrt(as.vector(d1-d2) %*% as.vector(d1-d2))) >= 0.000001 ) d1 <- d2
else break
}
o <- t(a) %*% (1/d2)
plot(d2, o, type="n")
text(d2, o, a_label)
結果
こんな感じになりました(四角内の数字は16節時点での実際の順位)。
OD手法では
- Offensive ratingが高い = 攻撃力が高い
- Defensive ratingが低い = 防御力が高い
となるので、上図で言うと以下のように評価出来ます。
- 左上 = 攻撃力・守備力共に高い(広島、鹿島など)
- 左下 = 攻撃力は低いが、守備力が高い(FC東京、甲府など)
- 右上 = 攻撃力は高いが、守備力が低い(名古屋、大阪など)
- 右下 = 攻撃力・守備力共に低い(山形・清水など)
勝ち点や得失点だけが並んだ殺風景な順位表よりは、直感的な視覚化になりました(多分)。
勝ち点ベースで評価される実際の順位とも、それほど違和感がない感じになりました。やっぱり広島強いですね…。
個別チームについて細かくコメントするとJリーグ大好きな知人に刺されそうなので、どう評価するかは皆さんにお任せです。
おまけ
J1がそこそこ面白い結果だったので、相撲でも同じことが出来ないかと考えてやってみました。
- 対戦データはこちらからお借りしました
- 得点の概念がないので、勝ち星をそのまま得点と見立てています
白鵬の異常な強さが良くわかる結果になりました。
が、勝ち星を得点と見立てた都合上、比較的新顔の力士は負け数も少ない = 防御力が高いと判定されています。
うーん。一工夫必要な感じ。