ホーム&アウェー方式のアウェーゴールは妥当なのか

チャンピオンズリーグを見ていた。
CL というか本拠地があってホームでもアウェーでも試合を行うタイプのスポーツにあるのが、先に言ったホームとアウェーで戦い方というか試合に影響する部分が大きい、ということである。
サッカーは特にホーム&アウェーの影響が大きい(アウェーゴール)らしいのだが、ホームとアウェーで各々入れ替えて試合をするのでそれはいいのでは…とは思うが、それでも、先にホームで戦うのかそれともアウェーなのかも影響する、と言われているらしい。
というわけでデータを集計して解析した。
データはwikipediaのCL のページをクローリングするが、1992年から試合形式が変わっているようなので、1992年から2017年まで、ホーム&アウェー形式で試合をしている部分をパースしてくる。
ちなみに決勝戦は事前に決められた中立地で一発勝負で行われるため、決勝戦は除く。

# R
i <- 1992:2017
j <- c(93:99,0:18)
sprintf("https://en.wikipedia.org/wiki/%d%s%02d_UEFA_Champions_League", i, "&#8211;", j)
# ターミナル
wget -i url.txt
for i in $(ls [0-9]*)
do
  name=$(echo $i | grep -o '^[0-9]*')
  mv $i $name
done

その後、たぶんエンダッシュ – を目印にひたすら試合結果をパースする(後述)。
 
ホーム&アウェーで各2回ずつあるので分けて集計しておく。
何も考えずにホームとアウェーで平均ゴール数を出すと

[1] 1.623472 1.050530

となる。単純に1.5倍くらいホームのほうが点を取りやすい。

 
1試合あたりのゴール数はポアソン分布に従うと仮定すると、平均ゴール数はホームのほうがよく取れる。

poisson.test(colSums(goal))
	Comparison of Poisson rates

data:  colSums(goal) time base: 1
count1 = 3984, expected count1 = 3281, p-value < 2.2e-16
alternative hypothesis: true rate ratio is not equal to 1
95 percent confidence interval:
 1.470317 1.624500
sample estimates:
rate ratio 
  1.545384 

 
最初にホーム/アウェーにくるか、2試合目に来るか(1st leg と2nd leg という)で得点数に差があるかを検討する。単純にホーム&アウェーの順番も格納しておいたので個別にやるだけ。

   H_1st    A_1st    A_2nd    H_2nd 
1.443358 1.064385 1.036675 1.803586

得点数の分布は、2nd leg でホームにくる場合が最も多く点をとっているようである。平均ゴール数も1.8点と、2試合合計で本当に勝負がかかっているときがもっとも点をとる、ようである。

個別にポアソン検定をすると、
H_1st vs A_1st では、別のチームの比較だが、1st leg でホームのほうが点を取る。
H_1st vs A_2nd では、同じチームの比較だが、1st leg でホームのときのほうが点を取る。
H_1st vs H_2nd では、別のチームの比較だが、2nd leg でホームのほうが点を取る。
A_1st vs A_2nd では、別のチームの比較だが、アウェーでの点を取る数は変わらない。
A_1st vs H_2nd では、同じチームの比較だが、2nd leg でホームのときのほうが点を取る。
A_2nd vs H_2nd では、別のチームの比較だが、2nd leg でホームのほうが点を取る。
というわけで、2nd leg でホームのときが一番点を取れそうな気がするようである。
 
時系列でデータをとったので、Elo rating によるチームの強さ定量化をしたかったがやれていない。

[1] "H_1st vs A_1st"

	Comparison of Poisson rates

data:  colSums(score)[cmb[, i]] time base: 1
count1.H_1st = 1771, expected count1 = 1538.5, p-value < 2.2e-16
alternative hypothesis: true rate ratio is not equal to 1
95 percent confidence interval:
 1.261775 1.457655
sample estimates:
rate ratio.H_1st 
        1.356049 

[1] "H_1st vs A_2nd"

	Comparison of Poisson rates

data:  colSums(score)[cmb[, i]] time base: 1
count1.H_1st = 1771, expected count1 = 1521.5, p-value < 2.2e-16
alternative hypothesis: true rate ratio is not equal to 1
95 percent confidence interval:
 1.294792 1.497468
sample estimates:
rate ratio.H_1st 
        1.392296 

[1] "H_1st vs H_2nd"

	Comparison of Poisson rates

data:  colSums(score)[cmb[, i]] time base: 1
count1.H_1st = 1771, expected count1 = 1992, p-value = 2.675e-12
alternative hypothesis: true rate ratio is not equal to 1
95 percent confidence interval:
 0.7513686 0.8522624
sample estimates:
rate ratio.H_1st 
       0.8002711 

[1] "A_1st vs A_2nd"

	Comparison of Poisson rates

data:  colSums(score)[cmb[, i]] time base: 1
count1.A_1st = 1306, expected count1 = 1289, p-value = 0.5157
alternative hypothesis: true rate ratio is not equal to 1
95 percent confidence interval:
 0.9497088 1.1100190
sample estimates:
rate ratio.A_1st 
         1.02673 

[1] "A_1st vs H_2nd"

	Comparison of Poisson rates

