Trabalho de Análise de Sobrevivência

Pedro Garcia

Universidade Federal de Uberlândia

13-11-23

Modelando a Invencibilidade dos Times do Campeonato Brasileiro de Futebol com Análise de Sobrevivência

Introdução

A análise de sobrevivência é uma técnica estatística poderosa usada em várias áreas, incluindo medicina, engenharia, economia e ciências sociais, para investigar o tempo até que um evento específico ocorra.

Esse evento pode ser qualquer coisa, desde o início de uma doença até a falha de um equipamento ou até mesmo o tempo que leva para que um cliente faça uma compra após sua primeira visita a um site. A análise de sobrevivência é particularmente útil quando os dados incluem observações censuradas, o que significa que nem todas as observações têm um tempo de falha conhecido.

O objetivo é estimar curvas de sobrevivência no âmbito futebolístico, especificamente no campeonato brasileiro, onde desejamos conhecer a probabilidade de sobrevida dos times não perderem uma sequência de partidas. Logo, nosso tempo de sobrevivência será o número de partidas que um time ganha ou empata até que ocorra uma derrota.

Assim sendo, consideraremos em nossa modelagem a influência de diversas variáveis candidatas a terem impacto na invencibilidade das equipes, bem como : local da partida (se o time era mandante ou visitante), gols marcados e sofridos nas últimas n partidas e entre outras.

Nesta pesquisa teremos censura na seguinte situação. Como o estudo está sendo realizado com o campeonato em andamento, as observações que vem de uma invencibilidade, ou seja, até o último momento da coleta dos dados não ocorreu uma derrota, serão caracterizados como dados censurados. Há uma outra particularidade bem interessante no nosso experimento, em que os indiviudos poderão fazer parte dos dados novamente após terem uma quebra de invencibilidade.

Logo, se um determinado time começa o campeonato com 2 partidas sem derrota e no tempo 3 ele tem um fracasso, assim ele é incluído nos dados nesse tempo, iniciando novamente a contagem de sucessos a partir da 4ª rodada. Esse processo é realizado sucessivamente até a último dia disputado do campeonato, sendo uma metodologia denominada como análise de sobrevivência com dados de repetição.

Metodologia

Nosso ambiente de pesquisa para tratamento e modelagem dos dados será o R. A base de dados inicial para nossa pesquisa foi coletada no perfil do GitHub futpythontrader, onde há informações dos jogos como time mandante, time visitante, data, resultado da partida e diversos tipos de odds. A partir desses dados criaremos novas variáveis que possam ser significativas como a média móvel de gols marcados e sofridos pelas equipes nos últimos 6 jogos.

Após finalizado esse processo, faremos uma adequação da base de dados para um padrão de análise de sobrevivência, onde teremos todas as variáveis em função do tempo (número de partidas) em que o time perdeu a invencibilidade, além de implementar a informação de observações censuradas.

Portanto, concluindo todo esse processamento dos dados, iniciaremos a etapa de Modelagem, onde consideraremos Modelos de Regressão com diversos métodos como o Exponencial, Weibull, Log Normal e entre outros. Com finalidade de selecionar o que melhor modela nosso problema. Consequentemente, faremos uma seleção de variáveis utilizando as funções drop1 e step do R, buscando ter no nosso ajuste final a combinação das covariáveis que fornecem as melhores estimativas de sobrevivência.

Ajustado nosso modelo, poderemos agora fazer previsões sobre qual time tem uma maior probabilidade de sobrevida no geral, além de ver o quanto cada uma das variáveis impactam na curva de sobrevivência. Em uma outra abordagem, poderiamos estimar a probabilidadede um time que está disputando o campeonato brasileiro que está em andamento e verificar a chance dele continuar em uma sequência de invencibilidade, e assim constratar essa informação com as odds estimadas das casas de apostas, com objetivo de buscar oportunidades para “investimento” que tem valor.

Resultados

Importando Bibliotecas

require(survival)
require(stringr)
require(dplyr)
require(flexsurv)

Base de Dados Inicial

tail(arrange(dados[dados$Home == 'São Paulo' | dados$Away == 'São Paulo' ,var],Date),7)
                  Date            Home             Away Goals_H_FT Goals_A_FT
56 2023-08-06 16:00:00       São Paulo Atlético Mineiro          0          2
57 2023-08-13 18:30:00        Flamengo        São Paulo          1          1
58 2023-08-19 16:00:00       São Paulo         Botafogo          0          0
59 2023-08-27 16:00:00 América Mineiro        São Paulo          2          1
60 2023-09-13 21:30:00   Internacional        São Paulo          2          1
61 2023-09-20 21:30:00       São Paulo        Fortaleza          1          2
62 2023-09-27 19:00:00       São Paulo         Coritiba          2          1
   Odd_H_FT Odd_D_FT Odd_A_FT XG_Home_Pre XG_Away_Pre ShotsOnTarget_H
56     2.15     3.20     3.75        1.93        1.39               5
57     1.62     4.00     7.20        1.85        1.50               8
58     2.05     3.50     3.60        1.96        1.20               5
59     2.90     3.25     2.50        1.79        1.43               5
60     2.25     3.10     3.50        1.54        1.46               6
61     1.95     3.40     3.60        1.92        1.48               8
62     1.50     4.50     6.50        1.92        1.27               7

