Ici nous considérons le modèle Bayésien suivant \[ N_i \mid \lambda \stackrel{iid}{\sim} Poisson(\lambda), \qquad i = 1, \ldots, n, \] avec la loi a priori \(\lambda \sim Gamma(\alpha, \beta)\). Attention \(\beta\) est le paramètre d’intensité (rate).

Un peu de calcul et nous trouvons la loi a posteriori qui est une \(Gamma(\tilde \alpha, \tilde \beta)\) avec \[ \tilde \alpha = \alpha + \sum_{i=1}^n N_i, \qquad \tilde \beta = n + \beta, \]

Afin de vérifier que nous savons coder un Metropolis–Hastings sur un vrai modèle statistique on va utiliser la loi cible \[ \pi(\lambda \mid N_1, \ldots, N_n) \propto \lambda^{\alpha + \sum_{i=1}^n N_i} - 1 - (n + \beta) \log \lambda. \] Evidemment cela ne sert à rien normalement mais cela a un intérêt pédagogique.

football <- function(nchain, data, hyper_param,
                     prop_sd, init = 1){
  chain <- rep(NA, nchain + 1)
  chain[1] <- init
  nobs <- length(data)
  
  acc_rate <- 0
  
  for (i in 1:nchain){
    ## Générons un candidat (marche alétoire à l'échelle log)
    prop <- rlnorm(1, log(chain[i]), prop_sd)
    
    ## Calcul de la probabilité d'acceptation
    top <- -(nobs + hyper_param[2]) * prop +
      (hyper_param[1] + sum(data) - 1) * log(prop)
    bottom <- -(nobs + hyper_param[2]) * chain[i] +
      (hyper_param[1] + sum(data) - 1) * log(chain[i])
    
    ## Attention ici le ratio des noyaux de proposition n'est pas 1
    acc_prob <- exp(top - bottom) * prop / chain[i]
    
    ## Mise a jour
    if (runif(1) < acc_prob){
      chain[i+1] <- prop
      acc_rate <- acc_rate + 1
    } else
      chain[i+1] <- chain[i]
  }
  
  acc_rate <- acc_rate / nchain
  return(list(chain = chain, acc_rate = acc_rate))
}

Essai sur les données.

## Récupération des données depuis le web
url <- "https://www.football-data.co.uk/mmz4281/2324/F1.csv"
data <- read.csv(url)

## Data pre-processing
team <- "Lyon"
idx_home <- which(data$HomeTeam == team)
idx_away <- which(data$AwayTeam == team)

lyon_goals <- c(data[idx_home,"FTHG"],
                data[idx_away,"FTAG"])

Choisissons des hyper-paramètres raisonnables (prior elicitation).

alpha <- 1
beta <- 0.1
prior <- function(x)
  dgamma(x, alpha, beta)

plot(prior, from = 0, to = 10,
     xlab = expression(lambda),
     ylab = "Density", ylim = c(0, 1))

Ici on a choisi des hyper-paramètres afin que la loi a priori soit pas trop informative.

Testons notre function

nchain <- 50000
hyper_param <- c(alpha, beta)
prop_sd <- 0.35
ans <- football(nchain, lyon_goals, hyper_param,
                prop_sd)

chain <- ans$chain
acc_rate <- ans$acc_rate

Le taux d’acceptation de notre chaîne est 0.55166 ce qui est une bonne valeur pour une chaine en dimension 1. Confirmons cela en visualisant notre chaine. Pour cela on peut utiliser le package coda

library(coda)
plot(mcmc(chain))

acf(chain)

Ici pas besoin d’enlever la période de chauffe car l’on semble être déjà dans le régime stationnaire. De plus l’ACF montre une dépendance assez limitée. On est donc plus que content.

Regardons avec impatience si ça colle bien la théorie

nobs <- length(lyon_goals)
true_posterior <- function(x)
  dgamma(x, alpha + sum(lyon_goals),
         nobs + beta)

