1. Introdução: A Falha Estrutural do Modelo Poisson Independente
Na modelagem quantitativa de futebol de associação, o modelo de Poisson independente — introduzido pioneiramente na análise esportiva por M. J. Maher em 1982 — representa a referência clássica para estimar a expectativa de gols e as distribuições de placares exatos. A premissa central de Maher afirma que os gols marcados pela equipe mandante () e pela equipe visitante () ao longo dos 90 minutos de tempo regulamentar seguem processos estocásticos pontuais de Poisson mutuamente independentes, parametrizados pelas expectativas marginais de gols e :
A independência é uma hipótese que deve ser avaliada num conjunto de dados definido. O estado da partida e a interação tática podem criar dependência, mas uma correção também pode ajustar ruído. A comparação precisa de distinguir mudanças na dependência de mudanças nas intensidades. Uma diferença entre modelos não estabelece erro de cotação nem o sinal do valor esperado numa casa.
Com intensidades ilustrativas e , Poisson independente dá e . São valores calculados do modelo. Avaliar subestimação ou superestimação exige frequências observadas e probabilidades da mesma amostra, com datas, competições e incerteza indicadas. A comparação de entradas fixas abaixo isola o efeito da correção dos placares baixos.
Em seu célebre artigo de 1997 publicado no Journal of the Royal Statistical Society, os estatísticos britânicos Mark J. Dixon e Stuart G. Coles apresentaram a solução canônica para este problema: "Modelling Association Football Scores and Inefficiencies in the Football Betting Market". O modelo Dixon-Coles introduziu um fator de dependência de placares baixos que pondera explicitamente os estados de baixa pontuação, preservando integralmente a distribuição marginal de Poisson, associado a um mecanismo de decaimento temporal exponencial para refletir a evolução das forças relativas das equipes ao longo do tempo.
Este artigo deduz a correção de quatro células, preserva as marginais Poisson, define a verossimilhança ponderada no tempo e apresenta um exemplo Python com restrições. A comparação numérica usa entradas fixas explícitas. O ajuste e as probabilidades podem ser reproduzidos separadamente de alegações sobre cotações atuais ou rentabilidade.
2. O Fator de Correção Bivariada de Dixon-Coles
Rho é um parâmetro de dependência dos placares baixos, não a correlação de Pearson entre as contagens de gols. Seu domínio depende das duas intensidades. A implementação de verossimilhança exige todos os fatores tau estritamente positivos. Com lambda=10, mu=2 e rho=-0.35, tau(0,1)=-2.5; todo o conjunto de parâmetros deve ser rejeitado, mesmo ao avaliar um placar alto. Truncar a célula em zero e reescalar definiria outra distribuição.
Para corrigir a falha da independência estocástica sem perder a eficiência de cálculo das distribuições de Poisson, Dixon e Coles formularam a seguinte função de probabilidade conjunta ajustada:
O fator de ajuste é uma função escalar discreta definida pontualmente sobre os resultados de baixa pontuação , parametrizada pelo parâmetro de dependência :
Demonstração Matemática da Conservação da Probabilidade Total
Para que o modelo seja estatisticamente válido, a introdução de deve respeitar dois axiomas fundamentais: não negatividade () e conservação da probabilidade total ().
Podemos demonstrar a conservação total decompondo o somatório em relação à base clássica de Poisson mais a perturbação sobre os quatro estados modificados:
Onde o termo de perturbação é avaliado diretamente sobre os estados :
Substituindo as probabilidades de Poisson não ajustadas , , e , e evidenciando o termo comum :
A soma das perturbações é identicamente nula. Logo, a probabilidade total de permanece perfeitamente conservada em qualquer parametrização válida.
Interpretação Física do Parâmetro de Correlação
O ajuste abaixo escolhe uma região de rho não positivo como ilustração, junto com as restrições de positividade dependentes das intensidades. É uma escolha do modelo, não um sinal universal inferido dos dados de futebol. A família também pode admitir rho positivo: com lambda=1.65, mu=1.15 e rho=0.10, os quatro fatores são 0.81025, 1.165, 1.115 e 0.9, todos positivos.
No domínio válido, rho negativo tem os seguintes efeitos algébricos em relação à independência com as mesmas intensidades:
- No estado : . Como , o produto , fazendo com que . A probabilidade de um empate sem gols é aumentada em relação ao Poisson ingênuo.
- No estado : . Como , . A probabilidade do placar 1 a 1 é explicitamente amplificada.
- Nos estados e : e . As probabilidades de vitórias magras de 1 a 0 são reduzidas, compensando com exatidão o ganho dos empates.
3. Parametrização das Equipes: Ataque, Defesa e Mando de Campo
No modelo de Dixon e Coles, as expectativas de gols (equipe mandante contra visitante ) e (equipe visitante contra mandante ) são estruturadas de forma log-linear através das forças latentes de cada clube:
Onde:
- : O índice ofensivo (força de ataque) da equipe . Um time com gera 30% mais chances de gol do que a média da competição.
- : A fragilidade defensiva (vulnerabilidade a sofrer gols) da equipe . Um time com concede 20% menos gols do que a média da liga.
- : O fator global de vantagem do mando de campo da liga. Em ligas europeias consolidadas, varia entre e .
A Restrição de Identificabilidade de Parâmetros
Um erro conceitual comum em modelos de classificação esportiva é a indeterminação de escala. Multiplicar todos os parâmetros de ataque por uma constante e dividir todas as defesas por gera valores idênticos para .
Para eliminar graus de liberdade espúrios e fixar o estimador de Máxima Verossimilhança, Dixon e Coles estabeleceram uma restrição de normalização sobre todos os clubes do campeonato:
Isso padroniza a força média de ataque da liga em exatamente 1,00, servindo de âncora matemática para todas as métricas relativas.
Fixar o ataque médio em um remove a simetria de escala entre ataque e defesa. Isso não demonstra um ótimo global único da verossimilhança, informação suficiente nos confrontos ou precisão preditiva. Os índices são relativos ao modelo e à amostra ajustados; só a normalização não torna comparáveis diretamente os índices de competições diferentes.
4. Decaimento Temporal Exponencial: A Função de Memória
Modelos ingênuos tratam partidas disputadas há dois anos com o mesmo peso de partidas realizadas no último domingo. No futebol real, transferências de atletas, trocas de comissão técnica e oscilações físicas alteram substancialmente o rendimento das equipes.
Para incorporar a dinâmica temporal sem perder a robustez amostral, Dixon e Coles introduziram uma função de decaimento temporal exponencial para cada partida histórica realizada na data , relativa à data de previsão :
Onde representa o tempo decorrido em dias e é o hiperparâmetro de decaimento contínuo.
A unidade temporal deve corresponder ao coeficiente. Este exemplo usa dias decorridos e uma taxa ilustrativa por dia, com meia-vida aproximada de 106.638 dias. Se as idades forem medidas em semanas, a taxa equivalente é por semana; manter o mesmo número mudaria os pesos. Essa aritmética não identifica um ótimo empírico. Escolha a taxa em dados cronológicos separados de validação e informe a métrica de avaliação.
5. Formulação de Máxima Verossimilhança (MLE)
Dado um histórico de jogos disputados, onde cada partida envolve o mandante , visitante , placar e data , a função de log-verossimilhança ponderada a ser maximizada é:
Ao expandir o logaritmo em seus termos aditivos, obtemos:
Como os termos fatoriais dependem apenas dos dados observados e não dos parâmetros , eles são ignorados durante a otimização computacional. Minimizamos, portanto, a log-verossimilhança negativa ponderada:
O exemplo usa SLSQP para lidar com a igualdade da escala de ataque e as desigualdades não lineares de tau. Verifica o sucesso do otimizador e as margens de viabilidade antes de armazenar os parâmetros. Uma terminação numérica bem-sucedida não certifica ótimo global nem desempenho em partidas futuras.
6. Comparação Numérica com Intensidades Fixas
Este cálculo usa parâmetros fixos , e . As probabilidades são somadas até 45 gols. As odds são os inversos das probabilidades de cada modelo. O exemplo é reproduzível sem cotações observadas de fechamento. Uma diferença entre dois modelos não estabelece vantagem sobre o mercado.
| Resultado | Probabilidade independente | Odd no modelo independente | Probabilidade Dixon-Coles | Odd no modelo Dixon-Coles | Variação, pontos percentuais |
|---|---|---|---|---|---|
| 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 |
| Vitória mandante | 49.026329% | 2.0397 | 47.641684% | 2.0990 | -1.384645 |
| Empate | 24.395242% | 4.0992 | 27.164533% | 3.6813 | +2.769290 |
| Vitória 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 contém as quatro células corrigidas, cujas alterações se cancelam. Sua probabilidade é 46.945368% nos dois modelos, com odd aproximada de 2.1301. Over 2.5 também não muda. A dependência pode alterar um total que contenha só parte dessas células, como Under 1.5, além dos empates e de ambas marcam. Reestimar as intensidades pode mudar Under 2.5 através delas. É um efeito diferente de aplicar rho mantendo as intensidades fixas.
7. Implementação Completa em Python: Classe DixonColesModel
Abaixo encontra-se o script completo em Python contendo a implementação da classe com otimização numérica via scipy.optimize.minimize:
O exemplo restringe os quatro fatores de placares baixos para cada par ordenado de equipes conhecidas, fixa o ataque médio em um e verifica sucesso e viabilidade do otimizador. Probabilidades inválidas são rejeitadas, não truncadas. A grade padrão usa quantis Poisson para manter a massa omitida abaixo de cerca de 2e-12, informa essa massa e conserva as probabilidades sem renormalização. Xi é uma taxa diária ilustrativa; sua escolha exige validação 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. Extensões Contemporâneas e Conclusão
Embora a formulação de Dixon e Coles tenha sido publicada em 1997, ela continua sendo a espinha dorsal de precificação de casas profissionais mundiais. Variações modernas introduzem cópulas contínuas arquimedianas (Frank e Gumbel) ou modelos bivariados de Poisson com choque comum (Karlis e Ntzoufras). Todavia, para precificação pré-jogo de 1X2, Dupla Hipótese, Placar Exato e Linhas de Totais, o ajuste do fator permanece incomparável em clareza analítica, precisão empírica e velocidade de execução estocástica.