Transformação para Formato Adequado

dados = read.table(file = paste("Dados_Sobrev_Br_2023.csv"), sep = ";",
                   header = T,encoding = "latin1",dec = ",",stringsAsFactors = TRUE)
arrange(dados[dados$Home == 'SãoPaulo' | dados$Away == 'SãoPaulo',var2],Date)
         Date           Home            Away tempo censura
1  2023-04-15       Botafogo        SãoPaulo     1       1
2  2023-04-22       SãoPaulo  AméricaMineiro     1       1
3  2023-05-07       SãoPaulo   Internacional     4       1
4  2023-05-20       SãoPaulo     VascodaGama     1       1
5  2023-05-27       SãoPaulo           Goiás     3       1
6  2023-06-04         Grêmio        SãoPaulo     8       1
7  2023-06-11       SãoPaulo       Palmeiras     1       1
8  2023-06-21       SãoPaulo      AtléticoPR     3       1
9  2023-06-24       Cruzeiro        SãoPaulo     2       1
10 2023-07-01       SãoPaulo      Fluminense     5       1
11 2023-07-16       SãoPaulo          Santos     2       1
12 2023-07-22         Cuiabá        SãoPaulo     4       1
13 2023-08-06       SãoPaulo AtléticoMineiro     2       1
14 2023-08-27 AméricaMineiro        SãoPaulo     3       1
15 2023-09-13  Internacional        SãoPaulo     1       1
16 2023-09-20       SãoPaulo       Fortaleza     1       1
17 2023-09-20       SãoPaulo       Fortaleza     3       0
18 2023-09-27       SãoPaulo        Coritiba     2       0
19 2023-09-27       SãoPaulo        Coritiba     1       1
20 2023-09-27       SãoPaulo        Coritiba     1       1

Descrevendo os Dados

Comentando brevemente sobre as covariáveis que serão utilizadas para modelagem temos o seguinte: Todas variáveis que se referem a Home ou H estão em função do time que é mandante, podemos replicar o mesmo para Away ou A que fazem alusão as métricas das equipes visitantes.

Dentre desse conjunto há variáveis a nível de especulação antes da partida, como as que se referem a Odds e XG, onde a Odd reflete a chance dos times ganharem, empatarem ou perderem a partida, já o XG se refere a uma medida de espectativa de gols de cada equipe.

Além dessas variáveis temos algumas que indicam o desempenho dos times como: GM (Gols Marcados), GS (Gols Sofridos), PTS (Pontos), SH (Chutes), SHG (Chutes no Gol), efc (Medida criada para captar a eficiência dos times em relação as oportunidades criadas)

Para complementar as features destacadas anteriormente, temos essas variáveis filtradas de maneira histórica como last e mm_6, onde last recebe a respectiva métrica do jogo anterior daquela equipe, um exemplo é last_PTS_H, que é o total de pontos conquistados pela equipe mandante na partida anterior ao qual será disputada. A mm_6 captura uma média móvel dos últimos 6 jogos anteriores em relação ao time e métrica, um exemplo, efc_mm_6_A é a média da eficiência do visitante nos últimos 6 jogos.

Por fim temos variáveis indicadoras (dummies) em relação as equipes, que nos fornecem a seguinte informação, na coluna Botafogo por exemplo, se temos 1 quer dizer que o Botafogo é o time que perdeu a invencibilidade em determinado tempo t, caso seja 0 não é o Botafogo. Na variável BotafogoCasa, se temos o valor 1, nos informa que o Botafogo estava jogando aquela partida como mandante, indenpendente de quem tenha perdido a invencibilidade naquela observação, caso seja 0, não era o Botafogo o mandante. Analogamente, BotafogoFora, nos da a informação se era ou não o Botafogo o visitante naquela observação, e isso pode ser replicado para todoso os demais participantes do campeonato brasileiro.

Esse tipo de variável pode nos informar futuramente se considerar x time como mandante ou visitante tem um impacto geral na curva de sobrevivência dos adversários, além da própria variável time que é neutra, que nos fornece se aquele time no geral tem sua curva prejudicada ou potencializada pela sua própria performance no campeonato.

Visualizando a Descrição dos Dados