col <- RColorBrewer::brewer.pal(3, "Set2")
par(mar = c(4, 5, 0.5, 0.5))
plot(density(chain), col = col[1], main = "",
     xlab = expression(lambda), lwd = 2)
curve(true_posterior, from = 0, to = 3,
      add = TRUE, col = col[2], lwd = 2 )
legend("topright", c("Estimated", "True"),
       lty = 1, bty = "n", col = col)

Conclusion on est trop fort !

Passons maintenant à la prédiction. Pour cela nous allons prendre comme estimateur la moyenne de la loi prédictive. On se rappelle que la loi prédictive a posteriori est donnée par \[ \pi(x_* \mid N_1, \ldots, N_n) = \int f(x_* \mid N_1, \ldots, N_n, \lambda) \pi(\lambda \mid N_1, \ldots, N_n) d\lambda, \] or ici puisque les matchs sont supposés indépendants, cela se simplifie en \[ \pi(x_* \mid N_1, \ldots, N_n) = \int f(x_* \mid \lambda) \pi(\lambda \mid N_1, \ldots, N_n) d\lambda. \] L’espérance \(\mu = \mathbb{E}(X_*)\) sous cette loi est donc \[ \mu = \sum_{k \geq 0} k \int f(k \mid \lambda) \pi(\lambda \mid N_1, \ldots, N_n) d\lambda = \int \sum_{k \geq 0} k f(k \mid \lambda) \pi(\lambda \mid N_1, \ldots, N_n) d\lambda, \] qui s’écrit comme \[ \mu_N = \mathbb{E}\left\{ \mathbb{E}\left[X_* \mid \lambda \right]\right\}, \] que l’on peut facilement estimer selon la loi des grands nombres. Concrètement, en utilisant encore notre chaine de Markov, on fait

sim <- rpois(length(chain), chain)
sim_factor <- factor(sim, levels = 0:10)
freq <- table(sim_factor) / length(chain)
plot(freq, xlab = "Nombre de buts",
     ylab = "Fonction de masse",
     col = col[1])




## Estimation et IC
c(mean(sim), quantile(sim, prob = c(0.025, 0.975)))
             2.5%    97.5% 
1.082778 0.000000 4.000000 

Ici on devrait plutôt utiliser comme estimateur le mode de la loi prédictive qui ici est 1 (mais 0 est très proche aussi). Donc un pari raisonnable serait de dire que Lyon marque moins de 1.5 buts. Bon il y a aussi le pari super sur “marque moins de 2.5 buts” mais ça ne devrait rien rapporter.

