1. Introducción: La Falla Estructural del Modelo de Poisson Independiente
En el modelado cuantitativo de fútbol profesional, el marco de Poisson independiente —desarrollado formalmente para el análisis deportivo por M. J. Maher en 1982— representa el punto de partida fundamental para calcular probabilidades de goles y matrices de marcadores exactos. La hipótesis axiomática de Maher establece que los goles convertidos por el equipo local () y el equipo visitante () a lo largo de los 90 minutos reglamentarios constituyen dos procesos estocásticos de Poisson independientes, gobernados por las tasas de intensidad esperadas y :
La independencia es una hipótesis que debe evaluarse con un conjunto de datos definido. El estado del partido y la interacción táctica pueden crear dependencia, pero una corrección también puede ajustar ruido. La comparación debe distinguir cambios en la dependencia de cambios en las intensidades. Una diferencia entre modelos no establece un error de cuota ni el signo del valor esperado en una casa.
Con intensidades ilustrativas y , Poisson independiente da y . Son valores calculados del modelo. Evaluar subestimación o sobrestimación exige frecuencias observadas y probabilidades de la misma muestra, con fechas, competiciones e incertidumbre indicadas. La comparación de entradas fijas siguiente aísla el efecto de la corrección de los marcadores bajos.
En su artículo canónico de 1997 publicado en el Journal of the Royal Statistical Society, los estadísticos Mark J. Dixon y Stuart G. Coles resolvieron definitivamente este problema: "Modelling Association Football Scores and Inefficiencies in the Football Betting Market". El modelo Dixon-Coles formuló un factor de dependencia de marcadores bajos que ajusta de forma exacta las probabilidades de marcadores reducidos conservando la distribución marginal de Poisson, complementado por un factor de decaimiento temporal exponencial para capturar la evolución de los rendimientos de los clubes en el tiempo.
Este artículo deduce la corrección de cuatro celdas, conserva las marginales Poisson, define la verosimilitud ponderada en el tiempo y presenta un ejemplo Python con restricciones. La comparación numérica usa entradas fijas explícitas. El ajuste y las probabilidades pueden reproducirse por separado de afirmaciones sobre cuotas actuales o rentabilidad.
2. El Factor de Corrección Bivariada de Dixon-Coles
Rho es un parámetro de dependencia de los marcadores bajos, no la correlación de Pearson entre los goles. Su dominio depende de ambas intensidades. La implementación de verosimilitud exige todos los factores tau estrictamente positivos. Con lambda=10, mu=2 y rho=-0.35, tau(0,1)=-2.5; debe rechazarse todo el conjunto de parámetros, incluso al evaluar un marcador alto. Recortar la celda a cero y reescalar definiría otra distribución.
Para corregir la correlación en marcadores bajos sin abandonar las propiedades computacionales de Poisson, Dixon y Coles formularon la distribución bivariada corregida:
El factor de ajuste es un multiplicador escalar discreto aplicado a las cuatro combinaciones de marcadores reducidos , gobernado por el parámetro de correlación :
Demostración Matemática de la Conservación de la Masa de Probabilidad
Para asegurar la solidez axiomática del modelo, la matriz modificadora debe verificar estrictamente la condición de normalización total: .
Descomponemos la suma doble en la base no perturbada más la perturbación sobre los cuatro estados modificados:
Evaluamos analíticamente la perturbación sobre los estados :
Introduciendo las densidades , , , , y factorizando el exponente :
El término perturbativo se anula de forma exacta e idéntica. Por lo tanto, la probabilidad total acumulada suma siempre el sin necesidad de renormalizaciones artificiales.
Interpretación Física del Parámetro de Correlación
El ajuste siguiente elige una región de rho no positivo como ilustración, junto con las restricciones de positividad dependientes de las intensidades. Es una elección del modelo, no un signo universal inferido de datos de fútbol. La familia también puede admitir rho positivo: con lambda=1.65, mu=1.15 y rho=0.10, los cuatro factores son 0.81025, 1.165, 1.115 y 0.9, todos positivos.
En el dominio válido, rho negativo tiene los siguientes efectos algebraicos respecto a la independencia con las mismas intensidades:
- Para el marcador : . Al ser , el producto , con lo cual . La probabilidad del 0–0 se incrementa directamente.
- Para el marcador : . Como , . La frecuencia de empates con un gol por bando aumenta de forma proporcional.
- Para los marcadores y : y . Las victorias mínimas por 1–0 se corrigen a la baja, garantizando el equilibrio global.
3. Modelado de Fuerzas de Equipo: Ataque, Defensa y Ventaja Local
Las intensidades esperadas de goles (local frente a visitante ) y (visitante frente a local ) se parametrizan multiplicativamente:
Donde:
- : Coeficiente ofensivo (potencia de ataque) del equipo . Un valor implica una producción goleadora un 25% superior al promedio de la liga.
- : Coeficiente defensivo (concesión de goles) del equipo . Un valor indica que el equipo recibe un 15% menos de goles que la media.
- : Factor macro de ventaja de campo. En las ligas europeas de primer orden, oscila habitualmente entre y .
La Restricción de Identificabilidad
Para evitar la indeterminación de escala entre parámetros de ataque y defensa, Dixon y Coles impusieron la restricción canónica sobre el conjunto de los equipos participantes:
Fijar el ataque medio en uno elimina la simetría de escala entre ataque y defensa. No demuestra un óptimo global único de la verosimilitud, información suficiente en los enfrentamientos ni precisión predictiva. Los índices son relativos al modelo y a la muestra ajustados; la normalización por sí sola no hace directamente comparables índices de competiciones distintas.
4. Ponderación Temporal Exponencial: El Parámetro de Memoria
Los resultados de hace dos temporadas no pueden tener la misma trascendencia analítica que los partidos disputados hace tres semanas. Dixon y Coles introdujeron una ponderación de decaimiento temporal continuo para cada partido histórico jugado en la fecha , respecto al día de análisis :
Donde es el intervalo transcurrido en días y es el coeficiente de olvido estocástico.
La unidad temporal debe corresponder al coeficiente. Este ejemplo usa días transcurridos y una tasa ilustrativa por día, con semivida aproximada de 106.638 días. Si las edades se expresan en semanas, la tasa equivalente es por semana; mantener el mismo número cambiaría los pesos. Esta aritmética no identifica un óptimo empírico. Elija la tasa con datos cronológicos de validación separados e indique la métrica de evaluación.
5. Optimización por Máxima Verosimilitud (MLE)
Dado un conjunto histórico de encuentros, la función de verosimilitud logarítmica ponderada se define como:
Omitiendo los factores factoriales constantes, el algoritmo minimiza la log-verosimilitud negativa ponderada:
El ejemplo usa SLSQP para tratar la igualdad de escala del ataque y las desigualdades no lineales de tau. Comprueba el éxito del optimizador y los márgenes de viabilidad antes de guardar los parámetros. Una terminación numérica satisfactoria no certifica un óptimo global ni el rendimiento en partidos futuros.
6. Comparación Numérica con Intensidades Fijas
Este cálculo usa parámetros fijos , y . Las probabilidades se suman hasta 45 goles. Las cuotas son los inversos de las probabilidades de cada modelo. El ejemplo es reproducible sin precios de cierre observados. Una diferencia entre dos modelos no establece una ventaja frente al mercado.
| Resultado | Probabilidad independiente | Cuota del modelo independiente | Probabilidad Dixon-Coles | Cuota del modelo Dixon-Coles | Cambio, puntos porcentuales |
|---|---|---|---|---|---|
| 0–0 | 6.081006% | 16.4446 | 7.465651% | 13.3947 | +1.384645 |
| 1–1 | 11.538709% | 8.6665 | 12.923355% | 7.7379 | +1.384645 |
| 1–0 | 10.033660% | 9.9665 | 8.649015% | 11.5620 | -1.384645 |
| 0–1 | 6.993157% | 14.2997 | 5.608512% | 17.8300 | -1.384645 |
| Victoria local | 49.026329% | 2.0397 | 47.641684% | 2.0990 | -1.384645 |
| Empate | 24.395242% | 4.0992 | 27.164533% | 3.6813 | +2.769290 |
| Victoria visitante | 26.578428% | 3.7624 | 25.193783% | 3.9692 | -1.384645 |
| Menos de 2.5 | 46.945368% | 2.1301 | 46.945368% | 2.1301 | +0.000000 |
Under 2.5 contiene las cuatro celdas corregidas, cuyos cambios se cancelan. Su probabilidad es del 46.945368% en ambos modelos, con cuota aproximada de 2.1301. Over 2.5 tampoco cambia. La dependencia puede alterar un total que contenga solo parte de esas celdas, como Under 1.5, además de los empates y ambos marcan. Reestimar las intensidades puede cambiar Under 2.5 a través de ellas. Es un efecto distinto de aplicar rho manteniéndolas fijas.
7. Implementación en Python: DixonColesModel
El ejemplo restringe los cuatro factores de marcadores bajos para cada par ordenado de equipos conocidos, fija el ataque medio en uno y comprueba éxito y viabilidad del optimizador. Las probabilidades inválidas se rechazan, no se recortan. La cuadrícula predeterminada usa cuantiles Poisson para dejar masa omitida inferior a aproximadamente 2e-12, informa esa masa y conserva las probabilidades sin renormalización. Xi es una tasa diaria ilustrativa; elegirla requiere validación separada.
import numpy as np
from scipy.optimize import minimize
from scipy.stats import poisson
class DixonColesModel:
def __init__(self, xi=0.0065):
# Illustrative daily decay rate, not a validated universal optimum.
if not np.isfinite(xi) or xi < 0:
raise ValueError("xi must be a finite nonnegative daily rate")
self.xi = xi
self.teams = []
self.team_indices = {}
self.params = None
self.gamma = 1.25
self.rho = -0.11
@staticmethod
def tau(x, y, lambda_, mu, rho):
# Check the whole joint model, including unobserved low-score cells.
if not np.all(np.isfinite([lambda_, mu, rho])) or min(lambda_, mu) <= 0:
raise ValueError("Intensities must be finite and positive")
factors = [1 - lambda_ * mu * rho, 1 + lambda_ * rho,
1 + mu * rho, 1 - rho]
if min(factors) <= 0:
raise ValueError("rho is outside the strictly positive tau domain")
return {(0, 0): factors[0], (0, 1): factors[1],
(1, 0): factors[2], (1, 1): factors[3]}.get((x, y), 1.0)
def bivariate_prob(self, x, y, lambda_, mu, rho):
if not np.all(np.isfinite([x, y])) or min(x, y) < 0 or int(x) != x or int(y) != y:
raise ValueError("Goals must be nonnegative integers")
factor = self.tau(x, y, lambda_, mu, rho)
return factor * poisson.pmf(x, lambda_) * poisson.pmf(y, mu)
def _unpack_params(self, params, n_teams):
return (params[:n_teams], params[n_teams:2*n_teams],
params[2*n_teams], params[2*n_teams + 1])
def fit(self, matches):
self.params = None
if not matches:
raise ValueError("At least one match is required")
for m in matches:
goals = [m['home_goals'], m['away_goals']]
age = m.get('days_ago', 0.0)
if (m['home'] == m['away'] or not np.all(np.isfinite(goals + [age]))
or min(goals) < 0 or any(int(v) != v for v in goals) or age < 0):
raise ValueError("Invalid teams, goals or match age")
self.teams = sorted({m['home'] for m in matches} | {m['away'] for m in matches})
self.team_indices = {team: i for i, team in enumerate(self.teams)}
n_teams = len(self.teams)
init = np.concatenate([np.ones(n_teams), np.ones(n_teams), [1.25, -0.10]])
def tau_constraints(params):
alpha, beta, gamma, rho = self._unpack_params(params, n_teams)
values = []
# Every ordered pairing of known teams must define a valid model.
for h in range(n_teams):
for a in range(n_teams):
if h == a:
continue
lam, mu = alpha[h] * beta[a] * gamma, alpha[a] * beta[h]
values.extend([1 - lam * mu * rho, 1 + lam * rho,
1 + mu * rho, 1 - rho])
return np.asarray(values) - 1e-8
def loss_fn(params):
alpha, beta, gamma, rho = self._unpack_params(params, n_teams)
nll = 0.0
for m in matches:
h, a = self.team_indices[m['home']], self.team_indices[m['away']]
lam, mu = alpha[h] * beta[a] * gamma, alpha[a] * beta[h]
try:
factor = self.tau(m['home_goals'], m['away_goals'], lam, mu, rho)
except ValueError:
return 1e12 # Reject an infeasible optimizer trial, never clip a probability.
weight = np.exp(-self.xi * m.get('days_ago', 0.0))
log_prob = (np.log(factor) + poisson.logpmf(m['home_goals'], lam)
+ poisson.logpmf(m['away_goals'], mu))
nll -= weight * log_prob
return float(nll)
constraints = [
{'type': 'ineq', 'fun': tau_constraints},
{'type': 'eq', 'fun': lambda p: np.mean(p[:n_teams]) - 1.0},
]
bounds = [(0.05, 3.5)] * (2*n_teams) + [(1.0, 1.8), (-0.35, 0.0)]
res = minimize(loss_fn, init, method='SLSQP', bounds=bounds,
constraints=constraints, options={'maxiter': 1000, 'ftol': 1e-9})
if (not res.success or not np.all(np.isfinite(res.x)) or not np.isfinite(res.fun)
or np.min(tau_constraints(res.x)) < -1e-9
or abs(np.mean(res.x[:n_teams]) - 1) > 1e-8):
raise RuntimeError(f"No valid converged fit: {res.message}")
self.params = res.x
_, _, self.gamma, self.rho = self._unpack_params(res.x, n_teams)
return res
def predict_score_matrix(self, home_team, away_team, max_goals=None):
if self.params is None:
raise ValueError("Fit the model first")
alpha, beta, gamma, rho = self._unpack_params(self.params, len(self.teams))
h, a = self.team_indices[home_team], self.team_indices[away_team]
lam, mu = alpha[h] * beta[a] * gamma, alpha[a] * beta[h]
self.tau(0, 0, lam, mu, rho)
if max_goals is None:
max_goals = int(max(poisson.ppf(1 - 1e-12, lam), poisson.ppf(1 - 1e-12, mu)))
if not isinstance(max_goals, (int, np.integer)) or max_goals < 1:
raise ValueError("max_goals must be an integer of at least one")
goals = np.arange(max_goals + 1)
matrix = np.outer(poisson.pmf(goals, lam), poisson.pmf(goals, mu))
for x, y in [(0, 0), (0, 1), (1, 0), (1, 1)]:
matrix[x, y] *= self.tau(x, y, lam, mu, rho)
# Keep the original probability mass; disclose the omitted grid tail.
self.omitted_mass = float(max(0.0, 1 - matrix.sum()))
return matrix, lam, mu
def predict_1x2(self, home_team, away_team):
matrix, lam, mu = self.predict_score_matrix(home_team, away_team)
home, draw, away = np.tril(matrix, -1).sum(), np.trace(matrix), np.triu(matrix, 1).sum()
return {'home_win': float(home), 'draw': float(draw), 'away_win': float(away),
'fair_odds_home': float(1/home), 'fair_odds_draw': float(1/draw),
'fair_odds_away': float(1/away), 'lambda': float(lam), 'mu': float(mu),
'omitted_probability_mass': self.omitted_mass}
8. Conclusiones Cuantitativas
La formulación de Dixon-Coles constituye una piedra angular indispensable en las apuestas de valor y el arbitraje estadístico de fútbol. Eliminar la suposición errónea de independencia en tanteos cerrados permite descubrir ineficiencias monetizables sistemáticas en los mercados de cuotas de partido, hándicaps asiáticos y totales de goles.