str(dados)
'data.frame':   196 obs. of  99 variables:
 $ Away               : Factor w/ 20 levels "AméricaMineiro",..: 19 19 17 19 19 2 19 19 13 8 ...
 $ Date               : Factor w/ 64 levels "2023-04-15","2023-04-16",..: 1 21 24 27 37 43 50 53 59 64 ...
 $ Home               : Factor w/ 20 levels "AméricaMineiro",..: 5 15 19 9 10 19 1 16 19 19 ...
 $ Rodada             : int  1 9 10 12 16 18 21 23 24 22 ...
 $ FT_Odds_H          : num  2.7 2.15 3 2.2 2.63 2.15 2.9 2.25 1.95 1.5 ...
 $ FT_Odds_D          : num  3.1 3.38 3 3.2 2.98 3.2 3.25 3.1 3.4 4.5 ...
 $ FT_Odds_A          : num  2.8 3.4 2.15 3.5 2.6 3.75 2.5 3.5 3.6 6.5 ...
 $ XG_Home_Pre        : num  0 1.62 1.67 1.88 1.59 1.93 1.79 1.54 1.92 1.92 ...
 $ XG_Away_Pre        : num  0 1.56 1.54 1.61 1.53 1.39 1.43 1.46 1.48 1.27 ...
 $ last_GM_H          : int  0 2 1 0 1 0 1 0 1 1 ...
 $ last_GS_H          : int  3 1 2 1 0 0 3 0 2 2 ...
 $ last_PTS_H         : int  0 3 0 0 3 1 0 1 0 0 ...
 $ GM_mm_6_H          : num  1.5 1.83 1.67 1.17 1 ...
 $ GS_mm_6_H          : num  1.333 1.667 1 1 0.667 ...
 $ PTS_mm_6_H         : num  1.667 1.833 1.833 0.833 1.333 ...
 $ last_SH_H          : int  3 10 18 9 8 14 14 12 12 14 ...
 $ last_SHG_H         : int  2 5 4 4 2 11 4 3 4 8 ...
 $ SH_mm_6_H          : num  14.2 11.8 13.8 14.8 12.5 ...
 $ SHG_mm_6_H         : num  5.67 4.5 5 7.33 5.5 ...
 $ efc_mm_6_H         : num  160 125 154 171 111 ...
 $ last_GM_A          : int  4 2 3 2 4 1 0 1 2 1 ...
 $ last_GS_A          : int  0 1 1 1 1 2 0 2 1 5 ...
 $ last_PTS_A         : int  3 3 3 3 3 0 1 0 3 0 ...
 $ GM_mm_6_A          : num  1.83 1.67 2.33 1.67 1.17 ...
 $ GS_mm_6_A          : num  1.333 0.833 0.667 1.5 0.833 ...
 $ PTS_mm_6_A         : num  1.67 2 2 1.67 1.67 ...
 $ last_SH_A          : int  7 18 15 9 24 17 14 14 18 9 ...
 $ last_SHG_A         : int  6 8 9 6 8 6 5 6 7 4 ...
 $ SH_mm_6_A          : num  11.8 13.5 17.7 15.8 16.5 ...
 $ SHG_mm_6_A         : num  5 5.5 7 6.17 6.5 ...
 $ efc_mm_6_A         : num  124 153 274 182 204 ...
 $ adv                : Factor w/ 20 levels "América Mineiro",..: 5 15 17 9 10 2 1 16 13 8 ...
 $ time               : Factor w/ 20 levels "AméricaMineiro",..: 19 19 19 19 19 19 19 19 19 19 ...
 $ evento             : int  0 0 0 0 0 0 0 0 0 1 ...
 $ tempo              : int  1 8 1 2 4 2 3 1 1 2 ...
 $ censura            : int  1 1 1 1 1 1 1 1 1 0 ...
 $ local              : int  0 0 1 0 0 1 0 0 1 1 ...
 $ FT_Goals_H         : int  2 2 0 1 2 0 2 2 1 2 ...
 $ FT_Goals_A         : int  1 1 2 0 1 2 1 1 2 1 ...
 $ BotafogoCasa       : int  1 0 0 0 0 0 0 0 0 0 ...
 $ BotafogoFora       : int  0 0 0 0 0 0 0 0 0 0 ...
 $ Botafogo           : int  0 0 0 0 0 0 0 0 0 0 ...
 $ GrêmioCasa         : int  0 1 0 0 0 0 0 0 0 0 ...
 $ GrêmioFora         : int  0 0 0 0 0 0 0 0 0 0 ...
 $ Grêmio             : int  0 0 0 0 0 0 0 0 0 0 ...
 $ SãoPauloCasa       : int  0 0 1 0 0 1 0 0 1 1 ...
 $ SãoPauloFora       : int  1 1 0 1 1 0 1 1 0 0 ...
 $ SãoPaulo           : int  1 1 1 1 1 1 1 1 1 1 ...
 $ CruzeiroCasa       : int  0 0 0 1 0 0 0 0 0 0 ...
 $ CruzeiroFora       : int  0 0 0 0 0 0 0 0 0 0 ...
 $ Cruzeiro           : int  0 0 0 0 0 0 0 0 0 0 ...
 $ CuiabáCasa         : int  0 0 0 0 1 0 0 0 0 0 ...
 $ CuiabáFora         : int  0 0 0 0 0 0 0 0 0 0 ...
 $ Cuiabá             : int  0 0 0 0 0 0 0 0 0 0 ...
 $ AméricaMineiroCasa : int  0 0 0 0 0 0 1 0 0 0 ...
 $ AméricaMineiroFora : int  0 0 0 0 0 0 0 0 0 0 ...
 $ AméricaMineiro     : int  0 0 0 0 0 0 0 0 0 0 ...
 $ InternacionalCasa  : int  0 0 0 0 0 0 0 1 0 0 ...
 $ InternacionalFora  : int  0 0 0 0 0 0 0 0 0 0 ...
 $ Internacional      : int  0 0 0 0 0 0 0 0 0 0 ...
 $ BragantinoCasa     : int  0 0 0 0 0 0 0 0 0 0 ...
 $ BragantinoFora     : int  0 0 0 0 0 0 0 0 0 0 ...
 $ Bragantino         : int  0 0 0 0 0 0 0 0 0 0 ...
 $ SantosCasa         : int  0 0 0 0 0 0 0 0 0 0 ...
 $ SantosFora         : int  0 0 0 0 0 0 0 0 0 0 ...
 $ Santos             : int  0 0 0 0 0 0 0 0 0 0 ...
 $ FluminenseCasa     : int  0 0 0 0 0 0 0 0 0 0 ...
 $ FluminenseFora     : int  0 0 0 0 0 0 0 0 0 0 ...
 $ Fluminense         : int  0 0 0 0 0 0 0 0 0 0 ...
 $ FortalezaCasa      : int  0 0 0 0 0 0 0 0 0 0 ...
 $ FortalezaFora      : int  0 0 0 0 0 0 0 0 1 0 ...
 $ Fortaleza          : int  0 0 0 0 0 0 0 0 0 0 ...
 $ AtléticoMineiroCasa: int  0 0 0 0 0 0 0 0 0 0 ...
 $ AtléticoMineiroFora: int  0 0 0 0 0 1 0 0 0 0 ...
 $ AtléticoMineiro    : int  0 0 0 0 0 0 0 0 0 0 ...
 $ BahiaCasa          : int  0 0 0 0 0 0 0 0 0 0 ...
 $ BahiaFora          : int  0 0 0 0 0 0 0 0 0 0 ...
 $ Bahia              : int  0 0 0 0 0 0 0 0 0 0 ...
 $ GoiásCasa          : int  0 0 0 0 0 0 0 0 0 0 ...
 $ GoiásFora          : int  0 0 0 0 0 0 0 0 0 0 ...
 $ Goiás              : int  0 0 0 0 0 0 0 0 0 0 ...
 $ AtléticoPRCasa     : int  0 0 0 0 0 0 0 0 0 0 ...
 $ AtléticoPRFora     : int  0 0 0 0 0 0 0 0 0 0 ...
 $ AtléticoPR         : int  0 0 0 0 0 0 0 0 0 0 ...
 $ CorinthiansCasa    : int  0 0 0 0 0 0 0 0 0 0 ...
 $ CorinthiansFora    : int  0 0 0 0 0 0 0 0 0 0 ...
 $ Corinthians        : int  0 0 0 0 0 0 0 0 0 0 ...
 $ PalmeirasCasa      : int  0 0 0 0 0 0 0 0 0 0 ...
 $ PalmeirasFora      : int  0 0 1 0 0 0 0 0 0 0 ...
 $ Palmeiras          : int  0 0 0 0 0 0 0 0 0 0 ...
 $ FlamengoCasa       : int  0 0 0 0 0 0 0 0 0 0 ...
 $ FlamengoFora       : int  0 0 0 0 0 0 0 0 0 0 ...
 $ Flamengo           : int  0 0 0 0 0 0 0 0 0 0 ...
 $ VascodaGamaCasa    : int  0 0 0 0 0 0 0 0 0 0 ...
 $ VascodaGamaFora    : int  0 0 0 0 0 0 0 0 0 0 ...
 $ VascodaGama        : int  0 0 0 0 0 0 0 0 0 0 ...
 $ CoritibaCasa       : int  0 0 0 0 0 0 0 0 0 0 ...
 $ CoritibaFora       : int  0 0 0 0 0 0 0 0 0 1 ...
 $ Coritiba           : int  0 0 0 0 0 0 0 0 0 0 ...

