1 Introdução

Este trabalho apresenta uma análise de sobrevivência utilizando dados da coorte SamiTrop, um projeto desenvolvido em parceria com pesquisadores de Minas Gerais e São Paulo, que envolve o acompanhamento e tratamento de mais de dois mil pacientes chagásicos em municípios do norte de MG.

O estudo compreende três ondas de avaliação:

  • Primeira onda: 2013-2014 (linha de base)
  • Segunda onda: 2015-2017 (sobreviventes da primeira onda)
  • Terceira onda: 2019-2021

O objetivo é estudar como covariáveis medidas na linha de base estão relacionadas com o desfecho óbito ao longo de todo o período de acompanhamento.

1.1 Carregamento de Pacotes

# Instalação de pacotes (se necessário)
#pacotes <- c("survival", "survminer", "ggplot2", "dplyr", "tidyr", "knitr", 
#             "kableExtra", "flexsurv", "car", "MASS", "pROC", "gridExtra",
#             "corrplot", "ggcorrplot", "mice", "naniar")

#pacotes_instalar <- pacotes[!(pacotes %in% installed.packages()[,"Package"])]
#if(length(pacotes_instalar)) install.packages(pacotes_instalar, repos = "https://cran.r-project.org")

# Carregamento
library(survival)
library(survminer)
library(ggplot2)
library(dplyr)
library(tidyr)
library(knitr)
library(kableExtra)
library(flexsurv)
library(car)
library(MASS)
library(gridExtra)

# Tema para gráficos
theme_set(theme_minimal(base_size = 12))

1.2 Carregamento dos Dados

# Carregar os dados
load("BancoChagas.RData")

# Visualizar estrutura
str(dados)
## tibble [1,000 × 12] (S3: tbl_df/tbl/data.frame)
##  $ Tempo           : num [1:1000] 8.16 5.9 7.18 9.08 5.71 ...
##  $ Obito           : num [1:1000] 1 0 0 0 0 0 0 0 0 0 ...
##   ..- attr(*, "format.spss")= chr "F12.0"
##   ..- attr(*, "display_width")= int 9
##  $ Sexo            : Factor w/ 2 levels "Masculino","Feminino": 1 2 2 2 2 2 2 2 2 1 ...
##  $ Idade           : num [1:1000] 69 NA 62 56 72 49 76 42 67 NA ...
##   ..- attr(*, "format.spss")= chr "F12.0"
##   ..- attr(*, "display_width")= int 16
##  $ Classe_Funcional: Factor w/ 4 levels "1","2","3","4": 1 3 3 3 3 2 1 3 3 1 ...
##  $ IAM             : Factor w/ 2 levels "Nao","Sim": 1 1 1 1 1 1 1 1 1 1 ...
##  $ Rochagan        : Factor w/ 2 levels "Nao","Sim": 1 2 1 2 1 1 1 2 1 1 ...
##  $ Beta_Bloqueador : Factor w/ 2 levels "Nao","Sim": NA 1 1 2 1 1 2 1 1 1 ...
##  $ Bnp             : num [1:1000] 2979 NA NA NA 125 ...
##   ..- attr(*, "format.spss")= chr "F12.0"
##   ..- attr(*, "display_width")= int 12
##  $ Anticorpos      : num [1:1000] 11.49 11.41 9.94 10.91 3.19 ...
##   ..- attr(*, "label")= chr "FL_Anticorpos"
##   ..- attr(*, "format.spss")= chr "F12.2"
##   ..- attr(*, "display_width")= int 24
##  $ ECG_Result      : Factor w/ 2 levels "Menores","Maiores": 2 1 2 2 2 1 2 2 2 1 ...
##  $ FE              : num [1:1000] 41 NA 64 74 64 64 70 66 NA NA ...
##   ..- attr(*, "format.spss")= chr "F12.0"
##   ..- attr(*, "display_width")= int 12

2 Análise Exploratória

2.1 Descrição das Variáveis

As variáveis disponíveis no banco de dados são:

Variável Descrição
Tempo Tempo de acompanhamento (em anos)
Obito Indicadora de óbito (0 = censurado, 1 = óbito)
Sexo Sexo do paciente (Masculino/Feminino)
Idade Idade do paciente em anos
Classe_Funcional Classificação NYHA de insuficiência cardíaca (1-4)
IAM Histórico de infarto (Não/Sim)
Rochagan Uso do medicamento Rochagan (Não/Sim)
Beta_Bloqueador Uso de betabloqueador (Não/Sim)
Bnp Dosagem de BNP (pg/mL)
Anticorpos Níveis de anticorpos
ECG_Result Alterações do ECG (Menores/Maiores)
FE Fração de ejeção (%)

2.2 Análise de Dados Faltantes

# Contagem de missings por variável
missing_summary <- dados %>%
  summarise(across(everything(), ~sum(is.na(.)))) %>%
  pivot_longer(everything(), names_to = "Variavel", values_to = "N_Missing") %>%
  mutate(Percentual = round(100 * N_Missing / nrow(dados), 2)) %>%
  arrange(desc(N_Missing))

kable(missing_summary, 
      caption = "Dados Faltantes por Variável",
      col.names = c("Variável", "N Missing", "Percentual (%)")) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Dados Faltantes por Variável
Variável N Missing Percentual (%)
Bnp 595 59.5
FE 302 30.2
Idade 199 19.9
ECG_Result 22 2.2
Beta_Bloqueador 13 1.3
Tempo 11 1.1
Anticorpos 10 1.0
Classe_Funcional 7 0.7
IAM 1 0.1
Obito 0 0.0
Sexo 0 0.0
Rochagan 0 0.0
# Gráfico de missings
missing_plot <- missing_summary %>%
  filter(N_Missing > 0) %>%
  ggplot(aes(x = reorder(Variavel, N_Missing), y = Percentual)) +
  geom_bar(stat = "identity", fill = "steelblue") +
  geom_text(aes(label = paste0(N_Missing, " (", Percentual, "%)")), 
            hjust = -0.1, size = 3) +
  coord_flip() +
  labs(x = "", y = "Percentual de Dados Faltantes (%)",
       title = "Percentual de Dados Faltantes por Variável") +
  ylim(0, max(missing_summary$Percentual) * 1.2)

print(missing_plot)
Padrão de dados faltantes

Padrão de dados faltantes

Observação sobre dados faltantes: As variáveis Bnp (59.5%), FE (30.2%) e Idade (19.9%) apresentam proporções substanciais de dados faltantes. Isso deve ser considerado na modelagem, pois a inclusão dessas variáveis pode reduzir significativamente o tamanho amostral efetivo.

2.3 Medidas Resumo

2.3.1 Variáveis Numéricas

# Resumo das variáveis numéricas
resumo_num <- dados %>%
  dplyr::select(Tempo, Idade, Bnp, Anticorpos, FE) %>%
  pivot_longer(everything(), names_to = "Variavel", values_to = "Valor") %>%
  group_by(Variavel) %>%
  summarise(
    N = sum(!is.na(Valor)),
    Media = mean(Valor, na.rm = TRUE),
    DP = sd(Valor, na.rm = TRUE),
    Mediana = median(Valor, na.rm = TRUE),
    Q1 = quantile(Valor, 0.25, na.rm = TRUE),
    Q3 = quantile(Valor, 0.75, na.rm = TRUE),
    Min = min(Valor, na.rm = TRUE),
    Max = max(Valor, na.rm = TRUE)
  ) %>%
  mutate(across(where(is.numeric), ~round(., 2)))

kable(resumo_num, 
      caption = "Medidas Resumo - Variáveis Numéricas") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Medidas Resumo - Variáveis Numéricas
Variavel N Media DP Mediana Q1 Q3 Min Max
Anticorpos 990 11.34 2.65 11.78 10.22 13.02 1.01 17.78
Bnp 405 545.30 1374.43 151.00 65.00 379.00 3.00 15279.00
FE 698 59.22 10.72 63.00 58.00 65.00 10.00 80.00
Idade 801 60.51 12.37 61.00 51.00 69.00 23.00 95.00
Tempo 989 6.73 2.02 7.20 5.92 8.19 0.07 9.34

