Source code for penaltyblog.models.goal_expectancy
import numpy as np
from scipy.optimize import minimize
from scipy.stats import poisson
[docs]
def goal_expectancy(
home: float, draw: float, away: float, dc_adj: bool = True, rho: float = 0.001
) -> dict:
"""
Estimates the bookmaker's goal expectencies for the home team and away team based on the
home, draw, and away probabilities.
Parameters
-----------
home : float
Probability of home win
draw : float
Probability of draw
away : float
Probability of away win
dc_adj : bool
Whether to apply the Dixon and Coles adjustment
rho : float
The value for rho within the Dixon and Coles adjustment if dc_adj is True
Returns
----------
Dictionary containing home team's goal expectancy, away team's goal expectancy,
the mean squared error between actual probabilities and estimated probabilities,
and whether the minimizer returned as successful or not
"""
# set up the basic options for the solver so we give up
# after 1000 attempts and don't log to screen
options = {
"maxiter": 1000,
"disp": False,
}
res = minimize(
fun=_mse,
x0=[0.5, -0.5],
args=(home, draw, away, dc_adj, rho),
options=options,
)
output = {
"home_exp": np.exp(res["x"][0]),
"away_exp": np.exp(res["x"][1]),
"error": res["fun"],
"success": res["success"],
}
return output
def _mse(
params: list,
home: float,
draw: float,
away: float,
dc_adj: bool = True,
rho: float = 0.001,
):
"""
Loss function used internally by the `goal_expectancy function` to
calculate the mean squared error of the estimate
"""
exp_params = np.exp(params)
mu1 = poisson(exp_params[0]).pmf(np.arange(0, 15))
mu2 = poisson(exp_params[1]).pmf(np.arange(0, 15))
mat = np.outer(mu1, mu2)
if dc_adj:
# apply Dixon and Coles adjustment
mat[0, 0] *= 1 - exp_params[0] * exp_params[1] * rho
mat[0, 1] *= 1 + exp_params[0] * rho
mat[1, 0] *= 1 + exp_params[1] * rho
mat[1, 1] *= 1 - rho
pred = np.array(
[
np.sum(np.tril(mat, -1)), # home
np.sum(np.diag(mat)), # draw
np.sum(np.triu(mat, 1)),
]
) # away
obs = np.array([home, draw, away])
mse = np.mean((pred - obs) ** 2)
return mse