Modelando

Ajustando o Modelo Inicial

i<-order(dados$tempo); dados<-dados[i,]; attach(dados)

typemodel = 'lognormal'

Ajustando o Modelo Inicial

i<-order(dados$tempo); dados<-dados[i,]; attach(dados)

typemodel = 'lognormal'

vars = colnames(dados)[!(colnames(dados) %in% c('FT_Goals_H','FT_Goals_A','Away','Date','Home','adv','evento',
                                            'time','tempo','censura','pred'))]

Ajustando o Modelo Inicial

i<-order(dados$tempo); dados<-dados[i,]; attach(dados)

typemodel = 'lognormal'

vars = colnames(dados)[!(colnames(dados) %in% c('FT_Goals_H','FT_Goals_A','Away','Date','Home','adv','evento',
                                            'time','tempo','censura','pred'))]

ajust1<-survreg(Surv(tempo, censura)~.,data = dados[,vars], dist=typemodel)
summary(ajust1)

Call:
survreg(formula = Surv(tempo, censura) ~ ., data = dados[, vars], 
    dist = typemodel)
                       Value Std. Error      z       p
(Intercept)         -0.78463    1.25360  -0.63 0.53138
Rodada               0.01941    0.00736   2.64 0.00837
FT_Odds_H            0.13524    0.15987   0.85 0.39759
FT_Odds_D           -0.22237    0.18328  -1.21 0.22503
FT_Odds_A            0.23697    0.07328   3.23 0.00122
XG_Home_Pre         -0.24198    0.20605  -1.17 0.24024
XG_Away_Pre          0.47987    0.25691   1.87 0.06178
last_GM_H            0.01480    0.07548   0.20 0.84451
last_GS_H           -0.07760    0.06613  -1.17 0.24057
last_PTS_H           0.08191    0.07928   1.03 0.30153
GM_mm_6_H           -0.11017    0.24607  -0.45 0.65437
GS_mm_6_H            0.07673    0.18411   0.42 0.67683
PTS_mm_6_H           0.16478    0.19939   0.83 0.40857
last_SH_H            0.00320    0.01275   0.25 0.80190
last_SHG_H           0.00768    0.02908   0.26 0.79167
SH_mm_6_H            0.02793    0.05950   0.47 0.63879
SHG_mm_6_H           0.01117    0.08763   0.13 0.89858
efc_mm_6_H          -0.00201    0.00318  -0.63 0.52735
last_GM_A            0.00229    0.07366   0.03 0.97520
last_GS_A           -0.10175    0.06422  -1.58 0.11308
last_PTS_A           0.05393    0.07136   0.76 0.44980
GM_mm_6_A            0.12818    0.28636   0.45 0.65444
GS_mm_6_A           -0.04132    0.16803  -0.25 0.80576
PTS_mm_6_A           0.30580    0.18957   1.61 0.10671
last_SH_A            0.00503    0.01113   0.45 0.65130
last_SHG_A           0.01895    0.02822   0.67 0.50191
SH_mm_6_A           -0.02641    0.05825  -0.45 0.65023
SHG_mm_6_A          -0.05628    0.09111  -0.62 0.53677
efc_mm_6_A           0.00112    0.00352   0.32 0.74979
local               -0.01897    0.10326  -0.18 0.85427
BotafogoCasa        -0.18955    0.40031  -0.47 0.63586
BotafogoFora        -0.88131    0.43851  -2.01 0.04446
Botafogo             1.43004    0.43721   3.27 0.00107
GrêmioCasa          -0.39328    0.37793  -1.04 0.29806
GrêmioFora          -1.15877    0.45586  -2.54 0.01102
Grêmio               1.59802    0.46891   3.41 0.00065
SãoPauloCasa        -0.08224    0.35680  -0.23 0.81770
SãoPauloFora        -0.11716    0.47863  -0.24 0.80663
SãoPaulo             0.41054    0.38993   1.05 0.29241
CruzeiroCasa        -0.21398    0.40091  -0.53 0.59352
CruzeiroFora        -0.58606    0.40662  -1.44 0.14950
Cruzeiro             1.07773    0.39409   2.73 0.00624
CuiabáCasa          -0.04398    0.39609  -0.11 0.91159
CuiabáFora          -0.82809    0.36782  -2.25 0.02436
Cuiabá               0.72807    0.39246   1.86 0.06357
AméricaMineiroCasa   0.28911    0.39472   0.73 0.46390
AméricaMineiroFora  -0.24316    0.49758  -0.49 0.62506
AméricaMineiro       0.20856    0.43692   0.48 0.63311
InternacionalCasa   -0.40348    0.40639  -0.99 0.32079
InternacionalFora   -0.34656    0.40845  -0.85 0.39617
Internacional        0.54756    0.39668   1.38 0.16747
BragantinoCasa      -0.31364    0.40026  -0.78 0.43328
BragantinoFora      -0.96835    0.42409  -2.28 0.02241
Bragantino           1.83559    0.43646   4.21 2.6e-05
SantosCasa          -0.32247    0.40771  -0.79 0.42898
SantosFora          -0.48470    0.42841  -1.13 0.25788
Santos               0.70196    0.42311   1.66 0.09711
FluminenseCasa      -0.28209    0.40048  -0.70 0.48119
FluminenseFora      -0.90442    0.50090  -1.81 0.07098
Fluminense           1.30852    0.48497   2.70 0.00697
FortalezaCasa       -0.31864    0.39717  -0.80 0.42240
FortalezaFora       -0.54387    0.37881  -1.44 0.15107
Fortaleza            0.84509    0.38513   2.19 0.02821
AtléticoMineiroCasa -1.11313    0.40851  -2.72 0.00643
AtléticoMineiroFora -0.51891    0.41911  -1.24 0.21566
AtléticoMineiro      1.44442    0.39641   3.64 0.00027
BahiaCasa            0.23147    0.38386   0.60 0.54651
BahiaFora            0.12771    0.42573   0.30 0.76420
Bahia                0.33789    0.40604   0.83 0.40532
GoiásCasa           -0.13810    0.42888  -0.32 0.74745
GoiásFora           -0.75925    0.38423  -1.98 0.04815
Goiás                0.67255    0.38881   1.73 0.08367
AtléticoPRCasa      -0.30705    0.36785  -0.83 0.40388
AtléticoPRFora      -1.02220    0.44123  -2.32 0.02052
AtléticoPR           1.49887    0.41518   3.61 0.00031
CorinthiansCasa     -0.58354    0.41352  -1.41 0.15819
CorinthiansFora     -0.41381    0.45991  -0.90 0.36825
Corinthians          1.07580    0.44064   2.44 0.01463
PalmeirasCasa       -0.79588    0.48585  -1.64 0.10140
PalmeirasFora       -0.91495    0.45342  -2.02 0.04360
Palmeiras            1.36286    0.44642   3.05 0.00227
FlamengoCasa        -0.39283    0.45478  -0.86 0.38770
FlamengoFora        -0.57848    0.39837  -1.45 0.14647
Flamengo             1.27966    0.41386   3.09 0.00199
VascodaGamaCasa     -0.67228    0.37251  -1.80 0.07112
VascodaGamaFora     -0.98710    0.40807  -2.42 0.01557
VascodaGama          1.23268    0.38758   3.18 0.00147
CoritibaCasa              NA    0.00000     NA      NA
CoritibaFora              NA    0.00000     NA      NA
Coritiba                  NA    0.00000     NA      NA
Log(scale)          -0.82629    0.05246 -15.75 < 2e-16