2.3.2 Variáveis Categóricas

# Função para criar tabela de frequência
freq_table <- function(var, var_name) {
  dados %>%
    count({{var}}) %>%
    mutate(
      Percentual = round(100 * n / sum(n), 1),
      Variavel = var_name
    ) %>%
    rename(Categoria = 1, N = n)
}

# Tabelas de frequência
freq_sexo <- freq_table(Sexo, "Sexo")
freq_classe <- freq_table(Classe_Funcional, "Classe_Funcional")
freq_iam <- freq_table(IAM, "IAM")
freq_rochagan <- freq_table(Rochagan, "Rochagan")
freq_beta <- freq_table(Beta_Bloqueador, "Beta_Bloqueador")
freq_ecg <- freq_table(ECG_Result, "ECG_Result")

# Combinar todas
freq_all <- bind_rows(freq_sexo, freq_classe, freq_iam, 
                       freq_rochagan, freq_beta, freq_ecg)

kable(freq_all %>% dplyr::select(Variavel, Categoria, N, Percentual),
      caption = "Distribuição das Variáveis Categóricas",
      col.names = c("Variável", "Categoria", "N", "Percentual (%)")) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
  collapse_rows(columns = 1, valign = "middle")
Distribuição das Variáveis Categóricas
Variável Categoria N Percentual (%)
Sexo Masculino 323 32.3
Feminino 677 67.7
Classe_Funcional 1 543 54.3
2 72 7.2
3 371 37.1
4 7 0.7
NA 7 0.7
IAM Nao 954 95.4
Sim 45 4.5
NA 1 0.1
Rochagan Nao 739 73.9
Sim 261 26.1
Beta_Bloqueador Nao 805 80.5
Sim 182 18.2
NA 13 1.3
ECG_Result Menores 402 40.2
Maiores 576 57.6
NA 22 2.2

2.3.3 Desfecho: Óbito

# Resumo do desfecho
desfecho_summary <- dados %>%
  count(Obito) %>%
  mutate(
    Status = ifelse(Obito == 1, "Óbito", "Censurado"),
    Percentual = round(100 * n / sum(n), 1)
  )

kable(desfecho_summary %>% dplyr::select(Status, n, Percentual),
      caption = "Distribuição do Desfecho",
      col.names = c("Status", "N", "Percentual (%)")) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Distribuição do Desfecho
Status N Percentual (%)
Censurado 788 78.8
Óbito 212 21.2
cat("Taxa de eventos (óbitos):", sum(dados$Obito), "de", nrow(dados), 
    "pacientes (", round(100*mean(dados$Obito), 1), "%)\n")
## Taxa de eventos (óbitos): 212 de 1000 pacientes ( 21.2 %)
cat("Tempo médio de acompanhamento:", round(mean(dados$Tempo, na.rm = TRUE), 2), "anos\n")
## Tempo médio de acompanhamento: 6.73 anos
cat("Tempo mediano de acompanhamento:", round(median(dados$Tempo, na.rm = TRUE), 2), "anos\n")
## Tempo mediano de acompanhamento: 7.2 anos

2.4 Curvas de Kaplan-Meier

2.4.1 Curva de Sobrevivência Global

# Criar objeto de sobrevivência
surv_obj <- Surv(dados$Tempo, dados$Obito)

# Ajuste KM global
km_global <- survfit(surv_obj ~ 1)

# Gráfico
ggsurvplot(km_global, 
           data = dados,
           conf.int = TRUE,
           risk.table = TRUE,
           risk.table.col = "strata",
           ggtheme = theme_minimal(),
           palette = "steelblue",
           title = "Curva de Sobrevivência Global - Coorte SamiTrop",
           xlab = "Tempo (anos)",
           ylab = "Probabilidade de Sobrevivência",
           legend = "none",
           risk.table.height = 0.25)
Curva de sobrevivência global

Curva de sobrevivência global

# Resumo do ajuste KM
summary_km <- summary(km_global, times = c(1, 2, 3, 5, 7, 9))
km_table <- data.frame(
  Tempo = summary_km$time,
  N_Risco = summary_km$n.risk,
  N_Eventos = summary_km$n.event,
  Sobrevivencia = round(summary_km$surv, 3),
  IC_Inferior = round(summary_km$lower, 3),
  IC_Superior = round(summary_km$upper, 3)
)

kable(km_table,
      caption = "Estimativas de Sobrevivência em Tempos Selecionados",
      col.names = c("Tempo (anos)", "N em Risco", "N Eventos", 
                    "S(t)", "IC 95% Inf", "IC 95% Sup")) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Estimativas de Sobrevivência em Tempos Selecionados
Tempo (anos) N em Risco N Eventos S(t) IC 95% Inf IC 95% Sup
1 966 23 0.977 0.967 0.986
2 933 33 0.943 0.929 0.958
3 906 24 0.919 0.902 0.936
5 860 46 0.872 0.852 0.893
7 549 42 0.822 0.798 0.847
9 49 31 0.700 0.652 0.752

2.4.2 Curvas por Sexo

km_sexo <- survfit(surv_obj ~ Sexo, data = dados)

ggsurvplot(km_sexo, 
           data = dados,
           conf.int = TRUE,
           pval = TRUE,
           pval.method = TRUE,
           risk.table = TRUE,
           ggtheme = theme_minimal(),
           palette = c("#E41A1C", "#377EB8"),
           title = "Curva de Sobrevivência por Sexo",
           xlab = "Tempo (anos)",
           ylab = "Probabilidade de Sobrevivência",
           legend.title = "Sexo",
           legend.labs = c("Masculino", "Feminino"),
           risk.table.height = 0.25)
Curvas de sobrevivência por sexo

Curvas de sobrevivência por sexo

2.4.3 Curvas por Classe Funcional (NYHA)

# Remover NAs
dados_classe <- dados %>% filter(!is.na(Classe_Funcional))
surv_obj_classe <- Surv(dados_classe$Tempo, dados_classe$Obito)

km_classe <- survfit(surv_obj_classe ~ Classe_Funcional, data = dados_classe)

ggsurvplot(km_classe, 
           data = dados_classe,
           conf.int = TRUE,
           pval = TRUE,
           pval.method = TRUE,
           risk.table = TRUE,
           ggtheme = theme_minimal(),
           palette = "Dark2",
           title = "Curva de Sobrevivência por Classe Funcional (NYHA)",
           xlab = "Tempo (anos)",
           ylab = "Probabilidade de Sobrevivência",
           legend.title = "Classe NYHA",
           risk.table.height = 0.30)
Curvas de sobrevivência por Classe Funcional NYHA

Curvas de sobrevivência por Classe Funcional NYHA

2.4.4 Curvas por IAM (Infarto)

dados_iam <- dados %>% filter(!is.na(IAM))
surv_obj_iam <- Surv(dados_iam$Tempo, dados_iam$Obito)

km_iam <- survfit(surv_obj_iam ~ IAM, data = dados_iam)

ggsurvplot(km_iam, 
           data = dados_iam,
           conf.int = TRUE,
           pval = TRUE,
           pval.method = TRUE,
           risk.table = TRUE,
           ggtheme = theme_minimal(),
           palette = c("#4DAF4A", "#984EA3"),
           title = "Curva de Sobrevivência por Histórico de IAM",
           xlab = "Tempo (anos)",
           ylab = "Probabilidade de Sobrevivência",
           legend.title = "IAM",
           legend.labs = c("Não", "Sim"),
           risk.table.height = 0.25)
Curvas de sobrevivência por histórico de IAM

Curvas de sobrevivência por histórico de IAM

2.4.5 Curvas por Uso de Rochagan

km_rochagan <- survfit(surv_obj ~ Rochagan, data = dados)

