stan_foot | R Documentation |
Fits football goal-based models using Stan via the CmdStan backend. Supported models include: double Poisson, bivariate Poisson, Skellam, Student's t, diagonal-inflated bivariate Poisson, zero-inflated Skellam, and negative Binomial.
stan_foot(
data,
model,
predict = 0,
ranking,
dynamic_type,
prior_par = list(ability = normal(0, NULL), ability_sd = cauchy(0, 5), home = normal(0,
5)),
home_effect = TRUE,
norm_method = "none",
ranking_map = NULL,
method = "MCMC",
...
)
data |
A data frame containing match data with columns:
|
model |
A character string specifying the Stan model to fit. Options are:
|
predict |
An integer specifying the number of out-of-sample matches for prediction. If missing, the function fits the model to the entire dataset without making predictions. |
ranking |
An optional
|
dynamic_type |
A character string specifying the type of dynamics in the model. Options are:
|
prior_par |
A list specifying the prior distributions for the parameters of interest:
See the rstanarm package for more details on specifying priors. |
home_effect |
A logical value indicating the inclusion of a home effect in the model. (default is |
norm_method |
A character string specifying the method used to normalize team-specific ranking points. Options are:
|
ranking_map |
An optional vector mapping ranking periods to data periods. If not provided and the number of ranking periods matches the number of data periods, a direct mapping is assumed. |
method |
A character string specifying the method used to obtain the Bayesian estimates. Options are:
|
... |
Additional arguments passed to |
Let (y^{H}_{n}, y^{A}_{n})
denote the
observed number of goals scored by the home
and the away team in the n
-th game,
respectively. A general bivariate Poisson model
allowing for goals' correlation
(Karlis & Ntzoufras, 2003) is the following:
Y^H_n, Y^A_n| \lambda_{1n}, \lambda_{2n}, \lambda_{3n} \sim \mathsf{BivPoisson}(\lambda_{1n}, \lambda_{2n}, \lambda_{3n})
\log(\lambda_{1n}) = \mu+att_{h_n} + def_{a_n}
\log(\lambda_{2n}) = att_{a_n} + def_{h_n}
\log(\lambda_{3n}) =\beta_0,
where the case \lambda_{3n}=0
reduces to
the double Poisson model (Baio & Blangiardo, 2010).
\lambda_{1n}, \lambda_{2n}
represent the
scoring rates for the home and the away team,
respectively, where: \mu
is the home effect;
the parameters att_T
and
def_T
represent the attack and the
defence abilities,
respectively, for each team T
, T=1,\ldots,N_T
;
the nested indexes h_{n}, a_{n}=1,\ldots,N_T
denote the home and the away team playing in the n
-th game,
respectively. Attack/defence parameters are imposed a
sum-to-zero constraint to achieve identifiability and
assigned some weakly-informative prior distributions:
att_T \sim \mathrm{N}(\mu_{att}, \sigma_{att})
def_T \sim \mathrm{N}(\mu_{def}, \sigma_{def}),
with hyperparameters \mu_{att}, \sigma_{att}, \mu_{def}, \sigma_{def}
.
Instead of using the marginal number of goals,
another alternative is to modelling directly
the score difference (y^{H}_{n}- y^{A}_{n})
.
We can use the Poisson-difference distribution
(or Skellam distribution) to model goal
difference in the n
-th match (Karlis & Ntzoufras, 2009):
y^{H}_{n}- y^{A}_{n}| \lambda_{1n}, \lambda_{2n} \sim PD(\lambda_{1n}, \lambda_{2n}),
and the scoring rates \lambda_{1n}, \lambda_{2n}
are
unchanged with respect to the bivariate/double Poisson model.
If we want to use a continue distribution, we can
use a student t distribution with 7 degrees of
freedom (Gelman, 2014):
y^{H}_{n}- y^{A}_{n} \sim t(7, ab_{h_{n}}-ab_{a(n)}, \sigma_y)
ab_t \sim \mathrm{N}(\mu + b \times {prior\_score}_t, sigma_{ab}),
where ab_t
is the overall ability for
the t
-th team, whereas prior\_score_t
is a prior measure of team's strength (for instance a
ranking).
These model rely on the assumption of static parameters.
However, we could assume dynamics in the attach/defence
abilities (Owen, 2011; Egidi et al., 2018, Macrì Demartino et al., 2024) in terms of weeks or seasons through the argument
dynamic_type
. In such a framework, for a given
number of times 1, \ldots, \mathcal{T}
, the models
above would be unchanged, but the priors for the abilities
parameters at each time \tau, \tau=2,\ldots, \mathcal{T},
would be:
att_{T, \tau} \sim \mathrm{N}({att}_{T, \tau-1}, \sigma_{att})
def_{T, \tau} \sim \mathrm{N}({def}_{T, \tau-1}, \sigma_{def}),
whereas for \tau=1
we have:
att_{T, 1} \sim \mathrm{N}(\mu_{att}, \sigma_{att})
def_{T, 1} \sim \mathrm{N}(\mu_{def}, \sigma_{def}).
Of course, the identifiability constraint must be imposed for
each time \tau
.
The current version of the package allows for the fit of a diagonal-inflated bivariate Poisson and a zero-inflated Skellam model in the spirit of (Karlis & Ntzoufras, 2003) to better capture draw occurrences. See the vignette for further details.
An object of class "stanFoot"
, which is a list containing:
fit
: The CmdStanFit
object returned by cmdstanr
.
data
: The input data.
stan_data
: The data list passed to Stan.
stan_code
: The Stan code of the underline model.
stan_args
: The optional cmdstanr
parameters passed to (...
).
alg_method
: The inference algorithm used to obtain the Bayesian estimates.
Leonardo Egidi legidi@units.it, Roberto Macrì Demartino roberto.macridemartino@deams.units.it, and Vasilis Palaskas vasilis.palaskas94@gmail.com.
Baio, G. and Blangiardo, M. (2010). Bayesian hierarchical model for the prediction of football results. Journal of Applied Statistics 37(2), 253-264.
Egidi, L., Pauli, F., and Torelli, N. (2018). Combining historical data and bookmakers' odds in modelling football scores. Statistical Modelling, 18(5-6), 436-459.
Gelman, A. (2014). Stan goes to the World Cup. From "Statistical Modeling, Causal Inference, and Social Science" blog.
Macrì Demartino, R., Egidi, L. and Torelli, N. Alternative ranking measures to predict international football results. Computational Statistics (2024), 1-19.
Karlis, D. and Ntzoufras, I. (2003). Analysis of sports data by using bivariate poisson models. Journal of the Royal Statistical Society: Series D (The Statistician) 52(3), 381-393.
Karlis, D. and Ntzoufras,I. (2009). Bayesian modelling of football outcomes: Using the Skellam's distribution for the goal difference. IMA Journal of Management Mathematics 20(2), 133-145.
Owen, A. (2011). Dynamic Bayesian forecasting models of football match outcomes with estimation of the evolution variance parameter. IMA Journal of Management Mathematics, 22(2), 99-113.
## Not run:
if (instantiate::stan_cmdstan_exists()) {
library(dplyr)
# Example usage with ranking
data("italy")
italy <- as_tibble(italy)
italy_2021 <- italy %>%
select(Season, home, visitor, hgoal, vgoal) %>%
filter(Season == "2021")
teams <- unique(italy_2021$home)
n_rows <- 20
# Create fake ranking
ranking <- data.frame(
periods = rep(1, n_rows),
team = sample(teams, n_rows, replace = FALSE),
rank_points = sample(0:60, n_rows, replace = FALSE)
)
ranking <- ranking %>%
arrange(periods, desc(rank_points))
colnames(italy_2021) <- c("periods", "home_team", "away_team", "home_goals", "away_goals")
fit_with_ranking <- stan_foot(
data = italy_2021,
model = "diag_infl_biv_pois",
ranking = ranking,
home_effect = TRUE,
prior_par = list(
ability = student_t(4, 0, NULL),
ability_sd = cauchy(0, 3),
home = normal(1, 10)
),
norm_method = "mad",
iter_sampling = 1000,
chains = 2,
parallel_chains = 2,
adapt_delta = 0.95,
max_treedepth = 15
)
# Print a summary of the model fit
print(fit_with_ranking, pars = c("att", "def"))
### Use Italian Serie A from 2000 to 2002
data("italy")
italy <- as_tibble(italy)
italy_2000_2002 <- italy %>%
dplyr::select(Season, home, visitor, hgoal, vgoal) %>%
dplyr::filter(Season == "2000" | Season == "2001" | Season == "2002")
colnames(italy_2000_2002) <- c("periods", "home_team", "away_team", "home_goals", "away_goals")
### Fit Stan models
## no dynamics, no predictions
fit_1 <- stan_foot(
data = italy_2000_2002,
model = "double_pois"
) # double poisson
print(fit_1, pars = c(
"home", "sigma_att",
"sigma_def"
))
fit_2 <- stan_foot(
data = italy_2000_2002,
model = "biv_pois"
) # bivariate poisson
print(fit_2, pars = c(
"home", "rho",
"sigma_att", "sigma_def"
))
fit_3 <- stan_foot(
data = italy_2000_2002,
mode = "skellam"
) # skellam
print(fit_3, pars = c(
"home", "sigma_att",
"sigma_def"
))
fit_4 <- stan_foot(
data = italy_2000_2002,
model = "student_t"
) # student_t
print(fit_4, pars = c("beta"))
## seasonal dynamics, no prediction
fit_5 <- stan_foot(
data = italy_2000_2002,
model = "double_pois",
dynamic_type = "seasonal"
) # double poisson
print(fit_5, pars = c(
"home", "sigma_att",
"sigma_def"
))
## seasonal dynamics, prediction for the last season
fit_6 <- stan_foot(
data = italy_2000_2002,
model = "double_pois",
dynamic_type = "seasonal",
predict = 170
) # double poisson
print(fit_6, pars = c(
"home", "sigma_att",
"sigma_def"
))
## other priors' options
# double poisson with
# student_t priors for teams abilities
# and laplace prior for the hyper sds
fit_p <- stan_foot(
data = italy_2000_2002,
model = "double_pois",
prior_par = list(
ability = student_t(4, 0, NULL),
ability_sd = laplace(0, 1),
home = normal(1, 10)
)
)
print(fit_p, pars = c(
"home", "sigma_att",
"sigma_def"
))
}
## End(Not run)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.