Scale= 0.438 

Log Normal distribution
Loglik(model)= -237.6   Loglik(intercept only)= -329.1
    Chisq= 182.89 on 89 degrees of freedom, p= 1.9e-08 
Number of Newton-Raphson Iterations: 6 
n= 196 

Seleção de Variáveis

selstep = step(ajust1,trace = FALSE)
vars = names(selstep$coefficients)[2:length(selstep$coefficients)]
ajust1<-survreg(Surv(tempo, censura)~.,data = dados[,vars], dist=typemodel)

Seleção de Variáveis com step e drop1

selstep = step(ajust1,trace = FALSE)
vars = names(selstep$coefficients)[2:length(selstep$coefficients)]
ajust1<-survreg(Surv(tempo, censura)~.,data = dados[,vars], dist=typemodel)

cortes = c(0.15);
for(corte in cortes){
  
  i = 1
  stop="Continue"
  
}

Seleção de Variáveis com step e drop1

selstep = step(ajust1,trace = FALSE)
vars = names(selstep$coefficients)[2:length(selstep$coefficients)]
ajust1<-survreg(Surv(tempo, censura)~.,data = dados[,vars], dist=typemodel)

cortes = c(0.15);
for(corte in cortes){
  
  i = 1
  stop="Continue"
  
  while(stop == 'Continue'){
    
  }
  
}