ggsurvplot(km_rochagan, 
           data = dados,
           conf.int = TRUE,
           pval = TRUE,
           pval.method = TRUE,
           risk.table = TRUE,
           ggtheme = theme_minimal(),
           palette = c("#FF7F00", "#A65628"),
           title = "Curva de Sobrevivência por Uso de Rochagan",
           xlab = "Tempo (anos)",
           ylab = "Probabilidade de Sobrevivência",
           legend.title = "Rochagan",
           legend.labs = c("Não", "Sim"),
           risk.table.height = 0.25)
Curvas de sobrevivência por uso de Rochagan

Curvas de sobrevivência por uso de Rochagan

2.4.6 Curvas por Uso de Beta-Bloqueador

dados_beta <- dados %>% filter(!is.na(Beta_Bloqueador))
surv_obj_beta <- Surv(dados_beta$Tempo, dados_beta$Obito)

km_beta <- survfit(surv_obj_beta ~ Beta_Bloqueador, data = dados_beta)

ggsurvplot(km_beta, 
           data = dados_beta,
           conf.int = TRUE,
           pval = TRUE,
           pval.method = TRUE,
           risk.table = TRUE,
           ggtheme = theme_minimal(),
           palette = c("#F781BF", "#999999"),
           title = "Curva de Sobrevivência por Uso de Beta-Bloqueador",
           xlab = "Tempo (anos)",
           ylab = "Probabilidade de Sobrevivência",
           legend.title = "Beta-Bloqueador",
           legend.labs = c("Não", "Sim"),
           risk.table.height = 0.25)
Curvas de sobrevivência por uso de Beta-Bloqueador

Curvas de sobrevivência por uso de Beta-Bloqueador

2.4.7 Curvas por Resultado do ECG

dados_ecg <- dados %>% filter(!is.na(ECG_Result))
surv_obj_ecg <- Surv(dados_ecg$Tempo, dados_ecg$Obito)

km_ecg <- survfit(surv_obj_ecg ~ ECG_Result, data = dados_ecg)

ggsurvplot(km_ecg, 
           data = dados_ecg,
           conf.int = TRUE,
           pval = TRUE,
           pval.method = TRUE,
           risk.table = TRUE,
           ggtheme = theme_minimal(),
           palette = c("#66C2A5", "#FC8D62"),
           title = "Curva de Sobrevivência por Resultado do ECG",
           xlab = "Tempo (anos)",
           ylab = "Probabilidade de Sobrevivência",
           legend.title = "ECG",
           legend.labs = c("Alterações Menores", "Alterações Maiores"),
           risk.table.height = 0.25)
Curvas de sobrevivência por resultado do ECG

Curvas de sobrevivência por resultado do ECG

2.4.8 Curvas por Idade (categorizada)

# Categorizar idade
dados_idade <- dados %>% 
  filter(!is.na(Idade)) %>%
  mutate(Faixa_Etaria = cut(Idade, 
                            breaks = c(0, 50, 65, Inf),
                            labels = c("< 50 anos", "50-65 anos", "> 65 anos")))

surv_obj_idade <- Surv(dados_idade$Tempo, dados_idade$Obito)
km_idade <- survfit(surv_obj_idade ~ Faixa_Etaria, data = dados_idade)

ggsurvplot(km_idade, 
           data = dados_idade,
           conf.int = TRUE,
           pval = TRUE,
           pval.method = TRUE,
           risk.table = TRUE,
           ggtheme = theme_minimal(),
           palette = "Set1",
           title = "Curva de Sobrevivência por Faixa Etária",
           xlab = "Tempo (anos)",
           ylab = "Probabilidade de Sobrevivência",
           legend.title = "Faixa Etária",
           risk.table.height = 0.30)
Curvas de sobrevivência por faixa etária

Curvas de sobrevivência por faixa etária

2.4.9 Curvas por BNP (categorizado)

# Categorizar BNP pela mediana
dados_bnp <- dados %>% 
  filter(!is.na(Bnp)) %>%
  mutate(BNP_Cat = ifelse(Bnp <= median(Bnp), "BNP Baixo", "BNP Alto"),
         BNP_Cat = factor(BNP_Cat, levels = c("BNP Baixo", "BNP Alto")))

surv_obj_bnp <- Surv(dados_bnp$Tempo, dados_bnp$Obito)
km_bnp <- survfit(surv_obj_bnp ~ BNP_Cat, data = dados_bnp)

ggsurvplot(km_bnp, 
           data = dados_bnp,
           conf.int = TRUE,
           pval = TRUE,
           pval.method = TRUE,
           risk.table = TRUE,
           ggtheme = theme_minimal(),
           palette = c("#1B9E77", "#D95F02"),
           title = "Curva de Sobrevivência por Nível de BNP",
           xlab = "Tempo (anos)",
           ylab = "Probabilidade de Sobrevivência",
           legend.title = "BNP",
           risk.table.height = 0.25)
Curvas de sobrevivência por nível de BNP

Curvas de sobrevivência por nível de BNP

2.4.10 Curvas por Fração de Ejeção (categorizada)

# Categorizar FE (< 40% = reduzida, 40-50% = levemente reduzida, > 50% = preservada)
dados_fe <- dados %>% 
  filter(!is.na(FE)) %>%
  mutate(FE_Cat = case_when(
    FE < 40 ~ "FE Reduzida (<40%)",
    FE >= 40 & FE <= 50 ~ "FE Levemente Reduzida (40-50%)",
    FE > 50 ~ "FE Preservada (>50%)"
  ),
  FE_Cat = factor(FE_Cat, levels = c("FE Preservada (>50%)", 
                                      "FE Levemente Reduzida (40-50%)", 
                                      "FE Reduzida (<40%)")))

surv_obj_fe <- Surv(dados_fe$Tempo, dados_fe$Obito)
km_fe <- survfit(surv_obj_fe ~ FE_Cat, data = dados_fe)

ggsurvplot(km_fe, 
           data = dados_fe,
           conf.int = TRUE,
           pval = TRUE,
           pval.method = TRUE,
           risk.table = TRUE,
           ggtheme = theme_minimal(),
           palette = c("#66C2A5", "#FC8D62", "#8DA0CB"),
           title = "Curva de Sobrevivência por Fração de Ejeção",
           xlab = "Tempo (anos)",
           ylab = "Probabilidade de Sobrevivência",
           legend.title = "Fração de Ejeção",
           risk.table.height = 0.30)
Curvas de sobrevivência por Fração de Ejeção

Curvas de sobrevivência por Fração de Ejeção

2.5 Resumo da Análise Exploratória

# Tabela resumo dos testes log-rank
testes_logrank <- data.frame(
  Variavel = c("Sexo", "Classe Funcional", "IAM", "Rochagan", 
               "Beta-Bloqueador", "ECG", "Faixa Etária", "BNP", "FE"),
  p_valor = c(
    survdiff(surv_obj ~ Sexo, data = dados)$pvalue,
    survdiff(surv_obj_classe ~ Classe_Funcional, data = dados_classe)$pvalue,
    survdiff(surv_obj_iam ~ IAM, data = dados_iam)$pvalue,
    survdiff(surv_obj ~ Rochagan, data = dados)$pvalue,
    survdiff(surv_obj_beta ~ Beta_Bloqueador, data = dados_beta)$pvalue,
    survdiff(surv_obj_ecg ~ ECG_Result, data = dados_ecg)$pvalue,
    survdiff(surv_obj_idade ~ Faixa_Etaria, data = dados_idade)$pvalue,
    survdiff(surv_obj_bnp ~ BNP_Cat, data = dados_bnp)$pvalue,
    survdiff(surv_obj_fe ~ FE_Cat, data = dados_fe)$pvalue
  )
) %>%
  mutate(
    p_valor_fmt = ifelse(p_valor < 0.001, "< 0.001", sprintf("%.4f", p_valor)),
    Significativo = ifelse(p_valor < 0.05, "Sim", "Não")
  )

kable(testes_logrank %>% dplyr::select(Variavel, p_valor_fmt, Significativo),
      caption = "Resumo dos Testes Log-Rank",
      col.names = c("Variável", "p-valor", "Significativo (α = 0.05)")) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Resumo dos Testes Log-Rank
