Em época de Copa do Mundo, o mercado de apostas sempre se torna incrivelmente aquecido.

As casas de apostas investem cada vez mais em métodos preditivos para melhorar suas odds, de forma a combinar a atratividade para o apostador e a contenção de eventuais perdas.

Sendo eu mesmo um apostador, criei esse projeto de predição de estatísticas em jogos.

Esse notebook visa criar três modelos para fazer predições sobre os seguintes dados estatísticos de jogos de futebol da Copa do Mundo.

Bibliotecas usadas

Usaremos os seguintes pacotes R

library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(readr)
library(corrplot)
## corrplot 0.92 loaded

Carrega o dataset, cria o vetor com os times da edição e filtra os times participantes da Copa do Mundo de 2022

dataset_estatisticas_copa <- read_csv("dataset_copa2010-2022.csv", show_col_types = FALSE)

paises_2022 <- unique(dataset_estatisticas_copa$time[dataset_estatisticas_copa$ano_copa == 2022])

dataset_treino <- dataset_estatisticas_copa %>%
  filter(time %in% paises_2022)

O dataset usado possui dados de jogos desde a Copa de 2010. Pode eventualmente haver alguma irregularidade de dados, pois o dataset recebeu algumas intervenções manuais, mas nada que implique em prejuízos ao trabalho.

Descoberta de correlações

Para selecionarmos as melhores variáveis para integrar os nossos modelos de regressão, é apropriado usarmos corrplot para verificarmos as correlações entre os dados

Criação da matriz de dados e plot de correlação

matriz_estatisticas <- dataset_estatisticas_copa %>%
  select(12, c(15:23)) %>%
  as.matrix()

correlacao_matriz <- cor(matriz_estatisticas, use = "complete.obs", method = "pearson")

corrplot(correlacao_matriz, 
         diag = FALSE, method = 'circle', type = "lower")

Como podemos observar no plot, há uma correlação direta, significativa e embasada na lógica entre:

  • Cartões amarelos e quantidade de faltas (faz sentido, pois o maior fator gerador de cartões são as faltas);

  • Escanteios a favor e posse de bola, chutes e chutes a gol (faz sentido, pois para gerar escanteios o time precisa ter a bola, finalizar e alvejar o gol);

  • Abertura do placar com precisão dos chutes (dado obtido dividindo chutes no gol pelo total de chutes) e chutes no gol (faz sentido, pois para marcar o primeiro gol é preciso investir em tentativas de finalização e acertar o gol).

Com base nisso, vamos criar os modelos de regressão com fórmulas que incluam essas variáveis.

Modelos

Usaremos a regressão de Poisson, pois estamos tratando de dados obtidos numa janela de tempo específica (90 minutos de jogo) e independentes entre os eventos (um dado de um time em determinado jogo não influencia no próximo jogo desse time, pelo menos não no âmbito da teoria estatística).

Modelo de cartões amarelos

modelo_amarelos <- glm(cartoes_amarelos ~ time + faltas_cometidas, 
                       data = dataset_treino, family = "poisson")

Modelo de escanteios

modelo_escanteios <- glm(escanteios_afavor ~ time + posse_bola + chutes_gol + chutes, 
                         data = dataset_treino, family =  "poisson")

Modelo de primeiro time a marcar

Para esse modelo usaremos a regressão logística, pelo fato da variável alvo ser binária (sim ou não, simbolizada por 1 e 0).

modelo_primeiro_gol <- glm(abriu_placar ~ time + chutes_gol + precisao_chute, 
                           data = dataset_treino, family = "binomial")

Datasets de testes

Quais dados passaremos para o modelo nos entregar predições para cartões amarelos, escanteios e quem marca o primeiro gol?

Optaremos por criar datasets de teste com dados que representem a média de cada dado pra cada time, de acordo com os dados do dataset de treino.

Parece ser algo apropriado na minha concepção, mas você pode modificar o código para o mínimo, máximo ou mesmo criar esse dataset com dados da sua preferência.

dataset_teste_amarelos <- dataset_treino %>%
  group_by(time) %>%
  summarise(faltas_cometidas = mean(faltas_cometidas, na.rm = TRUE))

print(dataset_teste_amarelos)
## # A tibble: 32 × 2
##    time       faltas_cometidas
##    <chr>                 <dbl>
##  1 Argentina              13  
##  2 Australia              15.3
##  3 Belgium                14  
##  4 Brazil                 14.0
##  5 Cameroon               13.7
##  6 Canada                 11  
##  7 Costa Rica             14.5
##  8 Croatia                14.2
##  9 Denmark                13.6
## 10 Ecuador                15  
## # … with 22 more rows
dataset_teste_escanteios <- dataset_treino %>%
  group_by(time) %>%
  summarise(posse_bola = mean(posse_bola, na.rm = TRUE),
            chutes_gol = mean(chutes_gol, na.rm = TRUE),
            chutes = mean(chutes, na.rm = TRUE))