Seleção de Variáveis com step e drop1

selstep = step(ajust1,trace = FALSE)
vars = names(selstep$coefficients)[2:length(selstep$coefficients)]
ajust1<-survreg(Surv(tempo, censura)~.,data = dados[,vars], dist=typemodel)

cortes = c(0.15);
for(corte in cortes){
  
  i = 1
  stop="Continue"
  
  while(stop == 'Continue'){
    
    if(i == 1){
      sel2 = drop1(ajust1,test="Chisq")
      stop = ifelse(length(row.names(na.omit(data.frame(sel2)))[na.omit(sel2$`Pr(>Chi)`) >= corte]) == 0
                    ,'Pare','Continue')
      sel2 = row.names(na.omit(data.frame(sel2)))[na.omit(sel2$`Pr(>Chi)`) < corte]
      ajust2 = survreg(Surv(tempo, censura)~.,data = dados[,  sel2], dist=typemodel)
    } 
    
    
  }
  
}

Seleção de Variáveis com step e drop1

selstep = step(ajust1,trace = FALSE)
vars = names(selstep$coefficients)[2:length(selstep$coefficients)]
ajust1<-survreg(Surv(tempo, censura)~.,data = dados[,vars], dist=typemodel)

cortes = c(0.15);
for(corte in cortes){
  
  i = 1
  stop="Continue"
  
  while(stop == 'Continue'){
    
    if(i == 1){
      sel2 = drop1(ajust1,test="Chisq")
      stop = ifelse(length(row.names(na.omit(data.frame(sel2)))[na.omit(sel2$`Pr(>Chi)`) >= corte]) == 0
                    ,'Pare','Continue')
      sel2 = row.names(na.omit(data.frame(sel2)))[na.omit(sel2$`Pr(>Chi)`) < corte]
      ajust2 = survreg(Surv(tempo, censura)~.,data = dados[,  sel2], dist=typemodel)
    } else {
      sel2 = drop1(ajust2,test="Chisq")
      stop = ifelse(length(row.names(na.omit(data.frame(sel2)))[na.omit(sel2$`Pr(>Chi)`) >= corte]) == 0
                    ,'Pare','Continue')
      sel2 = row.names(na.omit(data.frame(sel2)))[na.omit(sel2$`Pr(>Chi)`) < corte]
      ajust2 = survreg(Surv(tempo, censura)~.,data = dados[,  sel2], dist=typemodel)
    }
    
    i = i+1
    
    
  }
  
}

Modelo Final

ajust1<-survreg(Surv(tempo, censura)~.,data = dados[,vars], dist=typemodel)
summary(ajust1)

Call:
survreg(formula = Surv(tempo, censura) ~ ., data = dados[, vars], 
    dist = typemodel)
                       Value Std. Error      z       p