Variável p-valor Significativo (α = 0.05)
Sexo < 0.001 Sim
Classe Funcional < 0.001 Sim
IAM < 0.001 Sim
Rochagan < 0.001 Sim
Beta-Bloqueador < 0.001 Sim
ECG < 0.001 Sim
Faixa Etária < 0.001 Sim
BNP < 0.001 Sim
FE < 0.001 Sim

3 Modelos Paramétricos

3.1 Preparação dos Dados

Devido à alta proporção de dados faltantes em algumas variáveis (especialmente Bnp e FE), optamos por construir dois conjuntos de modelos:

  1. Modelo Completo: Utilizando apenas casos completos para todas as variáveis
  2. Modelo Reduzido: Excluindo variáveis com muitos missings (Bnp e FE)
# Dados para modelo reduzido (excluindo Bnp e FE)
dados_red <- dados %>%
  dplyr::select(-Bnp, -FE) %>%
  filter(complete.cases(.))

cat("Modelo Reduzido - N =", nrow(dados_red), "observações\n")
## Modelo Reduzido - N = 755 observações
cat("Eventos:", sum(dados_red$Obito), "\n\n")
## Eventos: 112
# Dados para modelo completo
dados_comp <- dados %>%
  filter(complete.cases(.))

cat("Modelo Completo - N =", nrow(dados_comp), "observações\n")
## Modelo Completo - N = 371 observações
cat("Eventos:", sum(dados_comp$Obito), "\n")
## Eventos: 50

3.2 Seleção da Distribuição

Vamos comparar diferentes distribuições paramétricas para escolher a mais adequada.

# Criar objeto de sobrevivência para dados completos
surv_red <- Surv(dados_red$Tempo, dados_red$Obito)

# Ajustar diferentes distribuições
dist_exp <- flexsurvreg(surv_red ~ 1, data = dados_red, dist = "exp")
dist_weibull <- flexsurvreg(surv_red ~ 1, data = dados_red, dist = "weibull")
dist_lnorm <- flexsurvreg(surv_red ~ 1, data = dados_red, dist = "lnorm")
dist_gamma <- flexsurvreg(surv_red ~ 1, data = dados_red, dist = "gamma")
dist_llogis <- flexsurvreg(surv_red ~ 1, data = dados_red, dist = "llogis")
dist_gompertz <- flexsurvreg(surv_red ~ 1, data = dados_red, dist = "gompertz")
dist_gengamma <- flexsurvreg(surv_red ~ 1, data = dados_red, dist = "gengamma")

# Comparar AIC
comparacao_dist <- data.frame(
  Distribuicao = c("Exponencial", "Weibull", "Log-Normal", "Gamma", 
                   "Log-Logística", "Gompertz", "Gama Generalizada"),
  AIC = c(AIC(dist_exp), AIC(dist_weibull), AIC(dist_lnorm), AIC(dist_gamma),
          AIC(dist_llogis), AIC(dist_gompertz), AIC(dist_gengamma)),
  BIC = c(BIC(dist_exp), BIC(dist_weibull), BIC(dist_lnorm), BIC(dist_gamma),
          BIC(dist_llogis), BIC(dist_gompertz), BIC(dist_gengamma))
) %>%
  arrange(AIC) %>%
  mutate(
    Delta_AIC = AIC - min(AIC),
    across(where(is.numeric), ~round(., 2))
  )

kable(comparacao_dist,
      caption = "Comparação de Distribuições Paramétricas",
      col.names = c("Distribuição", "AIC", "BIC", "ΔAIC")) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Comparação de Distribuições Paramétricas
Distribuição AIC BIC ΔAIC
Log-Normal 1020.52 1029.77 0.00
Gamma 1021.40 1030.65 0.88
Weibull 1022.13 1031.39 1.62
Log-Logística 1022.39 1031.64 1.87
Gama Generalizada 1023.94 1037.82 3.42
Gompertz 1031.10 1040.36 10.59
Exponencial 1090.56 1095.19 70.04
# Plot comparativo
plot(dist_weibull, col = "blue", ci = FALSE, lwd = 2,
     main = "Comparação de Distribuições Paramétricas vs Kaplan-Meier")
lines(dist_lnorm, col = "red", ci = FALSE, lwd = 2)
lines(dist_llogis, col = "green", ci = FALSE, lwd = 2)
lines(dist_gompertz, col = "purple", ci = FALSE, lwd = 2)
legend("bottomleft", 
       legend = c("Weibull", "Log-Normal", "Log-Logística", "Gompertz", "KM"),
       col = c("blue", "red", "green", "purple", "black"),
       lty = c(1, 1, 1, 1, 1), lwd = 2)
Comparação gráfica das distribuições ajustadas

Comparação gráfica das distribuições ajustadas

Justificativa da escolha: Com base nos critérios AIC e BIC, e na interpretação clínica, selecionaremos a distribuição Weibull para o modelo paramétrico. Esta distribuição é flexível, permite modelar riscos crescentes ou decrescentes com o tempo, e possui parâmetros com interpretação clínica útil.

3.3 Modelo Paramétrico Weibull - Seleção de Covariáveis

3.3.1 Modelo Inicial (Todas as Covariáveis)

# Modelo Weibull completo (dados reduzidos - sem Bnp e FE)
weibull_full <- survreg(surv_red ~ Sexo + Idade + Classe_Funcional + IAM + 
                         Rochagan + Beta_Bloqueador + Anticorpos + ECG_Result,
                        data = dados_red, dist = "weibull")

summary(weibull_full)
## 
## Call:
## survreg(formula = surv_red ~ Sexo + Idade + Classe_Funcional + 
##     IAM + Rochagan + Beta_Bloqueador + Anticorpos + ECG_Result, 
##     data = dados_red, dist = "weibull")
##                       Value Std. Error      z       p
## (Intercept)         4.24073    0.37972  11.17 < 2e-16
## SexoFeminino        0.33977    0.08738   3.89 0.00010
## Idade              -0.01522    0.00365  -4.17   3e-05
## Classe_Funcional2  -0.21737    0.13651  -1.59 0.11130
## Classe_Funcional3  -0.15669    0.09007  -1.74 0.08194
## Classe_Funcional4  -1.20426    0.32233  -3.74 0.00019
## IAMSim             -0.16797    0.13788  -1.22 0.22315
## RochaganSim         0.03479    0.11374   0.31 0.75968
## Beta_BloqueadorSim -0.17953    0.08870  -2.02 0.04297
## Anticorpos         -0.02919    0.01730  -1.69 0.09151
## ECG_ResultMaiores  -0.34567    0.10467  -3.30 0.00096
## Log(scale)         -0.88827    0.08588 -10.34 < 2e-16
## 
## Scale= 0.411 
## 
## Weibull distribution
## Loglik(model)= -459.6   Loglik(intercept only)= -509.1
##  Chisq= 98.93 on 10 degrees of freedom, p= 8.9e-17 
## Number of Newton-Raphson Iterations: 8 
## n= 755

3.3.2 Seleção de Variáveis por AIC (Stepwise)

# Seleção stepwise
weibull_step <- stepAIC(weibull_full, direction = "both", trace = FALSE)

cat("Variáveis selecionadas pelo procedimento stepwise:\n")
## Variáveis selecionadas pelo procedimento stepwise:
summary(weibull_step)
## 
## Call:
## survreg(formula = surv_red ~ Sexo + Idade + Classe_Funcional + 
##     Beta_Bloqueador + Anticorpos + ECG_Result, data = dados_red, 
##     dist = "weibull")
##                       Value Std. Error      z       p
## (Intercept)         4.28852    0.35069  12.23 < 2e-16
## SexoFeminino        0.35935    0.08681   4.14 3.5e-05
## Idade              -0.01600    0.00357  -4.48 7.4e-06
## Classe_Funcional2  -0.22517    0.13716  -1.64 0.10065
## Classe_Funcional3  -0.17317    0.08944  -1.94 0.05284
## Classe_Funcional4  -1.17863    0.32146  -3.67 0.00025
## Beta_BloqueadorSim -0.17629    0.08903  -1.98 0.04769
## Anticorpos         -0.02887    0.01611  -1.79 0.07322
## ECG_ResultMaiores  -0.35497    0.10487  -3.38 0.00071
## Log(scale)         -0.88395    0.08585 -10.30 < 2e-16
## 
## Scale= 0.413 
## 
## Weibull distribution
## Loglik(model)= -460.4   Loglik(intercept only)= -509.1
##  Chisq= 97.43 on 8 degrees of freedom, p= 1.4e-17 
## Number of Newton-Raphson Iterations: 8 
## n= 755