LS0tCnRpdGxlOiAiTW9kw6lsaXNhdGlvbiBkdSBub21icmUgZGUgYnV0cyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKSWNpIG5vdXMgY29uc2lkw6lyb25zIGxlIG1vZMOobGUgQmF5w6lzaWVuIHN1aXZhbnQKJCQKTl9pIFxtaWQgXGxhbWJkYSBcc3RhY2tyZWx7aWlkfXtcc2ltfSBQb2lzc29uKFxsYW1iZGEpLCBccXF1YWQgaSA9IDEsIFxsZG90cywgbiwKJCQKYXZlYyBsYSBsb2kgYSBwcmlvcmkgJFxsYW1iZGEgXHNpbSBHYW1tYShcYWxwaGEsIFxiZXRhKSQuIEF0dGVudGlvbiAkXGJldGEkIGVzdCBsZSBwYXJhbcOodHJlICoqZCdpbnRlbnNpdMOpKiogKHJhdGUpLgoKVW4gcGV1IGRlIGNhbGN1bCBldCBub3VzIHRyb3V2b25zIGxhIGxvaSBhIHBvc3RlcmlvcmkgcXVpIGVzdCB1bmUgJEdhbW1hKFx0aWxkZSBcYWxwaGEsIFx0aWxkZSBcYmV0YSkkIGF2ZWMKJCQKXHRpbGRlIFxhbHBoYSA9IFxhbHBoYSArIFxzdW1fe2k9MX1ebiBOX2ksIFxxcXVhZCBcdGlsZGUgXGJldGEgPSBuICsgXGJldGEsCiQkCgpBZmluIGRlIHbDqXJpZmllciBxdWUgbm91cyBzYXZvbnMgY29kZXIgdW4gTWV0cm9wb2xpcy0tSGFzdGluZ3Mgc3VyIHVuIHZyYWkgbW9kw6hsZSBzdGF0aXN0aXF1ZSBvbiB2YSB1dGlsaXNlciBsYSBsb2kgY2libGUKJCQKXHBpKFxsYW1iZGEgXG1pZCBOXzEsIFxsZG90cywgTl9uKSBccHJvcHRvIFxsYW1iZGFee1xhbHBoYSArIFxzdW1fe2k9MX1ebiBOX2l9IC0gMSAtIChuICsgXGJldGEpIFxsb2cgXGxhbWJkYS4KJCQKRXZpZGVtbWVudCBjZWxhIG5lIHNlcnQgw6AgcmllbiBub3JtYWxlbWVudCBtYWlzIGNlbGEgYSB1biBpbnTDqXLDqnQgcMOpZGFnb2dpcXVlLgoKYGBge3J9CmZvb3RiYWxsIDwtIGZ1bmN0aW9uKG5jaGFpbiwgZGF0YSwgaHlwZXJfcGFyYW0sCiAgICAgICAgICAgICAgICAgICAgIHByb3Bfc2QsIGluaXQgPSAxKXsKICBjaGFpbiA8LSByZXAoTkEsIG5jaGFpbiArIDEpCiAgY2hhaW5bMV0gPC0gaW5pdAogIG5vYnMgPC0gbGVuZ3RoKGRhdGEpCiAgCiAgYWNjX3JhdGUgPC0gMAogIAogIGZvciAoaSBpbiAxOm5jaGFpbil7CiAgICAjIyBHw6luw6lyb25zIHVuIGNhbmRpZGF0IChtYXJjaGUgYWzDqXRvaXJlIMOgIGwnw6ljaGVsbGUgbG9nKQogICAgcHJvcCA8LSBybG5vcm0oMSwgbG9nKGNoYWluW2ldKSwgcHJvcF9zZCkKICAgIAogICAgIyMgQ2FsY3VsIGRlIGxhIHByb2JhYmlsaXTDqSBkJ2FjY2VwdGF0aW9uCiAgICB0b3AgPC0gLShub2JzICsgaHlwZXJfcGFyYW1bMl0pICogcHJvcCArCiAgICAgIChoeXBlcl9wYXJhbVsxXSArIHN1bShkYXRhKSAtIDEpICogbG9nKHByb3ApCiAgICBib3R0b20gPC0gLShub2JzICsgaHlwZXJfcGFyYW1bMl0pICogY2hhaW5baV0gKwogICAgICAoaHlwZXJfcGFyYW1bMV0gKyBzdW0oZGF0YSkgLSAxKSAqIGxvZyhjaGFpbltpXSkKICAgIAogICAgIyMgQXR0ZW50aW9uIGljaSBsZSByYXRpbyBkZXMgbm95YXV4IGRlIHByb3Bvc2l0aW9uIG4nZXN0IHBhcyAxCiAgICBhY2NfcHJvYiA8LSBleHAodG9wIC0gYm90dG9tKSAqIHByb3AgLyBjaGFpbltpXQogICAgCiAgICAjIyBNaXNlIGEgam91cgogICAgaWYgKHJ1bmlmKDEpIDwgYWNjX3Byb2IpewogICAgICBjaGFpbltpKzFdIDwtIHByb3AKICAgICAgYWNjX3JhdGUgPC0gYWNjX3JhdGUgKyAxCiAgICB9IGVsc2UKICAgICAgY2hhaW5baSsxXSA8LSBjaGFpbltpXQogIH0KICAKICBhY2NfcmF0ZSA8LSBhY2NfcmF0ZSAvIG5jaGFpbgogIHJldHVybihsaXN0KGNoYWluID0gY2hhaW4sIGFjY19yYXRlID0gYWNjX3JhdGUpKQp9CmBgYAoKRXNzYWkgc3VyIGxlcyBkb25uw6llcy4KYGBge3J9CiMjIFLDqWN1cMOpcmF0aW9uIGRlcyBkb25uw6llcyBkZXB1aXMgbGUgd2ViCnVybCA8LSAiaHR0cHM6Ly93d3cuZm9vdGJhbGwtZGF0YS5jby51ay9tbXo0MjgxLzIzMjQvRjEuY3N2IgpkYXRhIDwtIHJlYWQuY3N2KHVybCkKCiMjIERhdGEgcHJlLXByb2Nlc3NpbmcKdGVhbSA8LSAiTHlvbiIKaWR4X2hvbWUgPC0gd2hpY2goZGF0YSRIb21lVGVhbSA9PSB0ZWFtKQppZHhfYXdheSA8LSB3aGljaChkYXRhJEF3YXlUZWFtID09IHRlYW0pCgpseW9uX2dvYWxzIDwtIGMoZGF0YVtpZHhfaG9tZSwiRlRIRyJdLAogICAgICAgICAgICAgICAgZGF0YVtpZHhfYXdheSwiRlRBRyJdKQpgYGAKCkNob2lzaXNzb25zIGRlcyBoeXBlci1wYXJhbcOodHJlcyByYWlzb25uYWJsZXMgKHByaW9yIGVsaWNpdGF0aW9uKS4KYGBge3J9CmFscGhhIDwtIDEKYmV0YSA8LSAwLjEKcHJpb3IgPC0gZnVuY3Rpb24oeCkKICBkZ2FtbWEoeCwgYWxwaGEsIGJldGEpCgpwbG90KHByaW9yLCBmcm9tID0gMCwgdG8gPSAxMCwKICAgICB4bGFiID0gZXhwcmVzc2lvbihsYW1iZGEpLAogICAgIHlsYWIgPSAiRGVuc2l0eSIsIHlsaW0gPSBjKDAsIDEpKQpgYGAKCkljaSBvbiBhIGNob2lzaSBkZXMgaHlwZXItcGFyYW3DqHRyZXMgYWZpbiBxdWUgbGEgbG9pIGEgcHJpb3JpIHNvaXQgcGFzIHRyb3AgaW5mb3JtYXRpdmUuCgpUZXN0b25zIG5vdHJlIGZ1bmN0aW9uCmBgYHtyfQpuY2hhaW4gPC0gNTAwMDAKaHlwZXJfcGFyYW0gPC0gYyhhbHBoYSwgYmV0YSkKcHJvcF9zZCA8LSAwLjM1CmFucyA8LSBmb290YmFsbChuY2hhaW4sIGx5b25fZ29hbHMsIGh5cGVyX3BhcmFtLAogICAgICAgICAgICAgICAgcHJvcF9zZCkKCmNoYWluIDwtIGFucyRjaGFpbgphY2NfcmF0ZSA8LSBhbnMkYWNjX3JhdGUKYGBgCgpMZSB0YXV4IGQnYWNjZXB0YXRpb24gZGUgbm90cmUgY2hhw65uZSBlc3QgYHIgYWNjX3JhdGVgIGNlIHF1aSBlc3QgdW5lIGJvbm5lIHZhbGV1ciBwb3VyIHVuZSBjaGFpbmUgZW4gZGltZW5zaW9uIDEuIENvbmZpcm1vbnMgY2VsYSBlbiB2aXN1YWxpc2FudCBub3RyZSBjaGFpbmUuIFBvdXIgY2VsYSBvbiBwZXV0IHV0aWxpc2VyIGxlIHBhY2thZ2UgKmNvZGEqCmBgYHtyfQpsaWJyYXJ5KGNvZGEpCnBsb3QobWNtYyhjaGFpbikpCmFjZihjaGFpbikKYGBgCgpJY2kgcGFzIGJlc29pbiBkJ2VubGV2ZXIgbGEgcMOpcmlvZGUgZGUgY2hhdWZmZSBjYXIgbCdvbiBzZW1ibGUgw6p0cmUgZMOpasOgIGRhbnMgbGUgcsOpZ2ltZSBzdGF0aW9ubmFpcmUuIERlIHBsdXMgbCdBQ0YgbW9udHJlIHVuZSBkw6lwZW5kYW5jZSBhc3NleiBsaW1pdMOpZS4gT24gZXN0IGRvbmMgcGx1cyBxdWUgY29udGVudC4KClJlZ2FyZG9ucyBhdmVjIGltcGF0aWVuY2Ugc2kgw6dhIGNvbGxlIGJpZW4gbGEgdGjDqW9yaWUKYGBge3J9Cm5vYnMgPC0gbGVuZ3RoKGx5b25fZ29hbHMpCnRydWVfcG9zdGVyaW9yIDwtIGZ1bmN0aW9uKHgpCiAgZGdhbW1hKHgsIGFscGhhICsgc3VtKGx5b25fZ29hbHMpLAogICAgICAgICBub2JzICsgYmV0YSkKCmNvbCA8LSBSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwoMywgIlNldDIiKQpwYXIobWFyID0gYyg0LCA1LCAwLjUsIDAuNSkpCnBsb3QoZGVuc2l0eShjaGFpbiksIGNvbCA9IGNvbFsxXSwgbWFpbiA9ICIiLAogICAgIHhsYWIgPSBleHByZXNzaW9uKGxhbWJkYSksIGx3ZCA9IDIpCmN1cnZlKHRydWVfcG9zdGVyaW9yLCBmcm9tID0gMCwgdG8gPSAzLAogICAgICBhZGQgPSBUUlVFLCBjb2wgPSBjb2xbMl0sIGx3ZCA9IDIgKQpsZWdlbmQoInRvcHJpZ2h0IiwgYygiRXN0aW1hdGVkIiwgIlRydWUiKSwKICAgICAgIGx0eSA9IDEsIGJ0eSA9ICJuIiwgY29sID0gY29sKQpgYGAKCkNvbmNsdXNpb24gb24gZXN0IHRyb3AgZm9ydCAhCgpQYXNzb25zIG1haW50ZW5hbnQgw6AgbGEgcHLDqWRpY3Rpb24uIFBvdXIgY2VsYSBub3VzIGFsbG9ucyBwcmVuZHJlIGNvbW1lIGVzdGltYXRldXIgbGEgbW95ZW5uZSBkZSBsYSBsb2kgcHLDqWRpY3RpdmUuIE9uIHNlIHJhcHBlbGxlIHF1ZSBsYSBsb2kgcHLDqWRpY3RpdmUgYSBwb3N0ZXJpb3JpIGVzdCBkb25uw6llIHBhcgokJApccGkoeF8qIFxtaWQgTl8xLCBcbGRvdHMsIE5fbikgPSBcaW50IGYoeF8qIFxtaWQgTl8xLCBcbGRvdHMsIE5fbiwgXGxhbWJkYSkgXHBpKFxsYW1iZGEgXG1pZCBOXzEsIFxsZG90cywgTl9uKSBkXGxhbWJkYSwKJCQKb3IgaWNpIHB1aXNxdWUgbGVzIG1hdGNocyBzb250IHN1cHBvc8OpcyBpbmTDqXBlbmRhbnRzLCBjZWxhIHNlIHNpbXBsaWZpZSBlbgokJApccGkoeF8qIFxtaWQgTl8xLCBcbGRvdHMsIE5fbikgPSBcaW50IGYoeF8qIFxtaWQgXGxhbWJkYSkgXHBpKFxsYW1iZGEgXG1pZCBOXzEsIFxsZG90cywgTl9uKSBkXGxhbWJkYS4KJCQKTCdlc3DDqXJhbmNlICRcbXUgPSBcbWF0aGJie0V9KFhfKikkIHNvdXMgY2V0dGUgbG9pIGVzdCBkb25jCiQkClxtdSA9IFxzdW1fe2sgXGdlcSAwfSBrIFxpbnQgZihrIFxtaWQgXGxhbWJkYSkgXHBpKFxsYW1iZGEgXG1pZCBOXzEsIFxsZG90cywgTl9uKSBkXGxhbWJkYSA9IFxpbnQgXHN1bV97ayBcZ2VxIDB9IGsgIGYoayBcbWlkIFxsYW1iZGEpIFxwaShcbGFtYmRhIFxtaWQgTl8xLCBcbGRvdHMsIE5fbikgZFxsYW1iZGEsCiQkCnF1aSBzJ8OpY3JpdCBjb21tZQokJApcbXVfTiA9IFxtYXRoYmJ7RX1cbGVmdFx7IFxtYXRoYmJ7RX1cbGVmdFtYXyogXG1pZCBcbGFtYmRhIFxyaWdodF1ccmlnaHRcfSwKJCQKcXVlIGwnb24gcGV1dCBmYWNpbGVtZW50IGVzdGltZXIgc2Vsb24gbGEgbG9pIGRlcyBncmFuZHMgbm9tYnJlcy4gQ29uY3LDqHRlbWVudCwgZW4gdXRpbGlzYW50IGVuY29yZSBub3RyZSBjaGFpbmUgZGUgTWFya292LCBvbiBmYWl0CmBgYHtyfQpzaW0gPC0gcnBvaXMobGVuZ3RoKGNoYWluKSwgY2hhaW4pCnNpbV9mYWN0b3IgPC0gZmFjdG9yKHNpbSwgbGV2ZWxzID0gMDoxMCkKZnJlcSA8LSB0YWJsZShzaW1fZmFjdG9yKSAvIGxlbmd0aChjaGFpbikKcGxvdChmcmVxLCB4bGFiID0gIk5vbWJyZSBkZSBidXRzIiwKICAgICB5bGFiID0gIkZvbmN0aW9uIGRlIG1hc3NlIiwKICAgICBjb2wgPSBjb2xbMV0pCgoKCiMjIEVzdGltYXRpb24gZXQgSUMKYyhtZWFuKHNpbSksIHF1YW50aWxlKHNpbSwgcHJvYiA9IGMoMC4wMjUsIDAuOTc1KSkpCgpgYGAKSWNpIG9uIGRldnJhaXQgcGx1dMO0dCB1dGlsaXNlciBjb21tZSBlc3RpbWF0ZXVyIGxlIG1vZGUgZGUgbGEgbG9pIHByw6lkaWN0aXZlIHF1aSBpY2kgZXN0IDEgKG1haXMgMCBlc3QgdHLDqHMgcHJvY2hlIGF1c3NpKS4gRG9uYyB1biBwYXJpIHJhaXNvbm5hYmxlIHNlcmFpdCBkZSBkaXJlIHF1ZSBgciB0ZWFtYCBtYXJxdWUgbW9pbnMgZGUgMS41IGJ1dHMuIEJvbiBpbCB5IGEgYXVzc2kgbGUgcGFyaSBzdXBlciBzdXIgIm1hcnF1ZSBtb2lucyBkZSAyLjUgYnV0cyIgbWFpcyDDp2EgbmUgZGV2cmFpdCByaWVuIHJhcHBvcnRlci4K