(Intercept)         -0.37073    0.45066  -0.82 0.41071
Rodada               0.01642    0.00545   3.01 0.00261
FT_Odds_H            0.26697    0.10919   2.45 0.01448
FT_Odds_D           -0.44826    0.15043  -2.98 0.00288
FT_Odds_A            0.26466    0.06188   4.28 1.9e-05
XG_Away_Pre          0.15103    0.09576   1.58 0.11476
last_PTS_H           0.14450    0.03052   4.73 2.2e-06
last_GS_A           -0.12948    0.03672  -3.53 0.00042
PTS_mm_6_A           0.37631    0.07694   4.89 1.0e-06
last_SHG_A           0.03264    0.01470   2.22 0.02641
Cuiabá              -0.49211    0.15214  -3.23 0.00122
AméricaMineiroCasa   0.42548    0.22537   1.89 0.05904
AméricaMineiroFora   0.75780    0.33544   2.26 0.02388
AméricaMineiro      -0.94477    0.27314  -3.46 0.00054
InternacionalFora    0.54235    0.22641   2.40 0.01660
Internacional       -0.67204    0.22423  -3.00 0.00273
Bragantino           0.60846    0.24749   2.46 0.01395
AtléticoMineiroCasa -0.77694    0.18632  -4.17 3.0e-05
AtléticoMineiro      0.41516    0.19915   2.08 0.03709
BahiaCasa            0.36179    0.21934   1.65 0.09907
BahiaFora            0.85628    0.26439   3.24 0.00120
Bahia               -0.68711    0.26141  -2.63 0.00858
Goiás               -0.37832    0.17453  -2.17 0.03018
CorinthiansCasa     -0.51395    0.18955  -2.71 0.00670
PalmeirasCasa       -0.37949    0.20677  -1.84 0.06646
VascodaGamaCasa     -0.29788    0.15712  -1.90 0.05797
CoritibaFora         0.69536    0.23308   2.98 0.00285
Coritiba            -0.95644    0.19644  -4.87 1.1e-06
Log(scale)          -0.73983    0.05238 -14.12 < 2e-16

Scale= 0.477 

Log Normal distribution
Loglik(model)= -253.9   Loglik(intercept only)= -329.1
    Chisq= 150.35 on 27 degrees of freedom, p= 4.4e-19 
Number of Newton-Raphson Iterations: 5 
n= 196 

Análise de Resíduos

Resíduos Padronizados

xb = c(); for(i in 1:nrow(dados)){
  xb[i] = rowSums(as.numeric(ajust1$coefficients[2:length(ajust1$coefficients)])*dados[i,vars])+ajust1$coefficients[1] }
sigma<-ajust1$scale
res<-(log(tempo)-(xb))/sigma   
ekm<- survfit(Surv(exp(res),censura)~1) 
resid<-ekm$time
sln<-pnorm(-log(resid))

Resíduos Padronizados

xb = c(); for(i in 1:nrow(dados)){
  xb[i] = rowSums(as.numeric(ajust1$coefficients[2:length(ajust1$coefficients)])*dados[i,vars])+ajust1$coefficients[1] }
sigma<-ajust1$scale
res<-(log(tempo)-(xb))/sigma   
ekm<- survfit(Surv(exp(res),censura)~1) 
resid<-ekm$time
sln<-pnorm(-log(resid))

Resíduos de Cox-Snell

ei<- -log(1-pnorm(res))
ekm1<-survfit(Surv(ei,censura)~1)
t<-ekm1$time
st<-ekm1$surv
sexp<-exp(-t)

Resíduos de Cox-Snell

ei<- -log(1-pnorm(res))
ekm1<-survfit(Surv(ei,censura)~1)
t<-ekm1$time
st<-ekm1$surv
sexp<-exp(-t)

Previsões

Simulação a partir dos dados

  ajust1 = flexsurvreg(Surv(tempo, censura)~.,data = dados[,vars], dist=typemodel)
  
  pred = c(); for(i in 1:nrow(dados)){
    pred[i] = predict(ajust1,newdata = dados[i,],type = "survival",times = dados[i,]$tempo)[2]$.pred_survival
  }; dados$pred = round(pred,4)*100

Simulação a partir dos dados

  ajust1 = flexsurvreg(Surv(tempo, censura)~.,data = dados[,vars], dist=typemodel)
  
  pred = c(); for(i in 1:nrow(dados)){
    pred[i] = predict(ajust1,newdata = dados[i,],type = "survival",times = dados[i,]$tempo)[2]$.pred_survival
  }; dados$pred = round(pred,4)*100
  
  head(arrange(dados[,c('time','local','adv','tempo','pred','censura','FT_Goals_H','FT_Goals_A')],(pred)),10)
             time local             adv tempo pred censura FT_Goals_H
1        Botafogo     1        Flamengo    13 0.82       1          1
2       Palmeiras     0           Bahia    11 2.60       1          1
3     Corinthians     0       Fortaleza     9 3.43       1          2
4        Cruzeiro     0     Corinthians     1 4.27       1          2
5        SãoPaulo     0          Grêmio     8 4.88       1          2
6     Corinthians     1        Botafogo     3 5.40       0          1
7   Internacional     0      Fluminense     7 5.58       1          2
8       Fortaleza     0 América Mineiro     7 5.83       1          2
9           Goiás     0       Palmeiras     9 6.16       1          1
10 AméricaMineiro     1        Cruzeiro     2 6.36       1          0
   FT_Goals_A
1           2
2           0
3           1
4           1
5           1
6           0
7           0
8           1
9           0
10          4

Simulação personalizada

  newdata = data.frame(t(c(25,2,2,2,1,1,1,1.25,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)))
  colnames(newdata) = c(names(ajust1$datameans))
  newdata
  Rodada FT_Odds_H FT_Odds_D FT_Odds_A XG_Away_Pre last_PTS_H last_GS_A
1     25         2         2         2           1          1         1
  PTS_mm_6_A last_SHG_A Cuiabá AméricaMineiroCasa AméricaMineiroFora