3.3.3 Modelo Final Paramétrico

# Ajustar modelo final com variáveis significativas
weibull_final <- survreg(surv_red ~ Sexo + Idade + Classe_Funcional + ECG_Result,
                          data = dados_red, dist = "weibull")

summary(weibull_final)
## 
## Call:
## survreg(formula = surv_red ~ Sexo + Idade + Classe_Funcional + 
##     ECG_Result, data = dados_red, dist = "weibull")
##                      Value Std. Error      z       p
## (Intercept)        3.94258    0.28375  13.89 < 2e-16
## SexoFeminino       0.36144    0.08589   4.21 2.6e-05
## Idade             -0.01619    0.00355  -4.56 5.0e-06
## Classe_Funcional2 -0.24122    0.13599  -1.77 0.07609
## Classe_Funcional3 -0.15913    0.08917  -1.78 0.07432
## Classe_Funcional4 -1.10685    0.30980  -3.57 0.00035
## ECG_ResultMaiores -0.39941    0.10395  -3.84 0.00012
## Log(scale)        -0.88822    0.08579 -10.35 < 2e-16
## 
## Scale= 0.411 
## 
## Weibull distribution
## Loglik(model)= -463.9   Loglik(intercept only)= -509.1
##  Chisq= 90.27 on 6 degrees of freedom, p= 2.7e-17 
## Number of Newton-Raphson Iterations: 8 
## n= 755
# Tabela de coeficientes com hazard ratios (tempo acelerado -> HR)
# Para modelo AFT Weibull: HR = exp(-coef/scale)rm(list = c("coefs", "se_coefs", "af", "z_vals", "p_vals", "weibull_results"), 

# ----- Extrair tabela do modelo Weibull (survreg) -----
tab <- summary(weibull_final)$table

# Remover o intercepto (primeira linha da tabela)
tab_no_int <- tab[-1, , drop = FALSE]

# Parâmetro de escala do modelo AFT Weibull
scale_param <- weibull_final$scale

# Vetores alinhados (todos com MESMO comprimento)
coefs    <- tab_no_int[, "Value"]
se_coefs <- tab_no_int[, "Std. Error"]
z_vals   <- tab_no_int[, "z"]
p_vals   <- tab_no_int[, "p"]

# Fator de aceleração (tempo): AF = exp(-coef/scale)
af <- exp(-coefs / scale_param)

# Montar tabela final (todos os vetores têm o mesmo tamanho)
weibull_results <- data.frame(
  Variavel         = rownames(tab_no_int),
  Coeficiente      = round(coefs, 4),
  EP               = round(se_coefs, 4),
  z                = round(z_vals, 3),
  p_valor          = round(p_vals, 4),
  Fator_Aceleracao = round(af, 3),
  row.names        = NULL
)

# Exibir tabela bonita
kable(
  weibull_results,
  caption   = "Coeficientes do Modelo Weibull Final",
  col.names = c("Variável", "Coeficiente", "Erro Padrão", "z", "p-valor",
                "Fator de Aceleração")
) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Coeficientes do Modelo Weibull Final
Variável Coeficiente Erro Padrão z p-valor Fator de Aceleração
SexoFeminino 0.3614 0.0859 4.208 0.0000 0.415
Idade -0.0162 0.0035 -4.564 0.0000 1.040
Classe_Funcional2 -0.2412 0.1360 -1.774 0.0761 1.797
Classe_Funcional3 -0.1591 0.0892 -1.785 0.0743 1.472
Classe_Funcional4 -1.1068 0.3098 -3.573 0.0004 14.739
ECG_ResultMaiores -0.3994 0.1039 -3.842 0.0001 2.640
Log(scale) -0.8882 0.0858 -10.353 0.0000 8.663
# Parâmetros de escala e forma
cat("\nParâmetro de escala (scale):", round(scale_param, 4), "\n")
## 
## Parâmetro de escala (scale): 0.4114
cat("Parâmetro de forma (shape = 1/scale):", round(1/scale_param, 4), "\n")
## Parâmetro de forma (shape = 1/scale): 2.4308

4 Modelo de Cox

4.1 Modelo de Cox - Seleção de Covariáveis

4.1.1 Modelo Inicial

# Modelo Cox completo (dados reduzidos)
cox_full <- coxph(surv_red ~ Sexo + Idade + Classe_Funcional + IAM + 
                   Rochagan + Beta_Bloqueador + Anticorpos + ECG_Result,
                  data = dados_red)

summary(cox_full)
## Call:
## coxph(formula = surv_red ~ Sexo + Idade + Classe_Funcional + 
##     IAM + Rochagan + Beta_Bloqueador + Anticorpos + ECG_Result, 
##     data = dados_red)
## 
##   n= 755, number of events= 112 
## 
##                         coef exp(coef)  se(coef)      z Pr(>|z|)    
## SexoFeminino       -0.820500  0.440212  0.201905 -4.064 4.83e-05 ***
## Idade               0.036675  1.037356  0.008338  4.398 1.09e-05 ***
## Classe_Funcional2   0.459898  1.583913  0.331231  1.388 0.165000    
## Classe_Funcional3   0.371256  1.449554  0.216753  1.713 0.086749 .  
## Classe_Funcional4   2.874901 17.723675  0.777916  3.696 0.000219 ***
## IAMSim              0.428735  1.535315  0.335861  1.277 0.201770    
## RochaganSim        -0.122172  0.884996  0.278238 -0.439 0.660595    
## Beta_BloqueadorSim  0.450302  1.568786  0.211922  2.125 0.033599 *  
## Anticorpos          0.069784  1.072277  0.042033  1.660 0.096868 .  
## ECG_ResultMaiores   0.813762  2.256380  0.244412  3.329 0.000870 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
##                    exp(coef) exp(-coef) lower .95 upper .95
## SexoFeminino          0.4402    2.27164    0.2963    0.6539
## Idade                 1.0374    0.96399    1.0205    1.0544
## Classe_Funcional2     1.5839    0.63135    0.8275    3.0316
## Classe_Funcional3     1.4496    0.68987    0.9478    2.2168
## Classe_Funcional4    17.7237    0.05642    3.8583   81.4173
## IAMSim                1.5353    0.65133    0.7949    2.9654
## RochaganSim           0.8850    1.12995    0.5130    1.5268
## Beta_BloqueadorSim    1.5688    0.63744    1.0356    2.3766
## Anticorpos            1.0723    0.93259    0.9875    1.1644
## ECG_ResultMaiores     2.2564    0.44319    1.3976    3.6430
## 
## Concordance= 0.777  (se = 0.02 )
## Likelihood ratio test= 96.8  on 10 df,   p=2e-16
## Wald test            = 97.55  on 10 df,   p=<2e-16
## Score (logrank) test = 115.2  on 10 df,   p=<2e-16

4.1.2 Seleção de Variáveis por AIC

# Seleção stepwise
cox_step <- stepAIC(cox_full, direction = "both", trace = FALSE)