print(dataset_teste_escanteios)
## # A tibble: 32 × 4
##    time       posse_bola chutes_gol chutes
##    <chr>           <dbl>      <dbl>  <dbl>
##  1 Argentina        58.1       6.52  13.3 
##  2 Australia        44.8       3.08   7.85
##  3 Belgium          53.8       6.47  13.3 
##  4 Brazil           55.8       7.55  15.9 
##  5 Cameroon         47.7       4.33  10.9 
##  6 Canada           52         1.33  11.3 
##  7 Costa Rica       37.9       3      6.55
##  8 Croatia          54.6       4.71  12.3 
##  9 Denmark          49         4.5   10.3 
## 10 Ecuador          50.5       3.67   9.5 
## # … with 22 more rows
dataset_teste_primeiro_gol <- dataset_treino %>%
  group_by(time) %>%
  summarise(chutes_gol = mean(chutes_gol, na.rm = TRUE), 
            precisao_chute = mean(precisao_chute, na.rm = TRUE))

print(dataset_teste_primeiro_gol)
## # A tibble: 32 × 3
##    time       chutes_gol precisao_chute
##    <chr>           <dbl>          <dbl>
##  1 Argentina        6.52          0.503
##  2 Australia        3.08          0.370
##  3 Belgium          6.47          0.478
##  4 Brazil           7.55          0.482
##  5 Cameroon         4.33          0.392
##  6 Canada           1.33          0.115
##  7 Costa Rica       3             0.434
##  8 Croatia          4.71          0.368
##  9 Denmark          4.5           0.463
## 10 Ecuador          3.67          0.395
## # … with 22 more rows

Resultados

Obtemos os resultados usando predict, e montamos o dataset com os resultados com cbind e dplyr.

resultados_amarelos <- cbind(dataset_teste_amarelos, 
                            amarelos_previstos = round(predict(modelo_amarelos, newdata = dataset_teste_amarelos, type = "response"), digits = 4))

resultados_amarelos <- resultados_amarelos %>% 
  select(time, amarelos_previstos) %>%
  arrange(desc(amarelos_previstos)) %>%
  as_tibble()

print(resultados_amarelos)
## # A tibble: 32 × 2
##    time         amarelos_previstos
##    <chr>                     <dbl>
##  1 Serbia                     3.28
##  2 Canada                     2.59
##  3 Saudi Arabia               2.39
##  4 Qatar                      2.28
##  5 Netherlands                2.23
##  6 Ghana                      2.20
##  7 Mexico                     2.09
##  8 Australia                  2.01
##  9 Iran                       1.99
## 10 Costa Rica                 1.98
## # … with 22 more rows
resultados_escanteios <- cbind(dataset_teste_escanteios, 
                               escanteios_previstos = round(predict(modelo_escanteios, newdata = dataset_teste_escanteios, type = "response"), digits = 4))

resultados_escanteios <- resultados_escanteios %>%
  select(time, escanteios_previstos) %>%
  arrange(desc(escanteios_previstos)) %>% 
  as_tibble()

print(resultados_escanteios)
## # A tibble: 32 × 2
##    time        escanteios_previstos
##    <chr>                      <dbl>
##  1 Brazil                      6.81
##  2 Spain                       6.36
##  3 England                     6.19
##  4 Germany                     6.08
##  5 Belgium                     6.07
##  6 Argentina                   5.89
##  7 South Korea                 5.15
##  8 Portugal                    5.11
##  9 Croatia                     5.03
## 10 Tunisia                     4.91
## # … with 22 more rows
resultados_primeiro_gol <- cbind(dataset_teste_primeiro_gol,
                                 chance_primeiro_gol_prevista = round(predict(modelo_primeiro_gol, newdata = dataset_teste_primeiro_gol, type = "response"), digits = 4))

resultados_primeiro_gol <- resultados_primeiro_gol %>%
  select(time, chance_primeiro_gol_prevista) %>%
  arrange(desc(chance_primeiro_gol_prevista)) %>%
  as_tibble()

print(resultados_primeiro_gol)
## # A tibble: 32 × 2
##    time        chance_primeiro_gol_prevista
##    <chr>                              <dbl>
##  1 Argentina                          0.799
##  2 Spain                              0.730
##  3 Brazil                             0.689
##  4 Portugal                           0.630
##  5 Germany                            0.605
##  6 France                             0.600
##  7 Uruguay                            0.581
##  8 Senegal                            0.578
##  9 Belgium                            0.534
## 10 Netherlands                        0.531
## # … with 22 more rows

Algumas interpretações dos nossos resultados:

Observações

Fazer predições de Copa do Mundo possui alguns aspectos pecualiares em comparação a ligas nacionais.

Como o universo dos times participantes varia em cada edição com as eliminatórias regionais, os dados históricos disponíveis dos times em Copas varia consideravelmente, culminando em times com mais e menos dados disponíveis.

Esse fator é observado especialmente com o Brasil, que até hoje foi o único país a jogar todas as edições, e também com País de Gales e Canadá, que raramente participam do torneio.

No entanto, temos aqui um estudo que nos entrega predições interessantes para estratégias de apostas, análises esportivas ou mesmo o puro estudo de estatística e ciência de dados aplicada ao esporte.

Sinta-se livre para modificar o código ou tomá-lo como inspiração para criar o seu próprio projeto. Até a próxima!