data:  colSums(score)[cmb[, i]] time base: 1
count1.A_1st = 1306, expected count1 = 1759.5, p-value < 2.2e-16
alternative hypothesis: true rate ratio is not equal to 1
95 percent confidence interval:
 0.5507167 0.6322103
sample estimates:
rate ratio.A_1st 
       0.5901491 

[1] "A_2nd vs H_2nd"

	Comparison of Poisson rates

data:  colSums(score)[cmb[, i]] time base: 1
count1.A_2nd = 1272, expected count1 = 1742.5, p-value < 2.2e-16
alternative hypothesis: true rate ratio is not equal to 1
95 percent confidence interval:
 0.5360617 0.6161042
sample estimates:
rate ratio.A_2nd 
       0.5747854 
# python3 data preprocessing
import re, glob, os
score = re.compile("\>\d+&#8211;\d+")
game = ["qualifying", "R16", "QuaterFinal", "SemiFinal"]
w0 = open("out.txt", "w")

for y in range(1992, 2018):
  f = open(str(y), "rU")
  res = []
  g0 = []
  scoreflag = -1
  leg = False
  table = False
  for g in f:
    g0 += [g]
    tmp = score.findall(g)
    if "1st leg" in g:
      leg = True
      table = True
      scoreflag = -1
    if len(tmp) > 0 and leg and table:
      scoreflag += 1
      if len(tmp) > 1:
        tmp = tmp[-1]
      tmp = [re.sub("&#8211;", "-", tmp[0])]
      tmp = [re.sub(">", "", tmp[0])]
      if scoreflag % 3 < 2:
        res += [re.sub("<[^>]*>", "", g0[-2]).strip(), tmp[0]]
      elif scoreflag % 3 == 2:
        res += [tmp[0]]
    if '</table' in g:
      leg = False
      table = False
  N = [int(len(res)/5)-14, 8, 4, 2]
  lab = sum(map(lambda x, y: [x]*y, game, N), [])
  for i in range(int(len(res)/5)):
    line = [str(y), lab[i], res[j*i], res[j*i+1], res[j*i+2], res[j*i+3], res[j*i+4]]
    print(line)
    w0.write("\t".join(line) + "\n")

w0.close()
# R 解析
# R analysis
# ひだりの列が初戦のホームチーム
# スコアはチームの並びを保ったまま記載される
# 1列目:初戦がホーム
# 2列目:初戦がアウェイ
# 3列目:2戦目がアウェイ
# 4列目:2戦目がホーム

library(igraph)
library(rstan)
dat <- read.delim("out.txt", stringsAsFactors=FALSE, header=FALSE)


gr <- graph_from_edgelist(as.matrix(dat[,c(3,5)]), directed=FALSE)
adj <- as.matrix(as_adjacency_matrix(gr))


sb <- subset(dat, V2=="R16")
sapply(split(sb[c(3,5)], sb$V1), unlist)


score <- t(sapply(sapply(apply(dat[,6:7], 1, paste, collapse="-"), strsplit, "-"), as.numeric, USE.NAMES=FALSE))
colnames(score) <- c("H_1st", "A_1st", "A_2nd", "H_2nd")


goal <- cbind(c(score[,c(1,4)]), c(score[,2:3]))
Max <- max(goal)
tab <- mapply(function(z) table(factor(goal[,z], 0:Max)), seq(ncol(goal)))
me <- colMeans(goal)
d <- mapply(dpois, list(0:Max), me)


# ゴール数ヒストグラム
cols <- c("blue", "green")
b0 <- barplot(t(tab), beside=TRUE, col=cols, xlab="Goal")
legend("topright", legend=sprintf("%s: %.3f", c("Home", "Away"), me), pch=15, col=cols, title="Expected goal", cex=1.5)
legend("right", legend=sprintf("%d games, %d goals", nrow(goal), sum(goal)), bty="n", cex=1.5)
title(sprintf("Home and Away goals of Champions League %d - %d", min(dat$V1), max(dat$V1)))
for(i in seq(ncol(tab))){
  lines(colMeans(b0), d[,i]*sum(tab[,i]), col=cols[i], lwd=3)
}

poisson.test(colSums(goal))


# 1st 2nd leg のHA すべて区別する
cmb <- combn(4, 2)
colMeans(score)
mat <- diag(0, ncol(score))
rownames(mat) <- colnames(mat) <- colnames(score)
for(i in 1:ncol(cmb)){
  res <- poisson.test(colSums(score)[ cmb[,i] ])
  mat[ cmb[1,i], cmb[2,i] ] <- res$p.value
  print(sprintf("%s vs %s", colnames(score)[cmb[1,i]], colnames(score)[cmb[2,i]]))
  print(res)
}

tab <- mapply(function(z) table(factor(score[,z], 0:Max)), colnames(score))
me <- colMeans(score)

# ゴール数ヒストグラム
cols <- c("blue", "green", "pink", "orange")
b0 <- barplot(t(tab), beside=TRUE, col=cols, xlab="Goal")
legend("topright", legend=sprintf("%s: %.3f", colnames(tab), me), pch=15, col=cols, title="Expected goal", cex=1.5)
title(sprintf("Home and Away goals of Champions League %d - %d", min(dat$V1), max(dat$V1)))
for(i in seq(ncol(tab))){
  lines(colMeans(b0), d[,i]*sum(tab[,i]), col=cols[i], lwd=3)
}