cat("Modelo selecionado pelo procedimento stepwise:\n")
## Modelo selecionado pelo procedimento stepwise:
summary(cox_step)
## Call:
## coxph(formula = surv_red ~ Sexo + Idade + Classe_Funcional + 
##     Beta_Bloqueador + Anticorpos + ECG_Result, data = dados_red)
## 
##   n= 755, number of events= 112 
## 
##                         coef exp(coef)  se(coef)      z Pr(>|z|)    
## SexoFeminino       -0.869458  0.419179  0.198134 -4.388 1.14e-05 ***
## Idade               0.038768  1.039529  0.008059  4.810 1.51e-06 ***
## Classe_Funcional2   0.481609  1.618676  0.330659  1.457 0.145252    
## Classe_Funcional3   0.410308  1.507282  0.214032  1.917 0.055233 .  
## Classe_Funcional4   2.788197 16.251699  0.772997  3.607 0.000310 ***
## Beta_BloqueadorSim  0.441603  1.555198  0.211852  2.084 0.037116 *  
## Anticorpos          0.070406  1.072943  0.038818  1.814 0.069718 .  
## ECG_ResultMaiores   0.833741  2.301914  0.243728  3.421 0.000624 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
##                    exp(coef) exp(-coef) lower .95 upper .95
## SexoFeminino          0.4192    2.38562    0.2843    0.6181
## Idade                 1.0395    0.96197    1.0232    1.0561
## Classe_Funcional2     1.6187    0.61779    0.8466    3.0947
## Classe_Funcional3     1.5073    0.66345    0.9909    2.2929
## Classe_Funcional4    16.2517    0.06153    3.5721   73.9392
## Beta_BloqueadorSim    1.5552    0.64301    1.0267    2.3557
## Anticorpos            1.0729    0.93202    0.9943    1.1578
## ECG_ResultMaiores     2.3019    0.43442    1.4277    3.7115
## 
## Concordance= 0.776  (se = 0.02 )
## Likelihood ratio test= 95.06  on 8 df,   p=<2e-16
## Wald test            = 94.18  on 8 df,   p=<2e-16
## Score (logrank) test = 110.9  on 8 df,   p=<2e-16

4.1.3 Modelo Final de Cox

# Modelo final com variáveis significativas
cox_final <- coxph(surv_red ~ Sexo + Idade + Classe_Funcional + ECG_Result,
                    data = dados_red)

summary(cox_final)
## Call:
## coxph(formula = surv_red ~ Sexo + Idade + Classe_Funcional + 
##     ECG_Result, data = dados_red)
## 
##   n= 755, number of events= 112 
## 
##                        coef exp(coef)  se(coef)      z Pr(>|z|)    
## SexoFeminino      -0.878446  0.415428  0.196201 -4.477 7.56e-06 ***
## Idade              0.039258  1.040039  0.008027  4.890 1.01e-06 ***
## Classe_Funcional2  0.519393  1.681008  0.328026  1.583 0.113332    
## Classe_Funcional3  0.379002  1.460826  0.214789  1.765 0.077642 .  
## Classe_Funcional4  2.628179 13.848530  0.750330  3.503 0.000461 ***
## ECG_ResultMaiores  0.941440  2.563670  0.239863  3.925 8.68e-05 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
##                   exp(coef) exp(-coef) lower .95 upper .95
## SexoFeminino         0.4154    2.40715    0.2828    0.6102
## Idade                1.0400    0.96150    1.0238    1.0565
## Classe_Funcional2    1.6810    0.59488    0.8838    3.1973
## Classe_Funcional3    1.4608    0.68454    0.9589    2.2255
## Classe_Funcional4   13.8485    0.07221    3.1822   60.2678
## ECG_ResultMaiores    2.5637    0.39007    1.6021    4.1024
## 
## Concordance= 0.758  (se = 0.022 )
## Likelihood ratio test= 87.64  on 6 df,   p=<2e-16
## Wald test            = 89.86  on 6 df,   p=<2e-16
## Score (logrank) test = 104.9  on 6 df,   p=<2e-16
# Tabela de Hazard Ratios
cox_results <- data.frame(
  Variavel = names(coef(cox_final)),
  HR = exp(coef(cox_final)),
  IC_Inf = exp(confint(cox_final))[,1],
  IC_Sup = exp(confint(cox_final))[,2],
  p_valor = summary(cox_final)$coefficients[,5]
) %>%
  mutate(across(c(HR, IC_Inf, IC_Sup), ~round(., 3)),
         p_valor = ifelse(p_valor < 0.001, "< 0.001", sprintf("%.4f", p_valor)))

kable(cox_results,
      caption = "Hazard Ratios do Modelo de Cox Final",
      col.names = c("Variável", "HR", "IC 95% Inferior", "IC 95% Superior", "p-valor")) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Hazard Ratios do Modelo de Cox Final
Variável HR IC 95% Inferior IC 95% Superior p-valor
SexoFeminino SexoFeminino 0.415 0.283 0.610 < 0.001
Idade Idade 1.040 1.024 1.057 < 0.001
Classe_Funcional2 Classe_Funcional2 1.681 0.884 3.197 0.1133
Classe_Funcional3 Classe_Funcional3 1.461 0.959 2.225 0.0776
Classe_Funcional4 Classe_Funcional4 13.849 3.182 60.268 < 0.001
ECG_ResultMaiores ECG_ResultMaiores 2.564 1.602 4.102 < 0.001
library(survminer)
library(survival)

# garante que os fatores estão no formato certo ANTES de ajustar o modelo
dados_red$Sexo             <- factor(dados_red$Sexo)
dados_red$Classe_Funcional <- factor(dados_red$Classe_Funcional)
dados_red$ECG_Result       <- factor(dados_red$ECG_Result)

# (re)ajusta o modelo já com tudo arrumado
cox_final <- coxph(
  surv_red ~ Sexo + Idade + Classe_Funcional + ECG_Result,
  data = dados_red,
  x = TRUE, model = TRUE
)

# usa exatamente o data do modelo
dados_forest <- model.frame(cox_final)

ggforest(
  cox_final,
  data = dados_forest,
  main = "Hazard Ratios - Modelo de Cox Final"
)
Forest plot dos Hazard Ratios

Forest plot dos Hazard Ratios

5 Avaliação da Qualidade do Ajuste

5.1 Verificação dos Pressupostos do Modelo de Cox

5.1.1 Teste de Riscos Proporcionais (Schoenfeld)

# Teste de proporcionalidade
test_ph <- cox.zph(cox_final)
print(test_ph)
##                  chisq df     p
## Sexo             3.686  1 0.055
## Idade            0.766  1 0.382
## Classe_Funcional 2.484  3 0.478
## ECG_Result       2.738  1 0.098
## GLOBAL           8.671  6 0.193
# Gráficos dos resíduos de Schoenfeld
par(mfrow = c(2, 2))
plot(test_ph)
Resíduos de Schoenfeld ao longo do tempo

Resíduos de Schoenfeld ao longo do tempo

par(mfrow = c(1, 1))

Interpretação: O teste de Schoenfeld avalia a hipótese de riscos proporcionais. Valores de p > 0.05 indicam que não há evidências de violação desta suposição. para alguma além da idade

Conclusão Global do Diagnóstico

Variável Suposição de Riscos Proporcionais
Sexo ❌ Violada
Idade ✅ Atendida
Classe Funcional ❌ Violada
ECG_Result ❌ Violada

5.1.2 Resíduos de Martingale

# Resíduos de Martingale
res_mart <- residuals(cox_final, type = "martingale")

# Gráfico
plot(predict(cox_final), res_mart,
     xlab = "Preditor Linear",
     ylab = "Resíduos de Martingale",
     main = "Resíduos de Martingale vs Preditor Linear")
abline(h = 0, col = "red", lty = 2)
lines(lowess(predict(cox_final), res_mart), col = "blue", lwd = 2)
Resíduos de Martingale vs valores ajustados

Resíduos de Martingale vs valores ajustados

5.1.3 Resíduos de Deviance

# Resíduos de Deviance
res_dev <- residuals(cox_final, type = "deviance")

# Gráfico
plot(predict(cox_final), res_dev,
     xlab = "Preditor Linear",
     ylab = "Resíduos de Deviance",
     main = "Resíduos de Deviance vs Preditor Linear")
abline(h = c(-2, 0, 2), col = c("red", "black", "red"), lty = c(2, 1, 2))
Resíduos de Deviance

