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:
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.
# 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))## 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
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 (%) |
# 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)| 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
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.
# 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)| 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 |
# 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")| 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 |
# 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)| 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 %)
## Tempo médio de acompanhamento: 6.73 anos
## Tempo mediano de acompanhamento: 7.2 anos
# 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
# 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)| 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 |
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
# 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
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
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
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
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
# 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
# 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
# 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
# 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)| 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 |
Devido à alta proporção de dados faltantes em algumas variáveis
(especialmente Bnp e FE), optamos por
construir dois conjuntos de modelos:
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
## 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
## Eventos: 50
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)| 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
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.
# 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
# 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:
##
## 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
# 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)| 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âmetro de escala (scale): 0.4114
## Parâmetro de forma (shape = 1/scale): 2.4308
# 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
# Seleção stepwise
cox_step <- stepAIC(cox_full, direction = "both", trace = FALSE)
cat("Modelo selecionado pelo procedimento stepwise:\n")## Modelo selecionado pelo procedimento stepwise:
## 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
# 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)| 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
## 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
Resíduos de Schoenfeld ao longo do tempo
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 |
# 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 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 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
# 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
## AIC - Modelo com log(Idade): 1307.405
# 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
## AIC - Modelo com Sexo*Idade: 1301.231
## 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
# 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)| 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 |
# 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)
Apesar da perda amostral significativa, ajustamos também um modelo
incluindo as variáveis Bnp e FE para verificar
sua importância prognóstica.
## Observações com dados completos: 371
## 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
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)| 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 |
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.
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.
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.
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.
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.
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.
Período de Acompanhamento: O tempo de acompanhamento variou substancialmente entre os pacientes, com alguns tendo seguimento muito curto.
Pacientes com classe funcional elevada (NYHA 3-4) devem receber acompanhamento mais intensivo.
Alterações eletrocardiográficas maiores devem servir como alerta para risco aumentado.
A idade deve ser considerada na estratificação de risco e decisões terapêuticas.
Estudos futuros devem buscar minimizar dados faltantes para variáveis prognósticas importantes como BNP e fração de ejeção.
Todo o código utilizado nesta análise está disponível neste documento R Markdown e pode ser reproduzido com os dados originais.
Análise realizada em 30/11/2025 utilizando R versão R version 4.4.2 (2024-10-31 ucrt).
## 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