1       1.25          0      0                  0                  0
  AméricaMineiro InternacionalFora Internacional Bragantino AtléticoMineiroCasa
1              0                 0             0          0                   0
  AtléticoMineiro BahiaCasa BahiaFora Bahia Goiás CorinthiansCasa PalmeirasCasa
1               0         0         0     0     0               0             0
  VascodaGamaCasa CoritibaFora Coritiba
1               0            0        0
round(as.data.frame(predict(ajust1, newdata = newdata, type = 'survival',times = c(1:38))[[1]])[,2],4)*100
 [1] 96.13 62.32 29.60 12.74  5.41  2.34  1.04  0.48  0.23  0.11  0.06  0.03
[13]  0.02  0.01  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00
[25]  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00
[37]  0.00  0.00

Considerações Finais

Conclusões

A partir da análise de resísudos e dos p-values para os coeficientes do modelo, podemos concluir que obtivemos um bom ajuste, utilizando uma abordagem de seleção de variáveis que considera as técnicas step e drop, e um pouco da opinião do pesquisador sobre as variáveis.

Podemos perceber que todas variáveis que se referem as chances (Odds) da partida das casas de apostas foram selecionadas, mostrando que a estimativa dessas probabilidades podem ajudar a explicar a invencibilidade dos times. Há também uma outra variável que se refere a expectativa de gols antes da partida, mas só a do time visitante foi selecionada.

Das variáveis relacionadas a performance nos últimos jogos, temos o número de pontos que o time da casa conquistou, a média móvel de pontos dos últimos 6 jogos do time visitante, a quantidade de gols sofrida e o número de chutes em gol da equipe visitante. Logo, a tendência que o modelo capta é majoritarimante sobre variáveis que se referem ao time visitante, assim sendo, temos que elas possuem um maior impacto para as observações que perderam a invencibilidade.

Temos também variáveis dummies relacionadas aos times, destacando alguns deles podemos observar o América Mineiro e o Bahia que aparecem em todas subdivisões das dummies, mostrando um impacto importante do time na permanência ou não da invencibilidade das observações. No total foram 18 variáveis dessa classe de 60 possíveis, mostrando um bom filtro da influencia que as equipes tem em diferentes cenários.

Interpretando os coeficientes podemos ter uma ideia prática de quanto cada variável selecionada interfere no sucesso dos times. Aplicando o exponencial dos coeficientes podemos interpretar de forma mais intuitiva, como a variação percentual na probabilidade de sobrevivência, para cada valor unitário.

        (Intercept)              Rodada           FT_Odds_H           FT_Odds_D 
          0.6902275           1.0165522           1.3060052           0.6387412 
          FT_Odds_A         XG_Away_Pre          last_PTS_H           last_GS_A 
          1.3029867           1.1630345           1.1554654           0.8785556 
         PTS_mm_6_A          last_SHG_A              Cuiabá  AméricaMineiroCasa 
          1.4569005           1.0331765           0.6113355           1.5303244 
 AméricaMineiroFora      AméricaMineiro   InternacionalFora       Internacional 
          2.1335785           0.3887706           1.7200391           0.5106660 
         Bragantino AtléticoMineiroCasa     AtléticoMineiro           BahiaCasa 
          1.8376083           0.4598117           1.5146203           1.4358910 
          BahiaFora               Bahia               Goiás     CorinthiansCasa 
          2.3543754           0.5030299           0.6850121           0.5981259 
      PalmeirasCasa     VascodaGamaCasa        CoritibaFora            Coritiba 
          0.6842071           0.7423929           2.0044267           0.3842592 

Pelo intercepto podemos ver que as observações analisadas tendem a começar com -31% na variação percentual que impacta a chance sobrevivência. Quando olhamos ao nível dos times observamos que quando é o América-MG a equipe que está sendo analisada como perdedora da invencibilidade temos uma variação percentual reduzindo 61% na chance de sobrevivência.

Por outra ótica, observamos as demais variáveis relacionadas a esse mesmo time, onde quando as demais observações enfrentam o América na suas dependências, as equipes tem uma probabilidade adicional de sobrevida de 53%, e quando os times encaram o América como mandante, eles possuem uma probabilidade sobrevivência a mais de 113%.

Analogamente, poderíamos extender essas interpretações para as demais variáveis, buscando entender o impacto de cada uma nas probabilidade de sobrevida dos times.

Na prática esse tipo de modelagem poderia ser aplicada de diversos modos, uma delas bem simples seria de estudar as tendências dos times do campeonato que os tornam mais propensos a não perder jogos com facilidade, e analisar os fatores que mais influenciam.

Por outro lado, poderiamos realizar um acompanhamento rodada a rodada desse modelo de sobrevivência estimando a chance dos times não perderem os jogos, de modo que comparemos a probabilidade de sobrevivência com estimativas de casas de apostas visando alguma oportunidade.

Possíveis Melhorias

  • Criação de Mais Variáveis
  • Acrescentar temporadas anteriores de 2023
  • Pegar dados que não houveram falha e fazer previsões
  • Implementar Cross Validation

Referências

  • Base de Dados

  • “Analysis of Survival Data with Cross-Sectional and Longitudinal Data” de John P. Klein e Melvin L. Moeschberger

  • “Survival Analysis: Models and Applications” de Xian Liu