Resíduos de Deviance

5.1.4 Verificação da Forma Funcional (Idade)

# Resíduos de Martingale do modelo nulo para verificar forma funcional
cox_null <- coxph(surv_red ~ 1, data = dados_red)
res_null <- residuals(cox_null, type = "martingale")

# Plot para Idade
plot(dados_red$Idade, res_null,
     xlab = "Idade",
     ylab = "Resíduos de Martingale (modelo nulo)",
     main = "Forma Funcional - Idade")
lines(lowess(dados_red$Idade, res_null), col = "red", lwd = 2)
abline(h = 0, lty = 2)
Avaliação da forma funcional - Idade

Avaliação da forma funcional - Idade

5.1.5 Avaliação de Transformações

# Testar transformação logarítmica para Idade
cox_log_idade <- coxph(surv_red ~ Sexo + log(Idade) + Classe_Funcional + ECG_Result,
                        data = dados_red)

# Comparar modelos
cat("AIC - Modelo com Idade linear:", AIC(cox_final), "\n")
## AIC - Modelo com Idade linear: 1306.032
cat("AIC - Modelo com log(Idade):", AIC(cox_log_idade), "\n")
## AIC - Modelo com log(Idade): 1307.405
# Teste de razão de verossimilhança não é apropriado pois não são aninhados
# Usar AIC para comparação

5.2 Avaliação de Interações

# Testar interações potencialmente relevantes
cox_int1 <- coxph(surv_red ~ Sexo * Idade + Classe_Funcional + ECG_Result,
                   data = dados_red)

cox_int2 <- coxph(surv_red ~ Sexo + Idade + Classe_Funcional * ECG_Result,
                   data = dados_red)

cat("AIC - Modelo sem interações:", AIC(cox_final), "\n")
## AIC - Modelo sem interações: 1306.032
cat("AIC - Modelo com Sexo*Idade:", AIC(cox_int1), "\n")
## AIC - Modelo com Sexo*Idade: 1301.231
cat("AIC - Modelo com Classe*ECG:", AIC(cox_int2), "\n\n")
## AIC - Modelo com Classe*ECG: 1311.283
# Teste de razão de verossimilhança para interação Sexo*Idade
anova(cox_final, cox_int1, test = "LRT")
## Analysis of Deviance Table
##  Cox model: response is  surv_red
##  Model 1: ~ Sexo + Idade + Classe_Funcional + ECG_Result
##  Model 2: ~ Sexo * Idade + Classe_Funcional + ECG_Result
##    loglik  Chisq Df Pr(>|Chi|)   
## 1 -647.02                        
## 2 -643.62 6.8012  1    0.00911 **
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

5.3 Comparação Global dos Modelos

# Comparação AIC/BIC
modelos_comp <- data.frame(
  Modelo = c("Cox Final", "Weibull Final", "Cox com log(Idade)", 
             "Cox com interação Sexo*Idade"),
  AIC = c(AIC(cox_final), AIC(weibull_final), AIC(cox_log_idade), AIC(cox_int1)),
  Concordancia = c(
    summary(cox_final)$concordance[1],
    NA,  # Weibull não tem concordância direta
    summary(cox_log_idade)$concordance[1],
    summary(cox_int1)$concordance[1]
  )
) %>%
  mutate(across(where(is.numeric), ~round(., 3)))

kable(modelos_comp,
      caption = "Comparação de Modelos") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Comparação de Modelos
Modelo AIC Concordancia
Cox Final 1306.032 0.758
Weibull Final 943.865 NA
Cox com log(Idade) 1307.405 0.756
Cox com interação Sexo*Idade 1301.231 0.761

5.4 Análise de Influência

# dfbetas para identificar observações influentes
dfb <- residuals(cox_final, type = "dfbetas")

# Plot para principais variáveis
par(mfrow = c(2, 2))
for(i in 1:min(4, ncol(dfb))) {
  plot(dfb[,i], main = colnames(dfb)[i],
       xlab = "Observação", ylab = "dfbeta")
  abline(h = c(-0.2, 0.2), col = "red", lty = 2)
}
Observações influentes (dfbetas)

Observações influentes (dfbetas)

par(mfrow = c(1, 1))

6 Modelo com Dados Completos (incluindo BNP e FE)

Apesar da perda amostral significativa, ajustamos também um modelo incluindo as variáveis Bnp e FE para verificar sua importância prognóstica.

# Verificar N disponível
cat("Observações com dados completos:", nrow(dados_comp), "\n")
## Observações com dados completos: 371
cat("Eventos:", sum(dados_comp$Obito), "\n\n")
## Eventos: 50
if(nrow(dados_comp) >= 50 & sum(dados_comp$Obito) >= 20) {
  
  surv_comp <- Surv(dados_comp$Tempo, dados_comp$Obito)
  
  # Modelo Cox completo
  cox_comp <- coxph(surv_comp ~ Sexo + Idade + Classe_Funcional + IAM + 
                     Rochagan + Beta_Bloqueador + Anticorpos + ECG_Result + 
                     log(Bnp + 1) + FE,
                    data = dados_comp)
  
  summary(cox_comp)
  
  # Seleção stepwise
  cox_comp_step <- stepAIC(cox_comp, direction = "both", trace = FALSE)
  
  cat("\nModelo selecionado (dados completos):\n")
  summary(cox_comp_step)
  
} else {
  cat("Amostra insuficiente para ajuste do modelo completo.\n")
}
## 
## Modelo selecionado (dados completos):
## Call:
## coxph(formula = surv_comp ~ Idade + Classe_Funcional + Rochagan + 
##     Beta_Bloqueador + Anticorpos + log(Bnp + 1) + FE, data = dados_comp)
## 
##   n= 371, number of events= 50 
## 
##                        coef exp(coef) se(coef)      z Pr(>|z|)    
## Idade               0.03892   1.03969  0.01483  2.624  0.00870 ** 
## Classe_Funcional2   0.87374   2.39585  0.56205  1.555  0.12005    
## Classe_Funcional3  -0.20277   0.81646  0.33012 -0.614  0.53905    
## Classe_Funcional4   2.90644  18.29165  1.10335  2.634  0.00843 ** 
## RochaganSim        -0.99551   0.36953  0.51274 -1.942  0.05219 .  
## Beta_BloqueadorSim -0.77513   0.46064  0.36590 -2.118  0.03414 *  
## Anticorpos          0.17896   1.19598  0.08320  2.151  0.03148 *  
## log(Bnp + 1)        0.54888   1.73131  0.12084  4.542 5.57e-06 ***
## FE                 -0.05014   0.95110  0.01171 -4.283 1.85e-05 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
##                    exp(coef) exp(-coef) lower .95 upper .95
## Idade                 1.0397    0.96183    1.0099    1.0704
## Classe_Funcional2     2.3959    0.41739    0.7962    7.2090
## Classe_Funcional3     0.8165    1.22480    0.4275    1.5593
## Classe_Funcional4    18.2917    0.05467    2.1042  159.0111
## RochaganSim           0.3695    2.70612    0.1353    1.0095
## Beta_BloqueadorSim    0.4606    2.17088    0.2249    0.9437
## Anticorpos            1.1960    0.83614    1.0160    1.4078
## log(Bnp + 1)          1.7313    0.57760    1.3662    2.1940
## FE                    0.9511    1.05141    0.9295    0.9732
## 
## Concordance= 0.894  (se = 0.017 )
## Likelihood ratio test= 112.3  on 9 df,   p=<2e-16
## Wald test            = 98.08  on 9 df,   p=<2e-16
## Score (logrank) test = 128.1  on 9 df,   p=<2e-16

7 Interpretação e Conclusões

7.1 Resumo dos Resultados

7.1.1 Fatores de Risco Identificados

Com base nas análises realizadas, os seguintes fatores foram identificados como significativamente associados à mortalidade em pacientes chagásicos:

# Criar tabela resumo final
resumo_final <- data.frame(
  Variavel = c("Sexo (Feminino vs Masculino)", 
               "Idade (por ano adicional)",
               "Classe Funcional 2 vs 1",
               "Classe Funcional 3 vs 1", 
               "Classe Funcional 4 vs 1",
               "ECG Maiores vs Menores"),
  HR = round(exp(coef(cox_final)), 2),
  IC_95 = paste0("(", round(exp(confint(cox_final))[,1], 2), " - ", 
                 round(exp(confint(cox_final))[,2], 2), ")"),
  Interpretacao = c(
    "Mulheres têm menor risco de óbito",
    "Aumento da idade está associado a maior risco",
    "Maior risco em relação à classe 1",
    "Maior risco em relação à classe 1",
    "Maior risco em relação à classe 1",
    "Alterações maiores no ECG aumentam o risco"
  )
)

kable(resumo_final,
      caption = "Resumo dos Fatores de Risco - Modelo de Cox Final",
      col.names = c("Variável", "HR", "IC 95%", "Interpretação")) %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE)
Resumo dos Fatores de Risco - Modelo de Cox Final
Variável HR IC 95% Interpretação
SexoFeminino Sexo (Feminino vs Masculino) 0.42 (0.28 - 0.61) Mulheres têm menor risco de óbito
Idade Idade (por ano adicional) 1.04 (1.02 - 1.06) Aumento da idade está associado a maior risco
Classe_Funcional2 Classe Funcional 2 vs 1 1.68 (0.88 - 3.2) Maior risco em relação à classe 1
Classe_Funcional3 Classe Funcional 3 vs 1 1.46 (0.96 - 2.23) Maior risco em relação à classe 1
Classe_Funcional4 Classe Funcional 4 vs 1 13.85 (3.18 - 60.27) Maior risco em relação à classe 1
ECG_ResultMaiores ECG Maiores vs Menores 2.56 (1.6 - 4.1) Alterações maiores no ECG aumentam o risco

7.2 Discussão

7.2.1 Principais Achados

  1. Sexo: O sexo feminino apresentou efeito protetor em relação à mortalidade. Este achado é consistente com a literatura sobre doença de Chagas e doenças cardiovasculares em geral.

  2. Idade: Como esperado, a idade avançada está fortemente associada ao aumento do risco de óbito. Para cada ano adicional de idade, observa-se um aumento no risco.

  3. Classe Funcional (NYHA): A classificação de insuficiência cardíaca mostrou forte associação com a mortalidade. Pacientes em classes funcionais mais elevadas (3 e 4) apresentam risco substancialmente maior em comparação com a classe 1.

  4. ECG: Alterações eletrocardiográficas maiores foram associadas a maior risco de óbito, refletindo a importância do comprometimento cardíaco na doença de Chagas.

7.2.2 Variáveis Não Significativas

  • IAM: O histórico de infarto não foi significativamente associado ao óbito após ajuste pelas demais variáveis.
  • Rochagan: O uso deste medicamento não mostrou associação significativa.
  • Beta-Bloqueador: Similar ao Rochagan, não apresentou efeito significativo.
  • Anticorpos: Os níveis de anticorpos não foram preditores significativos de mortalidade.

7.2.3 Limitações

  1. Dados Faltantes: As variáveis Bnp e FE apresentaram alta proporção de dados faltantes, limitando sua inclusão no modelo final. Estas são variáveis potencialmente importantes na avaliação prognóstica.

  2. Seleção Amostral: Os 1.000 pacientes foram aleatoriamente selecionados da coorte completa. Embora isso preserve a representatividade, pode haver perda de poder estatístico.

  3. Período de Acompanhamento: O tempo de acompanhamento variou substancialmente entre os pacientes, com alguns tendo seguimento muito curto.

7.2.4 Recomendações

  1. Pacientes com classe funcional elevada (NYHA 3-4) devem receber acompanhamento mais intensivo.

  2. Alterações eletrocardiográficas maiores devem servir como alerta para risco aumentado.

  3. A idade deve ser considerada na estratificação de risco e decisões terapêuticas.

  4. Estudos futuros devem buscar minimizar dados faltantes para variáveis prognósticas importantes como BNP e fração de ejeção.

7.3 Código Completo para Reprodução

Todo o código utilizado nesta análise está disponível neste documento R Markdown e pode ser reproduzido com os dados originais.

8 Referências

  • Kaplan EL, Meier P. Nonparametric estimation from incomplete observations. J Am Stat Assoc. 1958;53(282):457-481.
  • Cox DR. Regression models and life-tables. J R Stat Soc Series B. 1972;34(2):187-220.
  • Therneau TM, Grambsch PM. Modeling Survival Data: Extending the Cox Model. New York: Springer; 2000.
  • Sabino EC, et al. Ten-year incidence of Chagas cardiomyopathy among asymptomatic Trypanosoma cruzi-seropositive former blood donors. Circulation. 2013.

Análise realizada em 30/11/2025 utilizando R versão R version 4.4.2 (2024-10-31 ucrt).

9 Sessão

sessionInfo()
## R version 4.4.2 (2024-10-31 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 26100)
## 
## Matrix products: default
## 
## 
## locale:
## [1] LC_COLLATE=Portuguese_Brazil.utf8  LC_CTYPE=Portuguese_Brazil.utf8   
## [3] LC_MONETARY=Portuguese_Brazil.utf8 LC_NUMERIC=C                      
## [5] LC_TIME=Portuguese_Brazil.utf8    
## 
## time zone: America/Sao_Paulo
## tzcode source: internal
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
##  [1] gridExtra_2.3    MASS_7.3-61      car_3.1-3        carData_3.0-5   
##  [5] flexsurv_2.3.2   kableExtra_1.4.0 knitr_1.49       tidyr_1.3.1     
##  [9] dplyr_1.1.4      survminer_0.5.1  ggpubr_0.6.2     ggplot2_4.0.1   
## [13] survival_3.7-0  
## 
## loaded via a namespace (and not attached):
##  [1] gtable_0.3.6        xfun_0.49           bslib_0.8.0        
##  [4] rstatix_0.7.3       lattice_0.22-6      numDeriv_2016.8-1.1
##  [7] quadprog_1.5-8      vctrs_0.6.5         tools_4.4.2        
## [10] generics_0.1.3      tibble_3.2.1        fansi_1.0.6        
## [13] pkgconfig_2.0.3     Matrix_1.7-1        data.table_1.16.2  
## [16] RColorBrewer_1.1-3  S7_0.2.1            lifecycle_1.0.4    
## [19] compiler_4.4.2      farver_2.1.2        stringr_1.5.1      
## [22] textshaping_0.4.0   statmod_1.5.1       mstate_0.3.3       
## [25] htmltools_0.5.8.1   sass_0.4.9          yaml_2.3.10        
## [28] Formula_1.2-5       pillar_1.9.0        jquerylib_0.1.4    
## [31] cachem_1.1.0        muhaz_1.2.6.4       abind_1.4-8        
## [34] km.ci_0.5-6         tidyselect_1.2.1    digest_0.6.37      
## [37] mvtnorm_1.3-3       stringi_1.8.4       purrr_1.0.2        
## [40] labeling_0.4.3      splines_4.4.2       cowplot_1.1.3      
## [43] fastmap_1.2.0       grid_4.4.2          cli_3.6.5          
## [46] magrittr_2.0.3      utf8_1.2.4          broom_1.0.7        
## [49] withr_3.0.2         scales_1.4.0        backports_1.5.0    
## [52] rmarkdown_2.29      ggtext_0.1.2        deSolve_1.40       
## [55] ggsignif_0.6.4      zoo_1.8-14          evaluate_1.0.1     
## [58] KMsurv_0.1-6        viridisLite_0.4.2   survMisc_0.5.6     
## [61] rlang_1.1.6         gridtext_0.1.5      Rcpp_1.0.13-1      
## [64] xtable_1.8-4        glue_1.8.0          xml2_1.3.6         
## [67] svglite_2.2.1       rstudioapi_0.17.1   jsonlite_1.8.9     
## [70] R6_2.5.1            systemfonts_1.2.3