Análise de Sobrevida e Aprendizado de Máquina em R

Autor
Afiliação

Prof. Letícia Martins Raposo

LAMDS — Laboratório de Análise e Modelagem de Dados em Saúde

Data de Publicação

18 de junho de 2026


NotaSobre esta apostila

Para quem é este material? Esta apostila foi escrita para estudantes e profissionais da área da saúde que têm pouca ou nenhuma experiência com estatística avançada, mas precisam entender — e aplicar — análise de sobrevida em pesquisas clínicas. Não é necessário ter formação matemática profunda: cada conceito é explicado com linguagem simples, analogias do cotidiano e exemplos do contexto de transplante de medula óssea (TMO) e Doença do Enxerto Contra o Hospedeiro (DECH).

Como usar esta apostila? Sugerimos a seguinte estratégia de leitura:

  • Primeira leitura: foque nos textos em prosa, nas analogias e nas caixas coloridas. Não se preocupe ainda com os códigos em R.
  • Segunda leitura: leia o código de cada exemplo e tente rodar no seu próprio computador. Modifique um parâmetro por vez e observe o que muda.
  • Terceira leitura (opcional): aprofunde-se nas equações matemáticas e nos fundamentos teóricos indicados nas referências.

Ícones e caixas de destaque:

Ícone / Cor Significado
💡 Azul (Dica) Analogia ou atalho intuitivo
⚠️ Amarelo (Importante) Ponto que merece atenção especial
❌ Vermelho (Aviso) Erro comum a evitar
📌 Cinza (Nota) Informação complementar ou regra prática

O que você vai aprender? Ao terminar esta apostila, você será capaz de:

  • Entender por que dados de tempo até evento são especiais
  • Construir e interpretar curvas de sobrevivência no R
  • Aplicar o modelo de Cox e ler seus resultados
  • Compreender o que é aprendizado de máquina e como ele se aplica à sobrevida
  • Treinar, avaliar e comparar modelos preditivos com o pacote mlr3proba

Foco clínico: Transplante alogênico de células-tronco hematopoiéticas (TCTH) e Doença do Enxerto Contra o Hospedeiro aguda (DECH aguda) em crianças.

Pré-requisitos: Noções básicas de estatística descritiva (média, porcentagem) e uso elementar do R.

Profundidade: Intermediária — mais profunda do que um tutorial, mais acessível do que um livro técnico.


1 Por Que a Análise de Sobrevida Existe?

Nota📌 O que você vai encontrar neste capítulo

Neste capítulo, entendemos por que a análise de sobrevida foi criada e o que a torna diferente das estatísticas convencionais. Os conceitos principais são:

  • O problema dos dados incompletos (censura)
  • O que chamamos de “evento” e de “tempo até o evento”
  • Por que simplesmente calcular uma média ou uma proporção pode nos enganar

Ao final, você conseguirá explicar para um colega por que não podemos analisar dados de sobrevida com ferramentas convencionais como média ou teste-t.

1.1 A história de três crianças

Imagine que você é pesquisador em um centro de transplante pediátrico. Após anos de coleta de dados, você tem informações de três crianças submetidas a TCTH que desenvolveram DECH aguda:

  • Ana, 6 anos: faleceu aos 120 dias após o transplante.
  • Bruno, 8 anos: estava vivo ao final do estudo, aos 400 dias de seguimento.
  • Clara, 7 anos: mudou de cidade (perdeu o seguimento) aos 200 dias, ainda viva.

Pergunta simples: qual foi o tempo médio de sobrevida dessas três crianças?

Se você calcular ingenuamente: (120 + 400 + 200) / 3 = 240 dias. Mas isso está errado.

Veja o problema: Bruno e Clara ainda estavam vivos quando saíram da observação. O “tempo real” deles é desconhecido — pode ter sido 500, 600 ou 1000 dias. Se jogarmos fora os dados de Bruno e Clara, estamos descartando informação valiosa e ficando apenas com quem morreu, o que produziria uma estimativa extremamente pessimista.

Análise de sobrevida existe exatamente para resolver esse problema: como incorporar, de forma correta, a informação parcial de quem ainda não apresentou o evento.

DicaAnalogia do dia a dia

Imagine que você quer saber qual é a durabilidade média de um celular. Você acompanha 100 aparelhos. Após um ano, 40 quebraram e 60 ainda funcionam. Se você calcular a média só dos que quebraram, vai subestimar muito a durabilidade real. Análise de sobrevida permite incluir os 60 ainda funcionando na estimativa — com o tempo que eles acumularam.

Importante⚠️ O erro mais comum de iniciantes

Ao receber um banco de dados com pacientes que saíram do estudo sem apresentar o evento, muitos pesquisadores cometem um destes dois erros:

  1. Excluir esses pacientes — o que elimina informação real e produz estimativas pessimistas;
  2. Tratar o último tempo observado como se fosse o tempo de evento — o que é matematicamente incorreto e subestima a sobrevida.

A análise de sobrevida foi criada para um terceiro caminho: usar a informação disponível até o momento da censura, sem fingir que sabemos o que aconteceu depois.

1.2 O que é um “evento” na análise de sobrevida?

Em análise de sobrevida, chamamos de evento qualquer desfecho de interesse que ocorre em um momento específico do tempo. O evento pode ser:

Tabela 1: Exemplos de eventos em diferentes áreas de aplicação
Área Evento típico
Oncologia pediátrica / TCTH Óbito, recidiva, falha do enxerto, DECH aguda
Cardiologia Infarto, AVC, hospitalização
Epidemiologia infecciosa Infecção, cura, hospitalização
Engenharia Falha de equipamento
Negócios Cancelamento de plano, inadimplência
Nota📌 Características essenciais de um evento bem definido

Para que a análise funcione corretamente, o evento precisa ter três características:

  1. Clareza: deve ser possível determinar com precisão se e quando ocorreu. “Piora clínica” é vago; “internação em UTI após o dia do transplante” é específico.
  2. Ocorrência única (na maioria dos casos): análise clássica assume que o evento ocorre uma única vez por paciente. Se o evento pode se repetir (ex.: hospitalizações), são necessários métodos de eventos recorrentes.
  3. Relevância clínica: o evento deve ser o desfecho que realmente importa para a pergunta de pesquisa.

No contexto de TCTH/DECH: o óbito por qualquer causa, o óbito relacionado ao transplante (TRM — transplant-related mortality) e a recidiva da doença de base são os três eventos mais comuns e devem ser claramente distinguidos no protocolo.

1.3 O que é “tempo até o evento”?

Em análise de sobrevida, definimos tempo até o evento como o intervalo entre uma origem temporal bem definida — também chamada de tempo zero — e a ocorrência de um evento de interesse. Esse evento pode ser morte, recidiva, falha terapêutica, desenvolvimento de uma complicação, alta hospitalar ou qualquer outro desfecho cuja ocorrência no tempo seja clinicamente relevante.

A escolha da origem temporal é uma decisão metodológica central. Ela determina quando o indivíduo passa a ser considerado em risco para o evento e, portanto, define a interpretação das estimativas de sobrevida, incidência acumulada ou risco ao longo do seguimento.

ImportanteA origem do tempo não é neutra

No contexto do transplante de células-tronco hematopoéticas (TCTH), o tempo pode ser contado a partir de diferentes marcos clínicos, por exemplo:

  • Da data do transplante, quando a pergunta é: qual é a trajetória do paciente desde o procedimento?
  • Da data do diagnóstico de DECH aguda, quando a pergunta é: qual é a evolução após o aparecimento da complicação?
  • Da data de início do tratamento, quando a pergunta é: qual é o tempo até resposta, falha terapêutica ou morte após a intervenção?

Essas escolhas não são intercambiáveis. Cada origem temporal define uma população em risco, uma escala de tempo e uma interpretação clínica distintas. Assim, antes de qualquer análise, é necessário explicitar e justificar o ponto zero.


2 Censura: Lidando com Informação Incompleta

Nota📌 O que você vai encontrar neste capítulo

A censura é o conceito mais importante — e mais frequentemente mal compreendido — da análise de sobrevida. Aqui você vai entender:

  • O que significa uma observação ser “censurada”
  • Os diferentes tipos de censura e quando cada um aparece
  • A hipótese crucial que sustenta os métodos clássicos
  • Por que ignorar os pacientes censurados leva a conclusões erradas
  • Como representar censura corretamente no R

2.1 O que é censura?

Em análise de sobrevida, dizemos que uma observação é censurada quando não observamos o evento de interesse durante todo o período em que gostaríamos, mas ainda assim temos informação útil sobre o tempo até o evento.

Mais precisamente, a censura ocorre quando sabemos que uma pessoa não apresentou o evento até certo momento, mas não sabemos se — ou quando — o evento ocorreu depois. Portanto, a informação não é completa, mas também não é ausente: ela é parcial.

DicaIdeia central

Uma observação censurada informa um limite inferior para o tempo até o evento.

Se uma criança foi acompanhada por 200 dias após o TCTH e não morreu nesse período, sabemos que seu tempo até o óbito é maior que 200 dias, ainda que não saibamos o valor exato.

Nota📌 Um resumo visual de como pensar na censura

Imagine uma linha do tempo para cada paciente:

Paciente com evento:
|────────────────●  (observamos o início E o fim)
t=0             t=evento

Paciente censurado:
|──────────────── X  (observamos o início, mas o fim é desconhecido)
t=0             t=censura    t=??? (evento ocorre em algum momento futuro,
                                    ou talvez nunca)

O símbolo representa o evento observado; o X representa o ponto de censura. O trecho com ??? é a parte que não conseguimos observar — mas sabemos que existe (o paciente ainda estava vivo quando saiu de cena).

2.1.1 Censura à direita

A censura à direita é o tipo mais comum em estudos clínicos. Ela ocorre quando o evento de interesse não foi observado até o último momento em que havia informação disponível sobre o paciente.

Isso pode acontecer, por exemplo, quando:

  • o paciente estava vivo ao final do período de acompanhamento;
  • o paciente perdeu seguimento;
  • o paciente saiu do estudo antes de apresentar o evento;
  • o banco de dados foi fechado antes que o evento ocorresse.

O ponto essencial é: não sabemos que o evento ocorreu depois. Sabemos apenas que ele não foi observado até aquele tempo.

No contexto de TCTH pediátrico, por exemplo, uma criança acompanhada por 365 dias após o transplante, sem óbito registrado até essa data, contribui com informação de sobrevida até 365 dias. Ela não deve ser excluída da análise.

ImportanteCensura não é falha de dado

Uma observação censurada não é um dado “perdido” no sentido usual. Ela contém informação temporal válida: o paciente permaneceu livre do evento pelo menos até o tempo observado.

2.1.2 A hipótese de censura não informativa

Grande parte dos métodos clássicos de análise de sobrevida, como Kaplan-Meier e o modelo de Cox, assume que a censura é não informativa ou independente. Em termos práticos, isso significa que, condicionalmente às variáveis consideradas na análise, o mecanismo que leva à censura não deve estar diretamente relacionado ao risco futuro do evento.

Essa hipótese é plausível quando a censura ocorre por encerramento administrativo do estudo. Por exemplo, se o banco foi fechado em 31 de dezembro e alguns pacientes ainda estavam vivos, essa censura tende a ser determinada pelo calendário do estudo, não pelo prognóstico individual.

A hipótese pode ser mais problemática quando há perda de seguimento. Se pacientes mais graves deixam de retornar ao serviço, a censura pode estar associada ao risco de morte, recaída ou falha terapêutica. Nesse caso, tratá-la como censura simples pode produzir estimativas enviesadas.

AvisoPergunta diagnóstica

Sempre pergunte: o motivo da censura tem relação plausível com o risco do evento? Se a resposta for sim, a interpretação das curvas de sobrevida exige cautela.

2.1.3 Tipos de censura na prática clínica

Na prática, é útil distinguir pelo menos dois cenários frequentes.

Censura administrativa ocorre quando o estudo termina antes que o evento tenha sido observado. Exemplo: uma criança está viva na data de fechamento do banco de dados.

Perda de seguimento ocorre quando o paciente deixa de ser acompanhado antes do fim planejado do estudo. Exemplo: mudança de cidade, transferência de serviço ou ausência de retorno ambulatorial.

Ambas podem ser representadas como censura à direita, mas têm implicações diferentes. A censura administrativa costuma ser mais defensável metodologicamente. A perda de seguimento exige avaliação clínica e epidemiológica mais cuidadosa.

Nota📌 Comparação rápida: tipos de censura na prática
Tipo Causa Defensável? Ação recomendada
Administrativa Banco encerrado em data pré-definida ✅ Sim Verificar se a data era prevista no protocolo
Perda de seguimento por mudança de cidade Paciente mudou de endereço ⚠️ Depende Investigar se pacientes que se mudam são sistematicamente diferentes
Saída por decisão do paciente Recusa em continuar ⚠️ Depende Avaliar se a decisão pode estar relacionada ao estado de saúde
Saída por piora clínica Paciente foi internado em outro serviço ❌ Problemático Considerar análise de sensibilidade; possível censura informativa

2.1.4 Censura não é evento competitivo

Um erro frequente é tratar qualquer situação que impede a observação do evento como censura. Isso nem sempre é correto.

Se o evento de interesse é recidiva (reaparecimento da doença), por exemplo, e a criança morre antes de recidivar, o óbito não é simplesmente uma censura neutra. A morte impede a ocorrência posterior da recidiva observável e, portanto, atua como evento competitivo.

Nessa situação, a análise por Kaplan-Meier pode superestimar a probabilidade acumulada de recidiva. O tratamento adequado geralmente envolve métodos de riscos competitivos, como a função de incidência acumulada.

Nota📌 Regra prática

Censure apenas quando o paciente ainda poderia, em princípio, apresentar o evento depois do último tempo observado. Se outro evento torna impossível ou altera substantivamente a ocorrência do evento de interesse, considere a estrutura de riscos competitivos.

2.1.5 Exemplo concreto — base lung do pacote survival

A base lung, do pacote survival, é frequentemente usada para introduzir censura à direita. Nessa base, a variável status tem codificação própria:

  • status = 1: censurado;
  • status = 2: óbito observado.
library(survival)
library(data.table)

data("lung", package = "survival")
lung_dt <- as.data.table(lung)

# Status original da base lung:
# 1 = censurado
# 2 = óbito observado

head(lung_dt[, .(time, status)], 10)
     time status
    <num>  <num>
 1:   306      2
 2:   455      2
 3:  1010      1
 4:   210      2
 5:   883      2
 6:  1022      1
 7:   310      2
 8:   361      2
 9:   218      2
10:   166      2
lung_dt[, .N, by = status]
   status     N
    <num> <int>
1:      2   165
2:      1    63

Para análises em que o evento deve ser codificado como 1 e a censura como 0, é necessário recodificar a variável.

lung_dt[, status_evento := as.integer(status == 2)]

lung_dt[, .N, by = .(status, status_evento)]
   status status_evento     N
    <num>         <int> <int>
1:      2             1   165
2:      1             0    63

2.1.6 Visualizando censura

Código
library(ggplot2)

df_linhas <- data.frame(
  paciente = paste("Paciente", sprintf("%02d", 1:12)),
  inicio   = 0,
  fim      = c(365, 120, 480, 60, 730, 200, 90, 550, 300, 420, 180, 650),
  desfecho = c(
    "Censura administrativa", "Óbito", "Censura administrativa", "Óbito",
    "Censura administrativa", "Perda de seguimento", "Óbito", "Censura administrativa",
    "Óbito", "Perda de seguimento", "Óbito", "Censura administrativa"
  )
)

# Ordena os pacientes pelo tempo observado
ord <- order(df_linhas$fim)

df_linhas$y_ord <- match(seq_len(nrow(df_linhas)), ord)

cores_desfecho <- c(
  "Óbito"                  = "#c0392b",
  "Censura administrativa" = "#2980b9",
  "Perda de seguimento"    = "#7f8c8d"
)

formas_desfecho <- c(
  "Óbito"                  = 19,
  "Censura administrativa" = 4,
  "Perda de seguimento"    = 4
)

ggplot(df_linhas, aes(y = y_ord)) +
  geom_vline(
    xintercept = 730,
    linetype = "dashed",
    color = "gray40",
    linewidth = 0.8
  ) +
  annotate(
    "text",
    x = 745,
    y = 11.5,
    label = "Fim \ndo\nestudo",
    size = 3,
    hjust = 0,
    color = "gray40"
  ) +
  geom_segment(
    aes(x = inicio, xend = fim, yend = y_ord, color = desfecho),
    linewidth = 1.5
  ) +
  geom_point(
    aes(x = fim, color = desfecho, shape = desfecho),
    size = 4
  ) +
  scale_color_manual(values = cores_desfecho, name = "Situação observada") +
  scale_shape_manual(values = formas_desfecho, name = "Situação observada") +
  scale_y_continuous(
    breaks = df_linhas$y_ord,
    labels = df_linhas$paciente
  ) +
  labs(
    x = "Dias desde o transplante",
    y = NULL,
    title = "Diagrama de acompanhamento — estudo de TCTH pediátrico hipotético",
    subtitle = "X = censura; círculo = óbito observado"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    legend.position = "top",
    panel.grid.minor = element_blank()
  )
Figura 1: Diagrama de acompanhamento de 12 pacientes hipotéticos em um estudo de TCTH pediátrico. Cada linha representa um paciente. O círculo indica óbito observado; o X indica censura. Pacientes censurados contribuem com informação até o momento em que deixam de ser observados.

2.2 Por que não simplesmente ignorar os censurados?

Ignorar observações censuradas é um erro porque elas contêm informação real sobre o tempo de seguimento.

Há duas consequências principais.

1. Viés

Se analisamos apenas os pacientes que apresentaram o evento, deixamos de lado pacientes que permaneceram livres do evento por períodos potencialmente longos. A amostra passa a representar uma subpopulação selecionada: aqueles em quem o evento foi observado.

Em estudos de sobrevida, isso tende a produzir estimativas artificialmente pessimistas, pois os indivíduos com melhor prognóstico ou maior tempo livre de evento são removidos da análise.

2. Perda de informação

Um paciente censurado aos 400 dias informa que permaneceu livre do evento por pelo menos 400 dias. Essa informação deve contribuir para a estimativa da sobrevida até esse tempo.

A análise de sobrevida foi desenvolvida precisamente para incorporar esse tipo de informação parcial sem tratá-la como evento e sem descartá-la como ausência de dado.

NotaPonto-chave

Censura não significa “não sabemos nada”. Significa: sabemos que o evento não ocorreu até certo tempo, mas não conhecemos o tempo completo até o evento.

2.3 Codificando censura no R

No R, a forma mais comum de representar dados de sobrevida é usar duas variáveis:

  • time: o tempo observado;
  • status: o indicador de evento.

Em geral, usamos:

  • status = 1 para evento observado;
  • status = 0 para censura.
library(survival)

criancas <- data.frame(
  paciente = c("Ana", "Bruno", "Clara", "Diego", "Elena"),
  time     = c(120,   400,     200,     350,     80),
  status   = c(1,     0,       0,       1,       1)
)

criancas
  paciente time status
1      Ana  120      1
2    Bruno  400      0
3    Clara  200      0
4    Diego  350      1
5    Elena   80      1
with(criancas, Surv(time, status))
[1] 120  400+ 200+ 350   80 

No objeto produzido por Surv(), o sinal + indica censura. Assim, 400+ significa que o paciente foi observado por 400 dias sem apresentar o evento.

AvisoArmadilha frequente: codificação do status

Nem todas as bases usam a mesma codificação.

Na base lung, por exemplo, status = 1 significa censura e status = 2 significa óbito. Em muitos fluxos de modelagem, porém, espera-se 0 para censura e 1 para evento.

Antes de ajustar qualquer modelo, verifique explicitamente qual valor representa o evento.

# Recodificação da base lung:
lung_dt[, status_evento := as.integer(status == 2)]

# Conferência:
lung_dt[, .N, by = .(status_original = status, status_evento)]
   status_original status_evento     N
             <num>         <int> <int>
1:               2             1   165
2:               1             0    63

3 Os Três Pilares Matemáticos da Análise de Sobrevida

Nota📌 O que você vai encontrar neste capítulo

Toda a análise de sobrevida gira em torno de três funções matemáticas que descrevem o mesmo fenômeno de ângulos diferentes. São elas:

  1. S(t) — a função de sobrevivência: “Qual é a chance de ainda estar bem no tempo t?”
  2. h(t) — a função de hazard (risco instantâneo): “Qual é o perigo neste exato momento?”
  3. H(t) — o risco acumulado: “Quanto risco total foi acumulado até agora?”

Elas são matematicamente relacionadas — conhecendo uma, podemos calcular as outras. Você não precisa memorizar as fórmulas agora, mas entender a intuição de cada uma é fundamental para interpretar corretamente qualquer modelo.

Nesta seção apresentamos os três conceitos matemáticos centrais. Não se preocupe se a notação parecer intimidadora no início — vamos construir a intuição passo a passo.

3.1 A função de sobrevivência S(t): “Qual é a chance de ainda estar bem?”

A função de sobrevivência S(t) responde à pergunta:

“Qual é a probabilidade de uma pessoa permanecer sem o evento após o tempo t?”

Formalmente: S(t) = P(T > t), onde T é o tempo até o evento.

3.1.1 Propriedades que fazem sentido intuitivo

  • S(0) = 1 — no início do estudo, todos estão “em risco” (ninguém ainda apresentou o evento)

  • S(t) nunca sobe — conforme o tempo passa, a probabilidade de sobreviver só pode diminuir ou ficar igual

  • S(t) \to 0 quando t \to \infty — em populações sem fração curada*, assume-se que a probabilidade de permanecer livre do evento tende a zero no longo prazo

  • Chamamos de fração curada a proporção de indivíduos que, em termos do evento de interesse, não parece mais estar suscetível ao evento no longo prazo. Nesses casos, a função de sobrevida não tende a zero, mas se estabiliza em um platô positivo:

S(t) \to \pi > 0 \quad \text{quando } t \to \infty,

em que \pi representa a fração curada.

Nota📌 Como ler S(t) de forma intuitiva — passo a passo

Quando você encontrar um valor de S(t) em um artigo ou relatório, siga estes três passos:

Passo 1 — Identifique o tempo: qual é o valor de t? (dias, meses, anos após o início do seguimento)

Passo 2 — Leia o valor de S: S(t) é um número entre 0 e 1, interpretado como probabilidade.

Passo 3 — Traduza para linguagem clínica: - S(90) = 0.85 → “85% dos pacientes permaneceram sem o evento após 90 dias” - S(365) = 0.60 → “60% dos pacientes permaneceram sem o evento após 1 ano” - S(730) = 0.45 → “45% dos pacientes permaneceram sem o evento após 2 anos”

Equivalentemente, você pode calcular o complemento: se S(365) = 0.60, então 1 - 0.60 = 0.40, ou seja, 40% dos pacientes apresentaram o evento até 1 ano.

DicaLendo uma curva S(t) na prática

Se S(180) = 0.65 em um estudo de DECH pós-TCTH, isso significa:

“65% dos pacientes ainda estavam vivos 180 dias após o transplante.”

Ou equivalentemente: a probabilidade de sobreviver além de 180 dias é de 65%.

3.1.2 Visualizando S(t) para diferentes distribuições

Código
library(ggplot2)

t_seq <- seq(0, 730, length.out = 500)

df_dists <- rbind(
  data.frame(
    t    = t_seq,
    S    = exp(-0.004 * t_seq),
    dist = "Exponencial\n(risco constante)"
  ),
  data.frame(
    t    = t_seq,
    S    = exp(-(0.003 * t_seq)^1.8),
    dist = "Weibull (k=1.8)\n(risco crescente)"
  ),
  data.frame(
    t    = t_seq,
    S    = 1 - pnorm((log(pmax(t_seq, 0.1)) - log(200)) / 0.8),
    dist = "Log-normal\n(risco em sino)"
  )
)

cores3 <- c(
  "Exponencial\n(risco constante)"  = "#1a6faf",
  "Weibull (k=1.8)\n(risco crescente)" = "#d95f02",
  "Log-normal\n(risco em sino)"     = "#2ca02c"
)

ggplot(df_dists, aes(t, S, color = dist)) +
  geom_line(linewidth = 1.3) +
  geom_hline(yintercept = 0.5, linetype = "dotted", color = "gray50") +
  annotate("text", x = 10, y = 0.52, label = "Mediana (S = 0.5)",
           hjust = 0, size = 3.2, color = "gray40") +
  scale_color_manual(values = cores3, name = NULL) +
  scale_y_continuous(labels = scales::percent, limits = c(0, 1)) +
  scale_x_continuous(breaks = seq(0, 730, 100)) +
  labs(
    x        = "Dias desde o transplante",
    y        = "S(t) — Probabilidade de sobrevivência",
    title    = "Comportamento de S(t) para diferentes distribuições",
    subtitle = "Linha pontilhada marca a mediana de sobrevida (S = 50%)"
  ) +
  theme_minimal(base_size = 12) +
  theme(legend.position = "top", legend.text = element_text(size = 10))
Figura 2: Curvas de sobrevivência S(t) para três distribuições diferentes. A distribuição exponencial tem taxa de risco constante ao longo do tempo (o risco é igual em qualquer momento). A distribuição Weibull com forma > 1 tem risco crescente (típico de doenças progressivas). A distribuição log-normal tem risco que sobe e depois cai (típico de algumas infecções pós-TCTH que têm pico de risco nos primeiros meses e depois diminuem).

3.2 A função de hazard h(t): “Qual é o risco agora?”

A função de hazard (também chamada de taxa de risco ou função de risco instantâneo) responde:

“Entre os pacientes que ainda não apresentaram o evento, qual é a intensidade do risco neste exato momento?”

Formalmente: h(t) = \lim_{\Delta t \to 0} \frac{P(t \leq T < t + \Delta t \mid T \geq t)}{\Delta t}

3.2.1 Intuição sem matemática

Pense no hazard como a “velocidade” com que os eventos estão ocorrendo num dado instante, apenas entre quem ainda não apresentou o evento.

DicaAnalogia: velocímetro de um carro

Se você está dirigindo a 80 km/h, isso não significa que percorreu 80 km — é a velocidade instantânea naquele momento. Da mesma forma, h(t) = 0.03 eventos/dia não é uma probabilidade — é a intensidade instantânea de ocorrência do evento entre quem ainda está em risco.

3.2.2 Formatos típicos de hazard em contextos clínicos

  • Constante: h(t) = \lambda — o risco é o mesmo em qualquer momento (distribuição exponencial). Raramente realista clinicamente.
  • Crescente: h(t) aumenta com o tempo — mais comum em doenças progressivas ou envelhecimento.
  • Em forma de U: risco alto no início (complicações imediatas), baixo no meio, alto no fim (envelhecimento). Comum em TCTH: alto risco nos primeiros meses (DECH aguda, infecções), menor risco no médio prazo, crescente a longo prazo (recidiva, DECH crônica).
  • Em sino / uni-modal: risco sobe e depois cai — típico de infecções curáveis.
Código
t_seq <- seq(0.1, 730, length.out = 500)

df_haz <- rbind(
  data.frame(t = t_seq, h = rep(0.004, length(t_seq)),
             forma = "Constante (exponencial)"),
  data.frame(t = t_seq, h = 0.000025 * t_seq,
             forma = "Crescente (Weibull k > 1)"),
  data.frame(t = t_seq, h = 0.012 * exp(-t_seq / 60) + 0.001 + 0.000003 * t_seq,
             forma = "Forma de U — típico TCTH"),
  data.frame(t = t_seq, h = 0.01 * (t_seq / 100) * exp(-t_seq / 100),
             forma = "Em sino (log-normal)")
)

cores_haz <- c(
  "Constante (exponencial)"  = "#1a6faf",
  "Crescente (Weibull k > 1)"= "#2ca02c",
  "Forma de U — típico TCTH" = "#d95f02",
  "Em sino (log-normal)"     = "#9467bd"
)

ggplot(df_haz, aes(t, h, color = forma, linetype = forma)) +
  geom_line(linewidth = 1.2) +
  scale_color_manual(values = cores_haz, name = NULL) +
  scale_linetype_manual(
    values = c("solid","solid","dashed","solid"), name = NULL) +
  labs(
    x        = "Dias desde o transplante",
    y        = "h(t) — Intensidade instantânea de risco",
    title    = "Formas típicas da função de hazard",
    subtitle = "O padrão pós-TCTH geralmente tem forma de U: alto risco inicial, queda e possível \nelevação tardia"
  ) +
  theme_minimal(base_size = 12) +
  theme(legend.position = "top", legend.text = element_text(size = 9))
Figura 3: Formas típicas da função de hazard h(t) em contextos clínicos. O padrão em forma de U (linha pontilhada laranja) é característico do seguimento pós-TCTH: o risco é elevado nos primeiros meses (DECH aguda, infecções oportunistas), diminui no médio prazo e pode voltar a crescer em longo prazo (recidiva, DECH crônica, efeitos tardios).

3.3 Risco acumulado H(t) e a conexão com S(t)

O risco acumulado, ou função de risco acumulado, H(t), mede a quantidade total de risco instantâneo acumulada desde o tempo 0 até o tempo t. Ele é definido como a integral da função de risco instantâneo:

H(t) = \int_0^t h(u),du.

A conexão fundamental entre o risco acumulado e a função de sobrevida é:

S(t) = \exp{-H(t)}.

Equivalentemente,

H(t) = -\log S(t).

Essa relação mostra que a sobrevida diminui à medida que o risco acumulado aumenta. Quando H(t)=0, temos S(t)=1; isto é, nenhum risco foi acumulado até aquele tempo. À medida que H(t) cresce, S(t) se aproxima de zero.

A interpretação é direta: H(t) resume a exposição acumulada ao risco do evento até o tempo t, enquanto S(t) traduz essa exposição acumulada em probabilidade de permanecer livre do evento até esse tempo.

DicaAnalogia: conta bancária negativa

Imagine que seu saldo começa em 100% e cada dia de risco “debita” um valor. O hazard h(t) é a taxa diária de débito; H(t) é o total debitado até hoje; S(t) = e^{-H(t)} é quanto sobrou. Quanto maior o débito acumulado, mais próximo de zero o saldo.

Código
t_v <- seq(0, 730, length.out = 400)
lambda <- 0.004
df_sh <- data.frame(
  t  = t_v,
  Ht = lambda * t_v,
  St = exp(-lambda * t_v)
)

library(ggplot2)

p_st <- ggplot(df_sh, aes(t, St)) +
  geom_line(color = "#1a6faf", linewidth = 1.3) +
  scale_y_continuous(labels = scales::percent, limits = c(0, 1)) +
  labs(x = "Dias", y = "S(t) — Sobrevivência",
       title = "Função de sobrevivência \nS(t) = exp(–H(t))") +
  theme_minimal(base_size = 12)

p_ht <- ggplot(df_sh, aes(t, Ht)) +
  geom_line(color = "#d95f02", linewidth = 1.3) +
  labs(x = "Dias", y = "H(t) — Risco acumulado",
       title = "Risco acumulado H(t)") +
  theme_minimal(base_size = 12)

library(patchwork)
p_st | p_ht
Figura 4: Relação entre o risco acumulado H(t) e a função de sobrevivência S(t) = exp(-H(t)). À medida que H(t) aumenta, S(t) decresce de forma exponencial. Observe como o eixo esquerdo (S, azul) vai de 1 a 0, enquanto H(laranja) vai de 0 para cima. Os dois descrevem a mesma realidade de ângulos diferentes.

4 Estimador de Kaplan-Meier: Descrevendo a Sobrevida sem Pressupostos

Nota📌 O que você vai encontrar neste capítulo

O estimador de Kaplan-Meier (KM) é provavelmente o gráfico mais publicado em artigos de medicina e oncologia. Aqui você vai aprender:

  • Como o KM funciona “por dentro” (com exemplo numérico passo a passo)
  • Como construir curvas KM no R com código comentado
  • Como ler cada elemento visual da curva e o que ele significa
  • Como calcular e interpretar a mediana de sobrevida
  • Quais armadilhas evitar na interpretação

Ao final deste capítulo, você conseguirá interpretar qualquer curva de Kaplan-Meier publicada em artigos científicos.

4.1 A ideia central

O estimador de Kaplan-Meier (KM) (KAPLAN & MEIER, 1958) é o método mais usado para descrever a função de sobrevivência a partir de dados reais, sem precisar assumir nenhuma distribuição para o tempo. É chamado de método não paramétrico por isso.

A ideia é a seguinte: para chegar vivo até o tempo t, o paciente precisa “passar” por todos os momentos anteriores em que houve eventos no estudo. Em cada um desses momentos, calculamos a chance de continuar sem o evento. A probabilidade de sobreviver até t é obtida multiplicando essas pequenas chances sucessivas.

4.1.1 Como funciona passo a passo

Em cada tempo em que ocorre pelo menos um evento t_j, calculamos:

\hat{S}(t) = \prod_{t_j \leq t} \left(1 - \frac{d_j}{n_j}\right)

Onde d_j = número de eventos (mortes) no tempo t_j; n_j = número de pacientes ainda em risco imediatamente antes de t_j

DicaExemplo simplificado

Imagine 5 pacientes pós-TCTH. Os eventos ocorrem nos dias 30, 90, 120 (dois eventos) e um paciente é censurado no dia 60.

Dia Em risco (n_j) Eventos (d_j) 1 - d_j/n_j \hat{S}(t)
30 5 1 4/5 = 0.80 0.80
90 3 1 2/3 = 0.67 0.80 × 0.67 = 0.53
120 2 2 0/2 = 0.00 0.53 × 0.00 = 0.00

Note: no dia 60, o paciente foi censurado. Ele reduziu n_j de 4 para 3 antes do dia 90, mas não causou queda na curva.

Nota📌 O que acontece com os pacientes censurados no cálculo?

Pacientes censurados não causam queda na curva — eles apenas saem do denominador (n_j) nos tempos seguintes. Isso é o mecanismo matemático que incorpora a informação parcial deles: eles “contribuíram” com seu tempo de seguimento antes da censura, mas não são contados como eventos.

Esse é exatamente o ponto que resolve o problema das crianças Bruno e Clara da primeira seção!

Dica💡 Por que ‘Kaplan-Meier’?

O estimador leva o nome de dois estatísticos americanos, Edward Kaplan e Paul Meier, que publicaram o método em 1958 no Journal of the American Statistical Association. Esse artigo se tornou um dos mais citados da história da estatística. A ideia central — multiplicar probabilidades condicionais de sobrevida em cada instante com evento — é elegante, computacionalmente simples e extremamente robusta, razão pela qual continua sendo o padrão ouro para descrição de dados de sobrevida mais de 60 anos depois.

library(survival)
library(data.table)

# Carrega e prepara os dados
data("lung", package = "survival")
lung_dt <- as.data.table(lung)
lung_dt[, status := as.integer(status == 2)]  # recodifica: 1=evento, 0=censura
lung_dt[, sex := factor(sex, 1:2, c("Masculino", "Feminino"))]

vars <- c("time","status","age","sex","ph.ecog","ph.karno","pat.karno",
          "meal.cal","wt.loss")
lung_limpo <- na.omit(lung_dt[, ..vars])

# Curva KM global
km_global <- survfit(Surv(time, status) ~ 1, data = lung_limpo)

# Resumo: probabilidades em tempos específicos
summary(km_global, times = c(90, 180, 365, 540))
Call: survfit(formula = Surv(time, status) ~ 1, data = lung_limpo)

 time n.risk n.event survival std.err lower 95% CI upper 95% CI
   90    148      20    0.881  0.0250        0.833        0.931
  180    122      23    0.743  0.0338        0.679        0.812
  365     49      45    0.411  0.0420        0.336        0.502
  540     25      16    0.260  0.0402        0.192        0.352
Código
library(ggsurvfit)

survfit2(Surv(time, status) ~ sex, data = lung_limpo) |>
  ggsurvfit(linewidth = 1.2) +
  add_confidence_interval(alpha = 0.15) +
  add_risktable(
    risktable_stats  = "n.risk",
    risktable_height = 0.28,
    theme = theme_risktable_default(axis.text.y.size = 10, plot.title.size = 10)
  ) +
  add_quantile(y_value = 0.50, linetype = "dashed",
               color = "gray40", linewidth = 0.8) +
  scale_color_manual(values = c("#1a6faf", "#d95f02"), name = NULL) +
  scale_fill_manual(values  = c("#1a6faf", "#d95f02"), name = NULL) +
  scale_y_continuous(labels = scales::percent, limits = c(0, 1)) +
  labs(
    x        = "Dias de acompanhamento",
    y        = "Probabilidade de sobrevivência S(t)",
    title    = "Curvas de Kaplan-Meier por sexo — Base lung",
    subtitle = "IC 95% pelo método log-log | Tabela: número em risco"
  ) +
  theme_ggsurvfit_default() +
  theme(legend.position = "top")
Figura 5: Curvas de Kaplan-Meier para a base lung, estratificadas por sexo. A linha tracejada horizontal em S = 0.50 indica a mediana de sobrevida. A faixa sombreada representa o intervalo de confiança de 95% (método log-log, mais adequado para amostras pequenas e caudas). A tabela abaixo do gráfico mostra o número de pacientes ainda em risco em cada tempo — essencial para interpretar a confiabilidade das estimativas: quando o número em risco é muito pequeno (< 10), a curva naquela região é pouco confiável.

4.2 Como ler e interpretar a curva KM

Cada elemento visual da curva tem um significado:

Tabela 2: Guia de leitura da curva de Kaplan-Meier
Elemento O que significa
Cada degrau vertical Um ou mais eventos ocorreram naquele momento
Trecho horizontal Período sem eventos observados
Marca + na linha Observação censurada naquele tempo
Faixa sombreada (IC 95%) Incerteza da estimativa; mais larga quando poucos pacientes restam
Linha pontilhada em S = 0.5 Mediana de sobrevida — o tempo em que metade dos pacientes já apresentou o evento
Tabela de risco abaixo Número de pacientes ainda “em jogo” em cada tempo
ImportanteCuidado com a cauda da curva

Quando restam poucos pacientes (ex.: menos de 10 em risco), o intervalo de confiança fica muito largo e as estimativas se tornam instáveis. Não interprete diferenças visuais na cauda da curva sem verificar o número em risco.

4.3 Mediana de sobrevida

A mediana de sobrevida é o tempo t_{0.5} tal que S(t_{0.5}) = 0.5, ou seja, o tempo em que 50% dos pacientes já apresentaram o evento. É preferível à média porque:

  1. Não é afetada por tempos extremos de sobrevida (outliers)
  2. Pode ser calculada mesmo quando 50% dos pacientes são censurados
# Mediana de sobrevida por sexo
km_sex <- survfit(Surv(time, status) ~ sex, data = lung_limpo)
print(km_sex)  # Exibe mediana e IC 95%
Call: survfit(formula = Surv(time, status) ~ sex, data = lung_limpo)

                n events median 0.95LCL 0.95UCL
sex=Masculino 104     83    284     229     353
sex=Feminino   64     38    426     345     641
NotaContextualizando no TCTH

Em um estudo de DECH aguda pós-TCTH pediátrico, se a mediana de sobrevida for 180 dias, isso significa que 50% das crianças com DECH aguda morreram antes de 180 dias após o transplante. Se compararmos dois grupos de doadores (aparentado vs. não aparentado) e um tiver mediana de 365 dias e o outro 180 dias, temos uma diferença clinicamente relevante que mereceria investigação.


5 Teste Log-Rank: Comparando Curvas Entre Grupos

5.1 O que o teste log-rank faz?

Quando temos dois ou mais grupos e queremos saber se as curvas de sobrevivência são estatisticamente diferentes, usamos o teste log-rank (MANTEL, 1966).

Hipóteses:

  • H_0: as curvas de sobrevivência dos grupos são iguais ao longo de todo o seguimento
  • H_1: pelo menos um grupo tem curva diferente em algum momento
DicaAnalogia: comparando corridas de maratona

Você quer saber se corredores do grupo A chegam antes do grupo B. O teste log-rank olha para cada “chegada” e verifica se os grupos chegam em proporções esperadas. Se o grupo A chega muito mais antes do que o esperado ao acaso, o p-valor será pequeno.

5.2 Como interpretar o p-valor

  • p < 0.05: evidência estatística de diferença global entre as curvas (no nível de 5%)
  • p ≥ 0.05: sem evidência suficiente para rejeitar a hipótese de igualdade
ImportanteO que o log-rank NÃO diz

Um p-valor pequeno indica que as curvas são diferentes, mas não diz:

  • Onde no tempo a diferença é maior
  • Quanto maior é o risco em um grupo vs. outro (isso é o hazard ratio do Cox)
  • Se a diferença é clinicamente relevante (estatística ≠ clínica)
  • Se a diferença se mantém após controle por covariáveis (sexo, idade, tipo de doador…)

O log-rank deve ser sempre acompanhado das curvas KM e, em seguida, de um modelo de Cox.

# Teste log-rank por sexo
lr_sex <- survdiff(Surv(time, status) ~ sex, data = lung_limpo)
print(lr_sex)
Call:
survdiff(formula = Surv(time, status) ~ sex, data = lung_limpo)

                N Observed Expected (O-E)^2/E (O-E)^2/V
sex=Masculino 104       83     69.5      2.60      6.16
sex=Feminino   64       38     51.5      3.52      6.16

 Chisq= 6.2  on 1 degrees of freedom, p= 0.01 
# Teste log-rank por ph.ecog categorizado
lung_limpo[, ecog_cat := ifelse(ph.ecog <= 1, "ECOG 0-1 (melhor)", "ECOG 2-3 (pior)")]
lr_ecog <- survdiff(Surv(time, status) ~ ecog_cat, data = lung_limpo)
print(lr_ecog)
Call:
survdiff(formula = Surv(time, status) ~ ecog_cat, data = lung_limpo)

                             N Observed Expected (O-E)^2/E (O-E)^2/V
ecog_cat=ECOG 0-1 (melhor) 128       86    100.7      2.15      12.9
ecog_cat=ECOG 2-3 (pior)    40       35     20.3     10.64      12.9

 Chisq= 12.9  on 1 degrees of freedom, p= 3e-04 
Código
lung_limpo[, ecog_cat := ifelse(ph.ecog <= 1, "ECOG 0-1\n(melhor estado geral)",
                                              "ECOG 2-3\n(pior estado geral)")]

survfit2(Surv(time, status) ~ ecog_cat, data = lung_limpo) |>
  ggsurvfit(linewidth = 1.2) +
  add_confidence_interval(alpha = 0.15) +
  add_risktable(risktable_stats = "n.risk",
                risktable_height = 0.26,
                theme = theme_risktable_default(axis.text.y.size = 10)) +
  add_pvalue(location = "annotation", x = 600, y = 0.85) +
  scale_color_manual(values = c("#1a6faf", "#c0392b"), name = NULL) +
  scale_fill_manual( values = c("#1a6faf", "#c0392b"), name = NULL) +
  scale_y_continuous(labels = scales::percent) +
  labs(
    x = "Dias de acompanhamento",
    y = "Probabilidade de sobrevivência S(t)",
    title = "Curvas KM por Performance Status (ECOG)",
    subtitle = "Base lung | Análogo ao uso em estudos de TCTH pediátrico"
  ) +
  theme_ggsurvfit_default() +
  theme(legend.position = "top")
Figura 6: Curvas de Kaplan-Meier estratificadas por performance status (ECOG categorizado). Pacientes com ECOG 0–1 (melhor estado geral) apresentam curva consistentemente acima da de ECOG 2–3. O p-valor do teste log-rank indica se essa diferença visual é estatisticamente significativa. No contexto de TCTH, o performance status pré-transplante é um dos principais preditores de mortalidade.

6 Modelo de Cox: Quantificando o Efeito das Covariáveis

Nota📌 O que você vai encontrar neste capítulo

Enquanto o Kaplan-Meier descreve a sobrevida, o modelo de Cox explica quais fatores influenciam o risco ao longo do tempo. Aqui você vai aprender:

  • Por que precisamos de um modelo multivariado (além do KM)
  • O que é o Hazard Ratio (HR) e como interpretá-lo corretamente
  • Como ajustar e ler a saída do modelo de Cox no R
  • O que é o pressuposto de riscos proporcionais e como testá-lo
  • O que fazer quando o pressuposto é violado

O modelo de Cox é o mais publicado na literatura médica para dados de sobrevida. Entender o HR é essencial para ler artigos da área.

6.1 Motivação: por que precisamos de um modelo?

O estimador KM e o teste log-rank são ferramentas univariadas — analisam uma variável por vez. Na prática clínica, a realidade é mais complexa: uma criança com DECH aguda tem simultaneamente uma certa idade, um tipo de doador, um grau de compatibilidade HLA, um regime de condicionamento, etc. Todos esses fatores podem influenciar o desfecho de forma conjunta.

O Modelo de Riscos Proporcionais de Cox (COX, 1972) permite analisar o efeito de múltiplas covariáveis sobre o hazard ao mesmo tempo.

6.2 A equação do modelo de Cox

Para o paciente i com características clínicas \mathbf{x}_i = (x_1, x_2, \ldots, x_p):

h_i(t) = h_0(t) \cdot \exp(\beta_1 x_{i1} + \beta_2 x_{i2} + \cdots + \beta_p x_{ip})

Dois componentes:

  • h_0(t): o hazard basal — o risco de um paciente “referência” (todas as covariáveis em zero ou na categoria de referência). Não é parametrizado — daí o nome “semiparamétrico”.
  • \exp(\beta_j x_{ij}): o multiplicador que modifica o risco basal de acordo com as características do paciente.
DicaAnalogia: o risco como volume de música

Imagine que h_0(t) é o volume base de uma música. Cada covariável é como um botão de ajuste: se \exp(\beta_j) = 2, esse botão dobra o volume; se \exp(\beta_j) = 0.5, ele reduz à metade. O modelo de Cox diz qual é o “fator de amplificação” de cada característica clínica sobre o risco de morte.

6.3 Hazard Ratio (HR): o coração da interpretação

A principal quantidade do modelo de Cox é o Hazard Ratio (HR), ou Razão de Riscos:

HR_j = e^{\beta_j}

O HR compara o risco instantâneo entre dois grupos (ou entre dois valores de uma covariável contínua):

HR = \frac{h(t \mid \text{grupo A})}{h(t \mid \text{grupo B})} = e^\beta

6.3.1 Guia de interpretação

NotaComo interpretar o HR
HR Interpretação
HR = 1 Nenhuma diferença de risco entre os grupos
HR = 2 O grupo A tem o dobro do risco instantâneo do grupo B
HR = 0.5 O grupo A tem metade do risco instantâneo do grupo B
HR = 1.5 O grupo A tem 50% a mais de risco que o grupo B
HR = 0.7 O grupo A tem 30% a menos de risco que o grupo B

Exemplos no contexto de TCTH/DECH:

  • HR = 2.3 para DECH grau IV vs. grau I → pacientes com DECH grau IV têm 2,3 vezes mais risco instantâneo de óbito
  • HR = 0.6 para doador aparentado vs. não aparentado → doador aparentado reduz o risco em 40%
  • HR = 1.02 por ano de idade → cada ano de idade aumenta o risco em 2%
Nota📌 O intervalo de confiança do HR e a significância estatística

Junto com o valor do HR, os artigos geralmente reportam o intervalo de confiança de 95% (IC 95%) e o p-valor. Veja como interpretar:

Situação Interpretação
IC 95% não contém 1 (ex.: [1.2, 2.7]) Resultado estatisticamente significativo (α = 5%)
IC 95% contém 1 (ex.: [0.8, 2.1]) Sem evidência estatística de diferença de risco
IC muito largo (ex.: [0.3, 6.8]) Incerteza alta — amostra provavelmente pequena
IC estreito (ex.: [1.4, 1.6]) Alta precisão da estimativa

Regra prática: se o IC 95% cruza o valor 1, o p-valor geralmente será maior que 0.05, indicando que não há evidência estatística suficiente para afirmar que os grupos têm riscos diferentes.

ImportanteO que o HR NÃO significa

HR = 2 não significa que o grupo A tem o dobro de probabilidade de morrer em qualquer horizonte temporal específico.

HR descreve a razão de taxas instantâneas. A tradução para probabilidade acumulada depende do hazard basal, do tempo de seguimento e das demais covariáveis. Para estimar probabilidades concretas (ex.: “qual é a chance de sobreviver 1 ano?”), precisamos da curva de sobrevivência ajustada.

6.4 Ajustando o modelo de Cox no R

library(survival)

# Modelo com múltiplas covariáveis
cox_modelo <- coxph(
  Surv(time, status) ~ age + sex + ph.ecog + ph.karno + wt.loss,
  data = lung_limpo
)

# Resultado completo
summary(cox_modelo)
Call:
coxph(formula = Surv(time, status) ~ age + sex + ph.ecog + ph.karno + 
    wt.loss, data = lung_limpo)

  n= 168, number of events= 121 

                 coef exp(coef)  se(coef)      z Pr(>|z|)    
age          0.011350  1.011415  0.011403  0.995 0.319570    
sexFeminino -0.558825  0.571881  0.198829 -2.811 0.004945 ** 
ph.ecog      0.825854  2.283830  0.215038  3.841 0.000123 ***
ph.karno     0.020059  1.020261  0.011057  1.814 0.069664 .  
wt.loss     -0.011743  0.988325  0.007598 -1.546 0.122191    
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

            exp(coef) exp(-coef) lower .95 upper .95
age            1.0114     0.9887    0.9891    1.0343
sexFeminino    0.5719     1.7486    0.3873    0.8444
ph.ecog        2.2838     0.4379    1.4984    3.4810
ph.karno       1.0203     0.9801    0.9984    1.0426
wt.loss        0.9883     1.0118    0.9737    1.0032

Concordance= 0.639  (se = 0.03 )
Likelihood ratio test= 25.95  on 5 df,   p=9e-05
Wald test            = 25.14  on 5 df,   p=1e-04
Score (logrank) test = 25.6  on 5 df,   p=1e-04

6.4.1 Lendo a saída do coxph()

# Extrai os coeficientes em formato organizado
cox_resultado <- as.data.frame(summary(cox_modelo)$conf.int)
cox_resultado$variavel <- rownames(cox_resultado)
setnames(setDT(cox_resultado),
         c("exp(coef)", "exp(-coef)", "lower .95", "upper .95"),
         c("HR", "1_HR", "IC_lower", "IC_upper"))
cox_resultado[, .(variavel, HR = round(HR, 3),
                  IC_lower = round(IC_lower, 3),
                  IC_upper = round(IC_upper, 3))]
      variavel    HR IC_lower IC_upper
        <char> <num>    <num>    <num>
1:         age 1.011    0.989    1.034
2: sexFeminino 0.572    0.387    0.844
3:     ph.ecog 2.284    1.498    3.481
4:    ph.karno 1.020    0.998    1.043
5:     wt.loss 0.988    0.974    1.003
Código
library(ggplot2)

cox_coef <- summary(cox_modelo)$conf.int
df_forest <- data.frame(
  variavel  = c("Idade", "Sexo\n(Feminino vs. Masc.)",
                "ECOG (por ponto)", "Karnofsky\n(médico)",
                "Perda de peso"),
  HR        = cox_coef[, "exp(coef)"],
  lower     = cox_coef[, "lower .95"],
  upper     = cox_coef[, "upper .95"],
  sig       = ifelse(cox_coef[, "lower .95"] > 1 | cox_coef[, "upper .95"] < 1,
                     "Significativo", "Não significativo")
)

ggplot(df_forest, aes(x = HR, y = reorder(variavel, HR), color = sig)) +
  geom_vline(xintercept = 1, linetype = "dashed", color = "gray50", linewidth = 0.9) +
  geom_errorbarh(aes(xmin = lower, xmax = upper), height = 0.25, linewidth = 0.9) +
  geom_point(size = 4) +
  scale_color_manual(
    values = c("Significativo" = "#c0392b", "Não significativo" = "#7f8c8d"),
    name   = NULL
  ) +
  scale_x_log10(
    breaks = c(0.3, 0.5, 0.75, 1, 1.5, 2, 3),
    labels = c("0.30","0.50","0.75","1.0","1.5","2.0","3.0")
  ) +
  labs(
    x        = "Hazard Ratio (IC 95%) — escala logarítmica",
    y        = NULL,
    title    = "Forest plot — Modelo de Cox",
    subtitle = "HR > 1: maior risco | HR < 1: fator protetor | Linha = HR de referência (1.0)"
  ) +
  theme_minimal(base_size = 12) +
  theme(
    legend.position   = "top",
    panel.grid.minor  = element_blank(),
    panel.grid.major.y = element_blank()
  )
Figura 7: Forest plot dos Hazard Ratios estimados pelo modelo de Cox. Cada ponto representa o HR; as barras horizontais representam o IC 95%. Pontos à direita da linha vertical (HR = 1) indicam maior risco; pontos à esquerda indicam fator protetor. A escala logarítmica no eixo horizontal é necessária para que distâncias simétricas acima e abaixo de 1 tenham interpretação simétrica (HR = 2 está tão distante de 1 quanto HR = 0.5).

6.5 O pressuposto de riscos proporcionais

O modelo de Cox não exige que o risco de evento seja constante ao longo do tempo. Pelo contrário: o risco basal pode aumentar, diminuir ou assumir formatos complexos durante o seguimento.

O pressuposto central é outro: o modelo assume que o Hazard Ratio (HR) associado a uma covariável permanece constante ao longo do tempo. Essa condição é chamada de pressuposto de riscos proporcionais.

Em outras palavras, o efeito relativo de uma variável sobre o risco instantâneo do evento é assumido como estável durante o acompanhamento.

DicaO que proporcional significa na prática

Suponha que o HR para DECH grau IV, em comparação com DECH grau I, seja 2.

Isso significa que, em qualquer tempo de seguimento, pacientes com DECH grau IV têm o dobro do risco instantâneo de morrer em relação aos pacientes com DECH grau I, assumindo que as demais variáveis do modelo estejam fixas.

O risco absoluto pode mudar muito ao longo do tempo. Por exemplo, o risco de morte pode ser maior nos primeiros dias após o transplante e menor depois de alguns meses. O que o modelo assume é que a razão entre os grupos permanece igual.

Assim, se o risco basal muda, os riscos dos dois grupos mudam juntos, mantendo a mesma proporção.

Clinicamente, esse pressuposto nem sempre é plausível.

No contexto de TCTH, é razoável imaginar que o efeito de uma complicação grave, como DECH grau IV, seja muito intenso nas primeiras semanas após o diagnóstico e se atenue entre os sobreviventes que ultrapassam os primeiros meses. Nesse caso, o HR não seria constante no tempo, e o pressuposto de riscos proporcionais estaria violado.

Quando isso acontece, reportar um único HR médio para todo o seguimento pode ser enganoso. O modelo estaria resumindo em um único número um efeito que, na realidade, muda ao longo do tempo.

6.5.1 Testando o pressuposto

Uma forma clássica de avaliar o pressuposto de riscos proporcionais é usar os resíduos de Schoenfeld escalonados. A função cox.zph() testa se há evidência de que o efeito de cada covariável varia sistematicamente com o tempo.

Código
cox_ph_teste <- cox.zph(cox_modelo)
print(cox_ph_teste)  # Veja o p-valor global e por variável
           chisq df     p
age      0.03216  1 0.858
sex      1.04293  1 0.307
ph.ecog  3.24258  1 0.072
ph.karno 6.28319  1 0.012
wt.loss  0.00693  1 0.934
GLOBAL   8.46702  5 0.132

A interpretação geral é:

  • p < 0.05: há evidência de que o efeito da covariável pode variar ao longo do tempo; teste global significativo: sugere que ao menos um componente do modelo viola o pressuposto;
  • p ≥ 0.05: não há evidência clara contra o pressuposto de riscos proporcionais.

O p-valor não deve ser interpretado mecanicamente. Em amostras grandes, desvios pequenos podem se tornar estatisticamente significativos. Em amostras pequenas, violações clinicamente relevantes podem não ser detectadas. Por isso, o teste formal deve ser avaliado junto com os gráficos.

Código
cox_ph_teste <- cox.zph(cox_modelo)

n_graficos <- nrow(cox_ph_teste$table) - 1  # exclui GLOBAL
n_colunas  <- min(3, n_graficos)
n_linhas   <- ceiling(n_graficos / n_colunas)

par(mfrow = c(n_linhas, n_colunas), mar = c(4, 4, 2.5, 1))

plot(cox_ph_teste, hr = TRUE)

par(mfrow = c(1, 1))
Figura 8: Avaliação gráfica do pressuposto de riscos proporcionais usando resíduos de Schoenfeld escalonados. Linhas aproximadamente horizontais sugerem efeito estável no tempo. Tendências ascendentes ou descendentes sugerem que o efeito da covariável pode variar durante o seguimento.

Nos gráficos, a linha suavizada representa a tendência estimada do efeito da covariável ao longo do tempo. Se essa linha permanece aproximadamente horizontal, o pressuposto de proporcionalidade é mais plausível. Se a linha mostra uma tendência clara de subida ou descida, isso sugere que o HR muda com o tempo.

AvisoInterpretação clínica antes da decisão estatística

Uma violação do pressuposto de riscos proporcionais não é apenas um problema técnico. Ela pode indicar que a covariável tem efeitos diferentes em fases distintas da doença ou do tratamento.

Nesses casos, a pergunta correta não é apenas “como corrigir o modelo?”, mas também: por que esse efeito mudaria ao longo do tempo?

Quando o pressuposto é violado, algumas alternativas são:

  • Incluir interação da covariável com o tempo;
  • Estratificar o modelo por uma variável que viola o pressuposto;
  • Dividir o seguimento em períodos clinicamente relevantes;
  • Usar modelos alternativos, como modelos com efeitos dependentes do tempo ou abordagens paramétricas flexíveis.

7 Aprendizado de Máquina: Do Zero ao Conceito

Nota📌 O que você vai encontrar neste capítulo

Se você nunca ouviu falar de aprendizado de máquina antes, este capítulo é seu ponto de partida. Aqui você vai entender:

  • O que é aprendizado de máquina em linguagem simples
  • Os três tipos principais: supervisionado, não supervisionado, por reforço
  • Por que o aprendizado de máquina pode ser útil na análise de sobrevida
  • Como ele se relaciona com (e não substitui) o modelo de Cox

Ao final deste capítulo, você terá o vocabulário necessário para entender as seções de implementação que vêm a seguir.

7.1 O que é aprendizado de máquina?

Aprendizado de máquina (AM, ou machine learning em inglês) é um conjunto de algoritmos capazes de identificar padrões em dados e fazer previsões sem que as regras sejam programadas manualmente (HASTIE et al., 2009).

DicaAnalogia médica: diagnóstico por experiência

Um médico especialista em DECH, depois de acompanhar centenas de casos, aprende padrões: “pacientes jovens com DECH grau III que receberam doador não aparentado com incompatibilidade HLA tendem a ter pior prognóstico”. Ele não calculou isso com uma fórmula — aprendeu pela experiência.

O aprendizado de máquina faz algo parecido: “lê” muitos casos de pacientes com suas características e desfechos, e aprende padrões que permitem prever o que acontecerá com novos pacientes.

7.2 Os três tipos de aprendizado

7.2.1 Aprendizado supervisionado

Aprende a partir de exemplos onde a resposta já é conhecida. Cada exemplo tem:

  • Entrada (X): características do paciente (idade, tipo de doador, ECOG, etc.)
  • Saída (y): a resposta que queremos prever (sobreviveu ou não, tempo de sobrevida)

O algoritmo aprende uma função f(X) \approx y.

Subtipos:

  • Classificação: y é uma categoria (DECH sim/não, grau 1/2/3/4)
  • Regressão: y é um número contínuo (biomarcador, dias de internação)
  • Sobrevida supervisionada: y é o par (tempo, status) com censura ← foco desta apostila

7.2.2 Aprendizado não supervisionado

Aprende padrões sem rótulo de resposta. O algoritmo busca estrutura intrínseca nos dados.

Exemplos em saúde:

  • Agrupamento (clustering) de pacientes pós-TCTH em fenótipos clínicos similares
  • Redução de dimensionalidade de dados ômicos (expressão gênica, proteômica)
  • Identificação de grupos de risco homogêneos sem definir os grupos a priori

7.2.3 Aprendizado por reforço

Um agente aprende a tomar decisões sequenciais em um ambiente, recebendo recompensas por boas decisões e penalizações por más.

Exemplos emergentes em saúde:

  • Otimização de protocolos de dosagem adaptativa de imunossupressores pós-TCTH
  • Tratamento dinâmico adaptativo (quando intensificar ou reduzir imunossupressão?)
Código
library(ggplot2)

df_am <- data.frame(
  tipo = c("Supervisionado\n(foco desta apostila)",
           "Não supervisionado",
           "Por reforço"),
  x    = c(1, 2, 3),
  y    = c(1, 1, 1),
  ex   = c(
    "Prever tempo de sobrevida\ncom DECH aguda (Cox, RSF)",
    "Agrupar pacientes em\nfenótipos imunológicos",
    "Otimizar protocolo de\nimunossupressão adaptativa"
  ),
  cor  = c("#1a6faf", "#7f8c8d", "#d95f02")
)

ggplot(df_am, aes(x, y)) +
  geom_tile(aes(fill = cor), width = 0.88, height = 0.7, alpha = 0.9) +
  geom_text(aes(label = tipo), fontface = "bold", size = 4,
            color = "white", vjust = -1.0) +
  geom_text(aes(label = ex), size = 3, color = "white", vjust = 1.5,
            lineheight = 1.2) +
  scale_fill_identity() +
  coord_cartesian(ylim = c(0.5, 1.5)) +
  labs(title = "Paradigmas de Aprendizado de Máquina",
       subtitle = "Com exemplos no contexto de TCTH e DECH",
       x = NULL, y = NULL) +
  theme_void(base_size = 12) +
  theme(plot.title    = element_text(hjust = 0.5, face = "bold", size = 14),
        plot.subtitle = element_text(hjust = 0.5, size = 10, color = "gray40"))
Figura 9: Os três paradigmas de aprendizado de máquina, com exemplos de aplicação no contexto de TCTH e DECH. Esta apostila foca no aprendizado supervisionado para sobrevida, destacado em azul.

7.3 Por que usar AM na análise de sobrevida?

O modelo de Cox é uma das ferramentas mais importantes da análise de sobrevida. Ele é muito usado porque produz resultados relativamente fáceis de interpretar, como o Hazard Ratio (HR). Isso permite responder perguntas do tipo: “pacientes com determinada característica têm risco maior ou menor de apresentar o evento?”.

No entanto, o modelo de Cox trabalha com algumas simplificações. Entre elas, assume que:

  • O efeito das variáveis é combinado de forma relativamente simples na escala do log-risco;
  • Interações entre variáveis precisam ser especificadas pelo pesquisador;
  • Variáveis contínuas, como idade ou valores laboratoriais, frequentemente são tratadas como se tivessem efeito linear, a menos que o modelo seja ajustado de outra forma.

Essas suposições podem ser limitantes em populações clínicas complexas, como crianças submetidas ao transplante de células-tronco hematopoéticas (TCTH).

Por exemplo, a relação entre idade e risco pode não ser uma linha reta. Bebês, crianças maiores e adolescentes podem ter perfis clínicos muito diferentes. Do mesmo modo, o efeito do tipo de doador pode depender do grau de compatibilidade HLA, da doença de base, da intensidade do condicionamento ou da presença de complicações precoces.

Em situações assim, pode ser difícil antecipar manualmente todas as combinações relevantes entre variáveis. É nesse ponto que os métodos de aprendizado de máquina (AM) podem ser úteis.

Modelos de AM para sobrevida são métodos construídos para estimar o tempo até um evento levando em conta censura. Eles podem aprender relações não lineares e interações complexas entre muitas variáveis simultaneamente. Em vez de exigir que o pesquisador especifique previamente todas as formas possíveis de associação, esses modelos procuram padrões nos dados que ajudam a prever o risco futuro.

Isso é especialmente relevante quando há muitas informações disponíveis ao mesmo tempo, como:

  • Dados clínicos;
  • Exames laboratoriais;
  • Características do transplante;
  • Tipo de doador;
  • Compatibilidade HLA;
  • Complicações pós-transplante;
  • Tratamentos recebidos;
  • Informações longitudinais do acompanhamento.
NotaAM não substitui o Cox — eles se complementam

O modelo de Cox é muito útil quando queremos interpretar associações específicas e testar hipóteses clínicas.

Os modelos de aprendizado de máquina são especialmente úteis quando o objetivo principal é predizer o risco de um novo paciente, combinando muitas informações ao mesmo tempo.

Uma forma simples de pensar é:

  • Cox ajuda a responder: quais fatores estão associados ao risco?
  • AM para sobrevida ajuda a responder: qual é o risco previsto para este paciente, considerando seu conjunto completo de características?

Essas abordagens não competem necessariamente entre si. Elas podem ser complementares.

Em pesquisa clínica, o modelo de Cox continua sendo muito adequado quando há hipóteses bem definidas, poucas variáveis principais e interesse em interpretação. Já os métodos de AM podem ser úteis para desenvolver modelos de predição, sistemas de estratificação de risco e ferramentas de apoio à decisão clínica.

Nota📌 Tabela comparativa: Cox vs. Aprendizado de Máquina para Sobrevida
Critério Cox Clássico AM para Sobrevida (ex: RSF)
Interpretabilidade Alta — HR com IC 95% Baixa a moderada (requer SHAP)
Flexibilidade Menor — relações lineares no log-risco Alta — captura não linearidades e interações
Hipótese de proporcionalidade Necessária (testável) Não necessária
Número de variáveis Funciona bem com poucas variáveis Funciona bem com muitas variáveis
Objetivo típico Inferência — “qual fator importa?” Predição — “qual o risco deste paciente?”
Publicação em periódicos clínicos Muito estabelecido Crescente, mas requer justificativa
Pacote R principal survival ranger via mlr3proba

8 Overfitting, Generalização e Validação

Nota📌 O que você vai encontrar neste capítulo

Este capítulo trata de um dos problemas mais importantes em aprendizado de máquina: construir um modelo que funcione em novos pacientes, não apenas nos dados que já temos. Você vai entender:

  • O que é overfitting (sobreajuste) e por que ele é um problema sério na prática clínica
  • O dilema entre modelos muito simples (subajuste) e muito complexos (sobreajuste)
  • Estratégias práticas para avaliar e controlar o overfitting
  • Como a validação cruzada funciona e por que é preferível a uma divisão única

Este capítulo é essencial para quem quer publicar ou aplicar modelos preditivos em saúde.

8.1 O problema do “decoro” estatístico

Overfitting (sobreajuste) ocorre quando um modelo aprende os dados de treinamento tão bem — incluindo os ruídos aleatórios — que funciona mal em novos dados.

DicaAnalogia: o estudante que decorou as respostas

Imagine um estudante que, em vez de aprender os princípios da imunologia do transplante, decorou as respostas exatas das 200 questões da prova anterior. Na nova prova, com questões diferentes (mas sobre o mesmo conteúdo), ele vai mal — porque decorou em vez de aprender o padrão geral.

O overfitting faz exatamente isso com modelos estatísticos: o modelo “decora” os dados de treino mas não generaliza para novos pacientes.

8.2 O dilema viés-variância

Todo modelo enfrenta um compromisso fundamental:

\text{Erro total} = \underbrace{\text{Viés}^2}_{\text{subajuste}} + \underbrace{\text{Variância}}_{\text{sobreajuste}} + \underbrace{\varepsilon}_{\text{irredutível}}

  • Viés alto (subajuste): modelo muito simples para capturar a realidade. Ex.: Cox linear quando a relação é altamente não linear.
  • Variância alta (sobreajuste): modelo muito complexo, muda muito de um conjunto de dados para outro. Ex.: árvore de decisão infinitamente profunda.
  • Ruído irredutível (\varepsilon): variabilidade inerente aos dados que nenhum modelo pode eliminar.
Código
comp <- seq(1, 20, length.out = 200)
bias2    <- 4 / comp
variance <- 0.08 * comp^1.6
ruido    <- rep(0.4, length(comp))
total_test  <- bias2 + variance + ruido
total_train <- pmax(0.15, 4 / comp^1.3 - 0.2)

df_bv <- data.frame(
  x = rep(comp, 4),
  y = c(total_train, bias2, variance, total_test),
  curva = rep(c("Erro de treino", "Viés²", "Variância", "Erro de teste (generalização)"),
              each = length(comp))
)

cores_bv <- c(
  "Erro de treino"              = "#1a6faf",
  "Viés²"                       = "#d95f02",
  "Variância"                   = "#9467bd",
  "Erro de teste (generalização)" = "#2ca02c"
)

lts_bv <- c(
  "Erro de treino"              = "dashed",
  "Viés²"                       = "dotted",
  "Variância"                   = "dotted",
  "Erro de teste (generalização)" = "solid"
)

idx_min <- which.min(total_test)

ggplot(df_bv, aes(x, y, color = curva, linetype = curva)) +
  geom_line(linewidth = 1.1) +
  geom_vline(xintercept = comp[idx_min], linetype = "dashed",
             color = "gray30", linewidth = 0.8) +
  annotate("text", x = comp[idx_min] + 0.5, y = 3.5,
           label = "Ponto ótimo", hjust = 0, size = 3.5, color = "gray30") +
  annotate("text", x = 3, y = 3.8, label = "Subajuste\n(viés alto)",
           hjust = 0.5, size = 3.5, color = "gray50") +
  annotate("text", x = 17, y = 3.8, label = "Sobreajuste\n(variância alta)",
           hjust = 0.5, size = 3.5, color = "gray50") +
  scale_color_manual(values = cores_bv, name = NULL) +
  scale_linetype_manual(values = lts_bv, name = NULL) +
  labs(
    x        = "Complexidade do modelo →",
    y        = "Erro",
    title    = "Compromisso Viés–Variância",
    subtitle = "Objetivo: minimizar o erro de teste (generalização para novos pacientes)"
  ) +
  theme_minimal(base_size = 12) +
  theme(legend.position = "top",
        axis.text.x = element_blank(),
        panel.grid.minor = element_blank())
Figura 10: Compromisso viés-variância. O erro de treinamento (azul) cai monotonicamente com o aumento da complexidade, mas o erro de teste (verde) tem um mínimo — o ponto ótimo de generalização. Modelos à esquerda do mínimo sofrem de subajuste (viés alto); modelos à direita sofrem de sobreajuste (variância alta). O objetivo é encontrar o ponto ótimo, normalmente via validação cruzada.

8.3 Estratégias para evitar overfitting

8.3.1 1. Separação treino-teste

Divide os dados em duas partes:

  • Conjunto de treino (70–80%): usado para ajustar o modelo
  • Conjunto de teste (20–30%): usado apenas para avaliação final

Regra de ouro: o conjunto de teste só é “olhado” uma vez, no final. Se você usa o teste para tomar decisões durante o processo de modelagem, ele contamina a avaliação.

AvisoCuidado com amostras pequenas

Em estudos de TCTH pediátrico com menos de 100 pacientes, uma única divisão 70/30 pode ser muito instável — diferentes divisões aleatórias produzem resultados muito diferentes. Nesses casos, prefira validação cruzada.

8.3.2 2. Validação cruzada k-fold

Divide os dados em k partes iguais (“folds”). Em cada iteração:

  • k-1 folds são usados para treino
  • 1 fold é usado para teste
  • Repete-se k vezes, cada fold servindo como teste uma vez

O desempenho final é a média dos k avaliações.

Nota📌 Por que 5-fold ou 10-fold? Qual escolher?

A escolha do número de folds envolve um compromisso:

  • Poucos folds (ex: 3-fold): cada fold de teste tem mais dados, mas o conjunto de treino é menor. Útil quando a amostra é muito pequena.
  • k = 5: boa escolha geral — amplamente usada em publicações. Balanceia viés e variância da estimativa de desempenho.
  • k = 10: mais comum em literatura de AM. Cada fold de teste tem 10% dos dados; o treino usa 90%. Resultados mais estáveis mas computacionalmente mais caro.
  • Leave-One-Out (k = n): cada paciente é testado individualmente. Excelente para amostras muito pequenas (n < 50), mas computacionalmente intensivo.

Recomendação para estudos de TCTH pediátrico: com n < 100, prefira CV-5 ou CV-10 e reporte os resultados com desvio padrão entre os folds para mostrar a estabilidade da estimativa.

Código
library(ggplot2)

folds_df <- data.frame(
  fold  = rep(1:5, each = 5),
  parte = rep(1:5, 5),
  papel = c(
    "Teste","Treino","Treino","Treino","Treino",
    "Treino","Teste","Treino","Treino","Treino",
    "Treino","Treino","Teste","Treino","Treino",
    "Treino","Treino","Treino","Teste","Treino",
    "Treino","Treino","Treino","Treino","Teste"
  )
)

ggplot(folds_df, aes(x = parte, y = fold, fill = papel)) +
  geom_tile(color = "white", linewidth = 1.8) +
  geom_text(aes(label = papel), size = 3.5, fontface = "bold",
            color = "white") +
  scale_fill_manual(
    values = c("Treino" = "#1a6faf", "Teste" = "#d95f02"),
    name   = NULL
  ) +
  scale_x_continuous(
    breaks = 1:5,
    labels = paste("Parte", 1:5),
    position = "top"
  ) +
  scale_y_continuous(
    breaks = 1:5,
    labels = paste("Iteração", 1:5),
    trans  = "reverse"
  ) +
  labs(
    title    = "Validação Cruzada 5-fold",
    subtitle = "Em cada iteração, uma parte diferente serve como teste",
    x = NULL, y = NULL
  ) +
  theme_minimal(base_size = 12) +
  theme(legend.position = "top",
        panel.grid = element_blank())

Esquema visual da validação cruzada 5-fold. Os dados são divididos em 5 partes iguais. Em cada uma das 5 iterações, uma parte diferente serve como conjunto de teste (laranja) e as demais como treino (azul). O desempenho final é a média das 5 avaliações. Este esquema é preferível à divisão única treino-teste quando o tamanho amostral é moderado.

8.3.3 3. Regularização

Técnica que adiciona uma penalidade ao modelo para reduzir sua complexidade:

Método O que faz Quando usar
Ridge (L2) Encolhe todos os coeficientes em direção a zero Muitas variáveis correlacionadas
LASSO (L1) Zera alguns coeficientes (seleção automática) Muitas variáveis, algumas irrelevantes
Elastic Net Combinação de Ridge e LASSO Situação intermediária

No contexto de TCTH, se você tem 50 variáveis laboratoriais e clínicas, LASSO pode selecionar automaticamente as mais relevantes, enquanto Ridge mantém todas mas com efeitos reduzidos.

8.3.4 4. Hiperparâmetros controlando a complexidade

Para Random Survival Forests (veremos a seguir), a complexidade é controlada por:

  • num.trees: mais árvores → mais estável, mas mais lento
  • min.node.size: folhas maiores → modelo mais simples (menos overfitting)
  • mtry: quantas variáveis testar em cada divisão → controla diversidade
  • max.depth: profundidade máxima da árvore → controla complexidade

9 Algoritmos de AM para Sobrevida

Nota📌 O que você vai encontrar neste capítulo

Neste capítulo, exploramos os principais algoritmos de aprendizado de máquina adaptados para dados de sobrevida. Os três mais usados na literatura médica são:

  • Random Survival Forest (RSF): o mais popular, baseado em florestas aleatórias
  • Gradient Boosting para Sobrevida: poderoso para muitas variáveis
  • Cox Penalizado (LASSO/Ridge): extensão interpretável do Cox clássico

Para cada um, apresentamos a ideia intuitiva, as vantagens, as limitações e quando usar.

9.1 Visão geral

Vários algoritmos foram adaptados para lidar com dados de sobrevida censurados. Cada um tem pontos fortes e fracos:

Código
library(ggplot2)
library(ggrepel)

df_mapa <- data.frame(
  nome  = c("KM / Log-rank", "Cox clássico",
            "Cox penalizado\n(LASSO/Ridge)", "Cox com\nsplines",
            "Random Survival\nForest", "CoxBoost /\nXGBoost Cox",
            "Redes neurais\nde sobrevida"),
  flex  = c(1, 2, 3, 3.5, 4.5, 4.2, 5),
  interp= c(5, 5, 4.2, 4, 2.5, 2, 1.5),
  cat   = c("Não paramétrico", "Semiparamétrico", "Penalizado",
            "Semiparamétrico", "Ensemble (floresta)", "Ensemble (boosting)",
            "Deep Learning")
)

cores_mapa <- c(
  "Não paramétrico"     = "#2980b9",
  "Semiparamétrico"     = "#1a6faf",
  "Penalizado"          = "#2ca02c",
  "Ensemble (floresta)" = "#d95f02",
  "Ensemble (boosting)" = "#e67e22",
  "Deep Learning"       = "#9467bd"
)

ggplot(df_mapa, aes(flex, interp, color = cat, label = nome)) +
  geom_point(size = 5) +
  geom_text_repel(size = 3.4, show.legend = FALSE,
                   box.padding = 0.6, max.overlaps = 20,
                   segment.color = "gray60") +
  scale_color_manual(values = cores_mapa, name = "Família") +
  scale_x_continuous(limits = c(0.5, 5.8),
                     breaks = c(1, 3, 5),
                     labels = c("Baixa", "Média", "Alta")) +
  scale_y_continuous(limits = c(1, 5.8),
                     breaks = c(1, 3, 5),
                     labels = c("Baixa", "Média", "Alta")) +
  labs(
    x        = "Flexibilidade →",
    y        = "← Interpretabilidade",
    title    = "Algoritmos de Análise de Sobrevida",
    subtitle = "Posicionados pelo eixo flexibilidade × interpretabilidade"
  ) +
  theme_minimal(base_size = 12) +
  theme(legend.position = "right",
        legend.text = element_text(size = 9))
Figura 11: Mapa dos principais algoritmos para análise de sobrevida, posicionados pelos eixos interpretabilidade vs. flexibilidade. Algoritmos no canto superior-esquerdo (ex.: Cox clássico) são altamente interpretáveis mas assumem formas funcionais rígidas. Algoritmos no canto inferior-direito (ex.: redes neurais de sobrevida) são altamente flexíveis mas difíceis de interpretar. O ideal depende do objetivo: interpretação vs. predição.

9.2 Random Survival Forest (RSF)

O RSF (ISHWARAN et al., 2008) é o algoritmo de AM para sobrevida mais amplamente usado na literatura médica. Ele adapta o Random Forest clássico para dados censurados.

9.2.1 Como uma árvore de decisão de sobrevida funciona

DicaAnalogia: triagem de risco em UTI

Imagine que uma enfermeira experiente triagem pacientes pós-TCTH com DECH em grupos de risco. Ela primeiro pergunta: “DECH grau > II?” — se sim, vai para o grupo de alto risco; se não, continua triagem. Depois: “Doador não aparentado?” etc.

Uma árvore de decisão de sobrevida faz exatamente isso: encontra automaticamente as perguntas (divisões) que melhor separam pacientes em grupos com diferentes curvas de sobrevivência.

Nota📌 Como visualizar uma árvore de decisão de sobrevida

Abaixo, um esquema simplificado de uma árvore de decisão de sobrevida para DECH pós-TCTH:

                     Todos os pacientes
                     (n=100, S(365)=0.60)
                            |
                    DECH grau ≤ II?
                   /                \
                 SIM                NÃO
         (n=60, S=0.75)       (n=40, S=0.38)
               |                     |
         Doador aparen.?       Idade ≤ 8 anos?
         /           \           /         \
        SIM          NÃO       SIM          NÃO
    S(365)=0.82  S(365)=0.65  S(365)=0.45  S(365)=0.28

Cada “folha” da árvore produz uma curva de sobrevivência para o subgrupo de pacientes que chegou até ali. O RSF constrói centenas dessas árvores e combina as previsões de todas elas.

O RSF constrói centenas de árvores (tipicamente 500–1000), cada uma:

  1. Usando uma amostra aleatória com reposição dos dados (bootstrap);
  2. Em cada divisão, testando apenas um subconjunto aleatório das variáveis disponíveis;
  3. Escolhendo a divisão que maximiza a diferença de log-rank entre os grupos filhos.

A previsão final é a média das curvas de Nelson-Aalen de todas as árvores — o que torna o RSF mais estável e menos propenso a overfitting do que uma única árvore.

9.2.2 Vantagens e limitações

Vantagens:

  • Captura interações e não linearidades automaticamente
  • Robusto a variáveis irrelevantes (ignora-as naturalmente)
  • Fornece importância de variáveis por permutação
  • Funciona bem com muitas variáveis preditoras

Limitações:

  • Não produz um “HR” interpretável diretamente — é uma “caixa preta”
  • Pode ter calibração inferior ao Cox em amostras pequenas
  • Requer ajuste de hiperparâmetros para melhor desempenho
  • Mais lento computacionalmente
NotaRSF no contexto de TCTH pediátrico

Com 50–100 variáveis clínicas, laboratoriais e imunogenéticas disponíveis, o RSF pode descobrir automaticamente que, por exemplo:

  • Em pacientes com DECH grau III-IV E HLA incompatibilidade em 2 loci, o efeito de uma covariável imunológica específica é muito maior do que em outros grupos
  • O nível de certos biomarcadores tem efeito fortemente não linear no risco

Essas interações complexas são difíceis de especificar manualmente no Cox.

9.3 Gradient Boosting para Sobrevida

O boosting constrói um ensemble de modelos fracos em sequência: cada novo modelo corrige os erros do anterior.

Para sobrevida, a função de perda derivada da verossimilhança parcial de Cox é minimizada iterativamente. Os pacotes xgboost (com objective = "survival:cox") e gbm implementam essa abordagem.

Vantagem chave sobre RSF: geralmente superior em datasets de alta dimensionalidade (ex.: dados genômicos com milhares de variáveis).

Desvantagem: maior risco de overfitting se o número de iterações não for bem calibrado. Requer ajuste cuidadoso de hiperparâmetros como learning_rate, max_depth e n.minobsinnode.

9.4 Cox Penalizado

Extensão direta do modelo de Cox com penalidade adicionada à verossimilhança parcial, implementada no R pelo pacote glmnet:

\ell_{penalizada}(\boldsymbol{\beta}) = \ell_{Cox}(\boldsymbol{\beta}) - \lambda \left[\alpha \|\boldsymbol{\beta}\|_1 + (1-\alpha) \|\boldsymbol{\beta}\|_2^2 \right]

O parâmetro \lambda (intensidade da penalidade) é escolhido por validação cruzada; \alpha = 1 é LASSO puro e \alpha = 0 é Ridge puro.

Caso de uso típico no TCTH: você tem 80 variáveis disponíveis (clínicas + laboratoriais + HLA + ômicas) e quer um modelo interpretável com seleção automática das mais relevantes. O Cox LASSO fará essa seleção.


10 Métricas de Avaliação em Sobrevida

Nota📌 O que você vai encontrar neste capítulo

Como saber se um modelo preditivo de sobrevida é bom? Este capítulo apresenta as principais métricas para avaliar modelos de sobrevida. Você vai aprender:

  • Por que as métricas convencionais (acurácia, AUC) são insuficientes para sobrevida
  • O C-index: como o modelo ordena pacientes por risco
  • O Brier Score e IBS: se as probabilidades previstas são corretas
  • Como comparar modelos usando uma linha de base (KM sem covariáveis)

Ao final, você saberá interpretar a tabela de resultados que aparece em artigos de modelos preditivos de sobrevida.

10.1 Por que acurácia não serve?

Em classificação binária, usamos acurácia: porcentagem de acertos. Em sobrevida, isso não funciona diretamente porque:

  1. Muitas observações são censuradas — não sabemos o “acerto verdadeiro”
  2. A resposta não é apenas “vai morrer ou não” — é também quando
  3. Modelos de sobrevida fazem dois tipos de previsão: ordenação de risco e probabilidades ao longo do tempo — que requerem métricas diferentes

10.2 C-index: quem tem mais risco?

O C-index (Índice de Concordância) mede a capacidade do modelo de ordenar corretamente os pacientes por risco (UNO et al., 2011).

“Se dois pacientes são comparáveis (podemos dizer quem deve ter maior risco), o modelo colocou o paciente de maior risco com escore mais alto?”

C = P(\hat{r}_i > \hat{r}_j \mid T_i < T_j,\ \delta_i = 1)

Onde \hat{r} é o escore de risco previsto pelo modelo.

DicaAnalogia: prova de corrida

Imagine que você tem um modelo para prever quem chega mais rápido numa maratona. Para cada par de corredores (A, B), se A chegou antes, o modelo deveria ter dado a A um escore maior. O C-index é a porcentagem de pares onde o modelo acertou a ordem.

Tabela 3: Interpretação do C-index
C-index Interpretação
0.50 Aleatório — como jogar uma moeda
0.60–0.69 Discriminação fraca
0.70–0.79 Discriminação moderada
0.80–0.89 Discriminação boa
≥ 0.90 Discriminação excelente (raro em dados reais)
ImportanteLimitação do C-index

O C-index avalia ordenação — não avalia se as probabilidades são corretas. Um modelo pode ter C-index = 0.72 mas prever probabilidade de 90% de sobrevida para pacientes que na realidade têm 50%. Isso seria um modelo discriminativo mas mal calibrado.

10.3 Brier Score e Integrated Brier Score: probabilidades corretas?

O Brier Score em um tempo t^* mede o erro quadrático médio das probabilidades de sobrevivência previstas (GRAF et al., 1999). Para um paciente i:

BS(t^*) = \frac{1}{n} \sum_{i=1}^n w_i \left[\mathbf{1}(T_i > t^*) - \hat{S}(t^* \mid \mathbf{x}_i)\right]^2

Os pesos w_i são a ponderação inversa pela probabilidade de censura (IPCW — Inverse Probability of Censoring Weighting), que corrige o viés introduzido pelas observações censuradas.

O Integrated Brier Score (IBS) agrega o Brier Score ao longo do tempo:

IBS = \frac{1}{\tau} \int_0^\tau BS(t)\, dt

DicaAnalogia: previsão do tempo

Imagine um meteorologista que prevê 80% de chuva para cada dia da semana. Se na realidade choveu 80% dos dias, a previsão está bem calibrada — o IBS seria baixo. Se choveu apenas 30% dos dias, a previsão superestimou o risco — o IBS seria alto.

Da mesma forma, um bom modelo de sobrevida deveria prever 80% de chance de sobrevida para grupos que realmente sobrevivem 80%.

Tabela 4: Métricas para avaliação de modelos de sobrevida
Métrica O que mede Direção ótima Tipo de predição
C-index Ordenação de risco Maior = melhor crank
Brier Score em t^* Erro em um tempo específico Menor = melhor distr
IBS Erro médio ao longo do tempo Menor = melhor distr

10.4 Linha de base: o modelo de referência

Para interpretar os valores de IBS, compare com a linha de base KM: o modelo que prevê \hat{S}(t) = \hat{S}_{KM}(t) para todos os pacientes, sem usar nenhuma covariável. Se o seu modelo sofisticado não bate a linha KM, algo está errado.

Código
library(ggplot2)

tempos_bs <- c(30, 90, 180, 365, 540, 730)
brier_cox <- c(0.12, 0.17, 0.21, 0.22, 0.20, 0.19)
brier_rsf <- c(0.11, 0.15, 0.18, 0.19, 0.18, 0.17)
brier_km  <- c(0.13, 0.19, 0.24, 0.26, 0.24, 0.22)

df_brier <- rbind(
  data.frame(t = tempos_bs, BS = brier_cox, modelo = "Cox"),
  data.frame(t = tempos_bs, BS = brier_rsf, modelo = "RSF"),
  data.frame(t = tempos_bs, BS = brier_km,  modelo = "KM (linha base)")
)

ggplot(df_brier, aes(t, BS, color = modelo, group = modelo)) +
  geom_line(linewidth = 1.1) +
  geom_point(size = 3) +
  scale_color_manual(
    values = c("Cox" = "#1a6faf", "RSF" = "#d95f02", "KM (linha base)" = "#7f8c8d"),
    name = NULL
  ) +
  scale_x_continuous(breaks = tempos_bs) +
  labs(
    x        = "Tempo (dias)",
    y        = "Brier Score",
    title    = "Brier Score ao longo do tempo: Cox vs. RSF vs. linha KM",
    subtitle = "Valores menores = menor erro nas probabilidades previstas | Comparar sempre com a linha base KM"
  ) +
  theme_minimal(base_size = 12) +
  theme(legend.position = "top")
Figura 12: Ilustração do Brier Score em diferentes tempos. A distância entre a probabilidade prevista pelo modelo (linha azul) e o estado real do paciente (0 ou 1, círculos cinzas) é a base do cálculo. A soma dessas distâncias ao quadrado, ponderada pelo peso de censura, dá o Brier Score naquele tempo. O IBS integra essa curva de Brier Scores ao longo do tempo.

11 Implementação Completa com mlr3proba

Nota📌 O que você vai encontrar neste capítulo

Este é o capítulo mais prático da apostila. Vamos implementar do zero uma análise completa de sobrevida com aprendizado de máquina no R, seguindo um fluxo passo a passo:

  1. Instalação e carregamento dos pacotes
  2. Preparação e diagnóstico dos dados
  3. Criação da TaskSurv (a “tarefa” de sobrevida no mlr3)
  4. Divisão treino-teste
  5. Definição dos modelos (Cox e RSF)
  6. Treinamento
  7. Avaliação das métricas
  8. Validação cruzada
  9. Ajuste de hiperparâmetros

Cada passo tem código comentado linha a linha. Recomendamos rodar o código no seu próprio R à medida que avança.

Dica💡 Dica antes de começar: organize seu projeto

Antes de rodar qualquer código, crie uma estrutura de pastas para o projeto:

meu_projeto_sobrevida/
├── dados/           # seus dados originais (nunca modifique)
├── scripts/         # seus arquivos .R ou .qmd
├── resultados/      # tabelas e gráficos exportados
└── referencias/     # artigos e documentação

Isso facilita a reprodutibilidade e evita confusões entre diferentes versões dos arquivos.

11.1 O ecossistema mlr3

O mlr3 é um framework de aprendizado de máquina para R que fornece uma interface unificada para treinar, avaliar e comparar modelos. O mlr3proba estende esse framework para análise de sobrevida.

Código
library(ggplot2)

df_eco <- data.frame(
  componente = c("Dados\n(lung, SIM, etc.)", "TaskSurv\n(mlr3proba)",
                 "Learner\n(Cox, RSF, ...)", "Predição\n(crank, distr)",
                 "Métrica\n(C-index, IBS)"),
  x = 1:5, y = 1,
  tipo = c("dados", "task", "learner", "pred", "metrica")
)

cores_eco <- c(
  dados   = "#bdc3c7",
  task    = "#2980b9",
  learner = "#1a6faf",
  pred    = "#d95f02",
  metrica = "#2ca02c"
)

ggplot(df_eco, aes(x, y)) +
  geom_tile(aes(fill = tipo), width = 0.82, height = 0.45) +
  geom_text(aes(label = componente), color = "white", fontface = "bold",
            size = 3.2, lineheight = 1.2) +
  geom_segment(
    data = data.frame(x = 1:4 + 0.42, xend = 2:5 - 0.42, y = 1, yend = 1),
    aes(x = x, xend = xend, y = y, yend = yend),
    arrow = arrow(length = unit(0.15, "inches"), type = "closed"),
    color = "gray50", linewidth = 0.8
  ) +
  scale_fill_manual(values = cores_eco) +
  coord_cartesian(ylim = c(0.6, 1.4)) +
  labs(
    title    = "Fluxo do mlr3 para Análise de Sobrevida",
    subtitle = "mlr3proba + mlr3extralearners + mlr3tuning",
    x = NULL, y = NULL
  ) +
  theme_void(base_size = 11) +
  theme(
    legend.position = "none",
    plot.title      = element_text(hjust = 0.5, face = "bold", size = 13),
    plot.subtitle   = element_text(hjust = 0.5, color = "gray40", size = 10)
  )
Figura 13: Ecossistema mlr3 para análise de sobrevida. O fluxo central vai de Dados → Task → Learner → Predição → Avaliação. O mlr3proba fornece a Task de sobrevida (TaskSurv), os Learners específicos (Cox, RSF) e as métricas (C-index, IBS). O mlr3tuning adiciona o ajuste automático de hiperparâmetros.

11.2 Passo 1: Instalação e carregamento

# Instala pacotes base do mlr3 e dependências para sobrevida
install.packages(
  c("mlr3", "mlr3verse", "mlr3extralearners", "mlr3tuning",
    "survival", "data.table", "ggplot2", "ranger"),
  repos = c("https://mlr-org.r-universe.dev", "https://cloud.r-project.org")
)

# mlr3proba pode precisar do repositório especial do mlr-org
install.packages(
  "mlr3proba",
  repos = c("https://mlr-org.r-universe.dev", "https://cloud.r-project.org")
)
library(data.table)
library(survival)
library(mlr3)
library(mlr3proba)
library(mlr3extralearners)
library(ggplot2)

# Reprodutibilidade: fixar semente aleatória
set.seed(2024)

11.3 Passo 2: Preparação dos dados

# Carrega a base lung
data("lung", package = "survival")
lung_dt <- as.data.table(lung)

# PASSO CRÍTICO: recodificar status para o padrão mlr3proba
# Original: 1 = censurado, 2 = morte
# mlr3proba: 0 = censurado, 1 = evento
lung_dt[, status := as.integer(status == 2)]

# Converte sexo para fator legível
lung_dt[, sex := factor(sex, levels = c(1, 2),
                        labels = c("masculino", "feminino"))]

# Seleciona variáveis e remove casos incompletos
vars <- c("time", "status", "age", "sex", "ph.ecog",
          "ph.karno", "pat.karno", "meal.cal", "wt.loss")
lung_modelo <- na.omit(lung_dt[, ..vars])

# Diagnóstico rápido
lung_modelo[, .(
  n_total      = .N,
  n_eventos    = sum(status == 1),
  n_censurados = sum(status == 0),
  pct_censura  = paste0(round(100 * mean(status == 0), 1), "%"),
  tempo_min    = min(time),
  tempo_mediano= median(time),
  tempo_max    = max(time)
)]
   n_total n_eventos n_censurados pct_censura tempo_min tempo_mediano tempo_max
     <int>     <int>        <int>      <char>     <num>         <num>     <num>
1:     168       121           47         28%         5         268.5      1022
AvisoVerifique sempre antes de prosseguir

Antes de criar a Task, confirme:

  1. status usa a convenção 0/1 (não 1/2)
  2. time contém apenas valores positivos
  3. Ausências (NAs) foram removidas ou imputadas
  4. A proporção de censura foi calculada e reportada?
  5. O mecanismo de censura foi discutido (informativa vs. não informativa)?
  6. Valores ausentes foram tratados de forma explícita?

11.4 Passo 3: Criando a TaskSurv

Nota📌 O que é uma ‘Task’ no mlr3?

No vocabulário do mlr3, uma Task é o objeto que encapsula o problema a ser resolvido. Ela combina:

  • Os dados (o banco de pacientes)
  • A variável resposta (no nosso caso, time e status)
  • O tipo de problema (sobrevida, neste caso)
  • Os preditores disponíveis

Pensar em “Task” é útil porque permite trocar o modelo (Learner) sem precisar reespecificar os dados — o que facilita a comparação de vários algoritmos no mesmo conjunto de dados.

# Cria a tarefa de sobrevida
task_lung <- as_task_surv(
  lung_modelo,
  time  = "time",       # coluna com o tempo observado
  event = "status",     # coluna com 0 (censura) ou 1 (evento)
  type  = "right",      # tipo de censura: à direita
  id    = "lung_sobrevida"
)

print(task_lung)

── <TaskSurv> (168x9) ──────────────────────────────────────────────────────────
• Target: time and status
• Properties: -
• Features (7):
  • dbl (6): age, meal.cal, pat.karno, ph.ecog, ph.karno, wt.loss
  • fct (1): sex
task_lung$feature_names
[1] "age"       "meal.cal"  "pat.karno" "ph.ecog"   "ph.karno"  "sex"      
[7] "wt.loss"  
task_lung$cens_prop()   # proporção de censura
[1] 0.2797619
task_lung$formula()     # fórmula no formato Surv()
Surv(time, status, type = "right") ~ .
<environment: namespace:survival>

11.5 Passo 4: Divisão treino-teste

set.seed(2024)
split  <- partition(task_lung, ratio = 0.70)
treino <- split$train
teste  <- split$test

cat("Observações no treino:", length(treino), "\n")
Observações no treino: 118 
cat("Observações no teste: ", length(teste),  "\n")
Observações no teste:  50 

11.6 Passo 5: Definindo os modelos (learners)

Nota📌 O que é um ‘Learner’ no mlr3?

Um Learner é o objeto que representa um algoritmo de aprendizado de máquina. Ele encapsula:

  • O algoritmo em si (ex: Cox, Random Survival Forest)
  • Seus hiperparâmetros (ex: número de árvores, profundidade máxima)
  • O tipo de predição que produz (crank para ordenação de risco, distr para probabilidades)

Note que criamos versões separadas de cada modelo para crank e distr. Isso é necessário porque métricas como C-index precisam de crank e o Brier Score/IBS precisa de distr. É uma exigência do mlr3proba, não uma limitação dos algoritmos em si.

# Cox: predict_type = "crank" para C-index
cox_crank <- lrn("surv.coxph", predict_type = "crank")
# Cox: predict_type = "distr" para Brier Score / IBS
cox_distr <- lrn("surv.coxph", predict_type = "distr")

# RSF: discriminação
rsf_crank <- lrn(
  "surv.ranger",
  predict_type  = "crank",
  num.trees     = 500,
  mtry.ratio    = 0.5,
  min.node.size = 10,
  importance    = "permutation"
)

# RSF: calibração
rsf_distr <- lrn(
  "surv.ranger",
  predict_type  = "distr",
  num.trees     = 500,
  mtry.ratio    = 0.5,
  min.node.size = 10,
  importance    = "permutation"
)

11.7 Passo 6: Treinamento

cox_crank$train(task_lung, row_ids = treino)
cox_distr$train(task_lung, row_ids = treino)
rsf_crank$train(task_lung, row_ids = treino)
rsf_distr$train(task_lung, row_ids = treino)

# Inspeciona o modelo Cox
summary(cox_distr$model)
Call:
survival::coxph(formula = task$formula(), data = task$data(), 
    model = TRUE)

  n= 118, number of events= 84 

                  coef  exp(coef)   se(coef)      z Pr(>|z|)   
age          5.896e-03  1.006e+00  1.445e-02  0.408  0.68336   
meal.cal     8.859e-05  1.000e+00  3.129e-04  0.283  0.77706   
pat.karno   -2.252e-02  9.777e-01  1.013e-02 -2.223  0.02623 * 
ph.ecog      7.890e-01  2.201e+00  2.546e-01  3.099  0.00194 **
ph.karno     3.139e-02  1.032e+00  1.255e-02  2.502  0.01236 * 
sexfeminino -6.163e-01  5.400e-01  2.412e-01 -2.555  0.01061 * 
wt.loss     -2.118e-02  9.790e-01  9.613e-03 -2.203  0.02759 * 
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

            exp(coef) exp(-coef) lower .95 upper .95
age            1.0059     0.9941    0.9778    1.0348
meal.cal       1.0001     0.9999    0.9995    1.0007
pat.karno      0.9777     1.0228    0.9585    0.9973
ph.ecog        2.2013     0.4543    1.3365    3.6256
ph.karno       1.0319     0.9691    1.0068    1.0576
sexfeminino    0.5400     1.8520    0.3366    0.8662
wt.loss        0.9790     1.0214    0.9608    0.9977

Concordance= 0.679  (se = 0.034 )
Likelihood ratio test= 27.85  on 7 df,   p=2e-04
Wald test            = 27.33  on 7 df,   p=3e-04
Score (logrank) test = 28.75  on 7 df,   p=2e-04

11.8 Passo 7: Avaliação

pred_cox_crank <- cox_crank$predict(task_lung, row_ids = teste)
pred_cox_distr <- cox_distr$predict(task_lung, row_ids = teste)
pred_rsf_crank <- rsf_crank$predict(task_lung, row_ids = teste)
pred_rsf_distr <- rsf_distr$predict(task_lung, row_ids = teste)

medida_cindex <- msr("surv.cindex")
tau <- as.numeric(quantile(task_lung$times(treino), probs = 0.80))
medida_ibs <- msr("surv.graf", t_max = tau)

resultados <- data.table(
  Modelo  = c("Cox", "RSF"),
  C_index = round(c(
    pred_cox_crank$score(medida_cindex, task = task_lung, train_set = treino),
    pred_rsf_crank$score(medida_cindex, task = task_lung, train_set = treino)
  ), 4),
  IBS = round(c(
    pred_cox_distr$score(medida_ibs, task = task_lung, train_set = treino),
    pred_rsf_distr$score(medida_ibs, task = task_lung, train_set = treino)
  ), 4)
)
print(resultados)
   Modelo C_index    IBS
   <char>   <num>  <num>
1:    Cox  0.5675 0.1907
2:    RSF  0.5828 0.1989

11.9 Passo 8: Validação cruzada

set.seed(2024)
cv5 <- rsmp("cv", folds = 5)

rr_cox_c <- resample(task_lung, lrn("surv.coxph", predict_type = "crank"), cv5)
rr_rsf_c <- resample(
  task_lung,
  lrn("surv.ranger", predict_type = "crank",
      num.trees = 500, mtry.ratio = 0.5, min.node.size = 10),
  cv5
)

cat("C-index Cox (CV-5):", round(rr_cox_c$aggregate(msr("surv.cindex")), 4), "\n")
C-index Cox (CV-5): 0.6106 
cat("C-index RSF (CV-5):", round(rr_rsf_c$aggregate(msr("surv.cindex")), 4), "\n")
C-index RSF (CV-5): 0.5922 

11.10 Passo 9: Ajuste de hiperparâmetros

Nota📌 O que são hiperparâmetros e por que ajustá-los?

Os hiperparâmetros são configurações do algoritmo que controlam como ele aprende — eles não são aprendidos a partir dos dados, mas definidos pelo pesquisador antes do treinamento.

No RSF, os principais hiperparâmetros são:

Hiperparâmetro O que controla Valor padrão Impacto
num.trees Número de árvores na floresta 500 Mais árvores → mais estável, mais lento
mtry Variáveis testadas em cada divisão √p Controla diversidade das árvores
min.node.size Tamanho mínimo das folhas 10 (sobrevida) Maior → modelo mais simples
max.depth Profundidade máxima das árvores Sem limite Menor → menos overfitting

Estratégia de busca aleatória (random_search): testamos 30 combinações aleatórias de hiperparâmetros e escolhemos a que maximiza o C-index na validação cruzada de 3 folds. É mais eficiente que testar todas as combinações possíveis (grid search).

library(mlr3tuning)

rsf_tunable <- lrn("surv.ranger", predict_type = "crank")
rsf_tunable$param_set$values$num.trees     <- to_tune(100, 1000)
rsf_tunable$param_set$values$mtry.ratio    <- to_tune(0.2, 0.8)
rsf_tunable$param_set$values$min.node.size <- to_tune(5, 50)

instancia <- tune(
  tuner      = tnr("random_search"),
  task       = task_lung,
  learner    = rsf_tunable,
  resampling = rsmp("cv", folds = 3),
  measure    = msr("surv.cindex"),
  term_evals = 30
)

cat("Melhores hiperparâmetros:\n")
Melhores hiperparâmetros:
print(instancia$result_learner_param_vals)
$num.threads
[1] 1

$min.node.size
[1] 49

$mtry.ratio
[1] 0.2309553

$num.trees
[1] 299
cat("C-index ótimo:", round(instancia$result_y, 4), "\n")
C-index ótimo: 0.6001 

12 Interpretação Integrada e Boas Práticas

12.1 Qual modelo escolher?

Tabela 5: Guia de escolha de modelo por objetivo
Objetivo Modelo recomendado Justificativa
Publicar com hipótese clínica (HR de um fator) Cox clássico Interpretável, inferência formal
Seleção de variáveis em alta dimensionalidade Cox LASSO Seleção automática
Prever risco individual para decisão clínica RSF ou boosting Melhor predição, captura interações
Comparar grupos (exploratório) Kaplan-Meier + log-rank Simples, não paramétrico

12.2 Checklist para uma análise defensável

Nota✅ Antes de qualquer análise
Nota✅ Durante a modelagem
Nota✅ Na interpretação e relato

13 Exercícios Práticos

13.1 Nível 1: Compreensão conceitual

Exercício 1.1 — O que é censura? Uma criança de 5 anos recebeu TCTH e participou de um estudo de 2 anos. Ela estava viva quando o estudo foi encerrado. Qual é o status dela? Qual é o time? Por que não deve ser removida da análise?

Exercício 1.2 — Lendo uma curva KM A curva KM de pacientes com DECH aguda grau III mostra \hat{S}(180) = 0.62 e \hat{S}(365) = 0.48. O que esses valores significam em linguagem clínica?

Exercício 1.3 — Interpretando HR Um Cox retorna HR = 1.8 para “doador não aparentado vs. aparentado” com IC 95% = [1.2, 2.7]. Escreva uma frase interpretando esse resultado. Ele é estatisticamente significativo?

13.2 Nível 2: Análise exploratória no R

Exercício 2.1 — KM por ECOG Na base lung, crie curvas KM estratificadas por ph.ecog (0, 1, ≥2). Aplique o teste log-rank. As curvas diferem significativamente?

Exercício 2.2 — Exploração da censura Calcule a proporção de censura por grupo de sexo. Há evidência de que o mecanismo de censura pode ser diferente entre os sexos?

Exercício 2.3 — Mediana de sobrevida Calcule e compare a mediana de sobrevida entre homens e mulheres. Como você expressaria essa diferença clinicamente?

13.3 Nível 3: Modelagem

Exercício 3.1 — Modelo de Cox completo Ajuste Cox com age, sex, ph.ecog e ph.karno. Interprete cada HR. Verifique a proporcionalidade dos riscos.

Exercício 3.2 — Cox vs. RSF Compare usando CV-5 com C-index e IBS. O RSF supera o Cox? A diferença parece prática ou apenas estatística?

Exercício 3.3 — Efeito do tamanho amostral Use apenas os 50 primeiros pacientes. Refaça a comparação. As conclusões mudam?

13.4 Nível 4: Reflexão crítica

Exercício 4.1 — Censura informativa Em um estudo de DECH pós-TCTH, pacientes com progressão rápida tendem a abandonar o seguimento por piora clínica. Isso é censura informativa? Que problemas causa? Que informações adicionais você precisaria?

Exercício 4.2 — Escolha de métricas Um colega diz “o RSF tem C-index maior, então é sempre melhor”. Você concorda? Quais outras dimensões de desempenho devem ser consideradas?

Exercício 4.3 — Relatório metodológico Escreva um parágrafo de métodos descrevendo a análise desta apostila: objetivo, desfecho, tipo de censura, modelo, métricas e limitações. Limite: 200 palavras.

DicaCritério de avaliação

Não basta produzir um número. Uma análise bem feita inclui: definição explícita do evento e da origem do tempo, interpretação substantiva dos resultados, reconhecimento das limitações e reprodutibilidade (semente aleatória, versões dos pacotes, código organizado).

13.5 Gabarito comentado (Nível 1)

Nota📌 Gabarito — Exercício 1.1

Status: status = 0 (censurada — não apresentou o evento até o fim do estudo)

Time: time = 730 (dias desde o transplante até o encerramento do estudo, assumindo 2 anos = 730 dias)

Por que não excluir: porque ela carrega a informação de que sobreviveu pelo menos 730 dias. Se excluirmos, estaremos dizendo que “não temos dados desta criança”, o que é mentira — temos 730 dias de seguimento sem evento. Excluí-la produziria estimativas de sobrevida artificialmente pessimistas.

Nota📌 Gabarito — Exercício 1.2
  • \hat{S}(180) = 0.62: “62% das crianças com DECH aguda grau III ainda estavam vivas 180 dias após o transplante” — ou equivalentemente, 38% morreram antes de completar 6 meses.
  • \hat{S}(365) = 0.48: “48% das crianças com DECH aguda grau III ainda estavam vivas após 1 ano do transplante” — ou seja, a maioria (52%) havia morrido até esse ponto.

Ponto importante: entre 180 e 365 dias, a sobrevida caiu de 62% para 48%, uma queda de 14 pontos percentuais. Isso sugere que o risco de morte continuou sendo relevante no período dos 6 meses ao 1 ano.

Nota📌 Gabarito — Exercício 1.3

Frase interpretativa: “Pacientes que receberam doador não aparentado tiveram 1,8 vez mais risco instantâneo de óbito em comparação com aqueles que receberam doador aparentado (HR = 1,8; IC 95%: 1,2–2,7).”

Significância estatística: Sim, o resultado é estatisticamente significativo ao nível de 5%, pois o intervalo de confiança de 95% (1,2 a 2,7) não contém o valor 1 — o valor nulo de hipótese (nenhuma diferença de risco).

Cuidado: HR = 1,8 não significa que 80% a mais dos pacientes com doador não aparentado morrerão — significa que, em qualquer momento do seguimento, o risco instantâneo de morte é 80% maior nesse grupo. A tradução para probabilidade acumulada depende do horizonte temporal e do hazard basal.


14 Conclusão

Nota📌 Mapa da jornada — o que aprendemos

Percorremos um caminho longo. Antes de ler a conclusão, relembre os pontos principais de cada capítulo:

Capítulo Conceito-chave O que você aprendeu
1 — Por que existe? Censura Dados de tempo-evento são especiais: há informação parcial que não pode ser ignorada nem tratada como evento
2 — Censura Tipos e hipóteses Censura à direita é a mais comum; a hipótese de não-informatividade é essencial
3 — Pilares matemáticos S(t), h(t), H(t) Três funções que descrevem o mesmo fenômeno de ângulos diferentes
4 — Kaplan-Meier Estimador não paramétrico Descreve a sobrevida sem assumir distribuição; base de toda análise exploratória
5 — Log-rank Teste de hipóteses Compara curvas entre grupos; não substitui o Cox para controlar covariáveis
6 — Modelo de Cox Hazard Ratio Quantifica o efeito de cada fator; requer verificação dos riscos proporcionais
7 — Aprendizado de máquina Supervisionado vs. outros AM captura padrões complexos; complementa, não substitui, o Cox
8 — Overfitting Viés-variância Modelos devem generalizar para novos pacientes; validação cruzada é essencial
9 — Algoritmos RSF, Boosting, Cox penalizado Cada algoritmo tem pontos fortes específicos
10 — Métricas C-index e IBS Discriminação e calibração são dimensões distintas de desempenho
11 — mlr3proba Implementação Fluxo passo a passo: Task → Learner → Train → Predict → Score

A análise de sobrevida não é apenas “uma regressão com variável resposta especial”. É um framework estatístico com lógica própria, motivado pela necessidade de lidar com informação parcial — a censura — de forma que preserve tanto a validade estatística quanto a interpretabilidade clínica.

Ao longo desta apostila, percorremos um caminho que vai da intuição simples de três crianças pós-TCTH até a implementação de modelos de aprendizado de máquina com validação rigorosa. Alguns pontos merecem ser reforçados como conclusão:

1. O mecanismo de censura é mais importante do que a escolha do algoritmo. Um Random Survival Forest cuidadosamente ajustado não corrige um mecanismo de censura informativa mal compreendido. Antes de qualquer modelagem, entenda por que e como as observações foram censuradas em seu banco de dados.

2. Cox e AM são complementares, não concorrentes. O modelo de Cox responde “qual é o efeito de cada fator sobre o risco?” com interpretação formal. O AM responde “qual será o desfecho deste próximo paciente?” com maior flexibilidade preditiva. Use Cox para publicações com hipóteses clínicas; use AM para sistemas de estratificação de risco e apoio à decisão.

3. Discriminação ≠ Calibração. Um modelo com C-index elevado pode ter probabilidades completamente descalibradas. Para aplicações clínicas onde a probabilidade em um horizonte específico importa, avalie o Brier Score ou IBS.

4. Reprodutibilidade é parte da análise. Fixar a semente aleatória, registrar versões dos pacotes, organizar o código e documentar cada decisão metodológica não são detalhes — são parte da qualidade científica.

NotaUma reflexão final sobre TCTH e DECH

A Doença do Enxerto Contra o Hospedeiro continua sendo uma das principais causas de morbidade e mortalidade após transplante alogênico de células-tronco hematopoiéticas em crianças. A complexidade imunológica, clínica e temporal desse cenário torna a análise de sobrevida não apenas útil, mas essencial.

Os métodos aqui apresentados — de Kaplan-Meier ao Random Survival Forest — são ferramentas para transformar dados clínicos em conhecimento que orienta decisões de tratamento. Mas ferramentas só funcionam bem nas mãos de quem entende seus pressupostos, seus limites e o contexto clínico que os dados representam.

14.1 Cartão de referência rápida

Nota📌 Guia rápido — para consultar durante a análise

Interpretação do S(t):

  • S(t) = 0.80 → 80% permaneceram sem o evento após o tempo t
  • 1 - S(t) = 0.20 → 20% apresentaram o evento até o tempo t

Interpretação do HR:

  • HR = 1 → sem diferença de risco
  • HR > 1 → maior risco no grupo A vs. B
  • HR < 1 → menor risco (fator protetor) no grupo A vs. B
  • IC 95% não contém 1 → estatisticamente significativo (α = 5%)

Escolha do método:

  • Explorar e descrever → Kaplan-Meier
  • Comparar dois ou mais grupos → Teste log-rank + KM
  • Controlar múltiplas covariáveis, publicar HR → Cox
  • Predizer risco individual com muitas variáveis → RSF ou boosting
  • Selecionar variáveis em alta dimensionalidade → Cox LASSO

Métricas de avaliação:

  • C-index: quanto maior, melhor (máximo = 1.0; 0.5 = aleatório)
  • IBS: quanto menor, melhor; sempre comparar com a linha KM

Codificação padrão no R:

  • status = 0 → censurado
  • status = 1 → evento observado
  • Surv(time, status) → objeto de sobrevida

Verificar sempre:

14.2 Leituras recomendadas

Para aprofundar o aprendizado além desta apostila, recomendamos:

Análise de Sobrevida — nível introdutório a intermediário:

  • Kleinbaum, D.G. & Klein, M. (2012). Survival Analysis: A Self-Learning Text (3ª ed.). Springer. — O livro mais didático disponível; usa linguagem acessível e muitos exemplos.
  • Therneau, T.M. & Grambsch, P.M. (2000). Modeling Survival Data: Extending the Cox Model. Springer. — Referência técnica do pacote survival no R.

Aprendizado de máquina para sobrevida:

  • Ishwaran, H. & Kogalur, U.B. (2022). Fast Unified Random Forests for Survival, Regression, and Classification. Vinheta do pacote randomForestSRC.
  • Binder, N. et al. (2021). Allowing for mandatory covariates in boosting estimation of sparse high-dimensional survival models. BMC Bioinformatics.

Avaliação de modelos preditivos:

  • Graf, E. et al. (1999). Assessment and comparison of prognostic classification schemes for survival data. Statistics in Medicine, 18, 2529–2545. — Artigo seminal do Brier Score para sobrevida.
  • Uno, H. et al. (2011). On the C-statistics for evaluating overall adequacy of risk prediction procedures with censored survival data. Statistics in Medicine, 30, 1105–1117.

Recursos online gratuitos:


15 Glossário de Termos

Esta seção reúne os principais termos técnicos utilizados ao longo da apostila, com definições simples e objetivas.

NotaComo usar o glossário

Se em qualquer momento da leitura você encontrar um termo desconhecido, procure-o aqui primeiro. Os termos estão em ordem alfabética.

Tabela 6: Glossário de termos utilizados nesta apostila
Termo Definição simplificada
Aprendizado de máquina (AM) Conjunto de algoritmos que identificam padrões em dados e fazem previsões sem que as regras sejam programadas manualmente
Bootstrap Técnica de reamostrar os dados com reposição para criar amostras artificiais. Usada pelo RSF para dar diversidade às árvores
Brier Score Métrica que mede o erro nas probabilidades de sobrevida previstas (quanto menor, melhor)
C-index Índice de concordância — mede se o modelo ordena corretamente os pacientes do maior ao menor risco (0.5 = aleatório, 1.0 = perfeito)
Calibração Grau em que as probabilidades previstas pelo modelo correspondem às frequências observadas nos dados
Censura Situação em que não observamos o evento de interesse até o final do acompanhamento, mas sabemos que o paciente permaneceu livre do evento até certo tempo
Censura à direita Tipo mais comum — sabemos que o evento não ocorreu até o último tempo de observação, mas não sabemos o que aconteceu depois
Cox (modelo de) Modelo semiparamétrico que estima o efeito de covariáveis sobre o hazard sem assumir forma para o hazard basal
Discriminação Capacidade de um modelo de separar pacientes em grupos de maior e menor risco
DECH (Doença do Enxerto Contra o Hospedeiro) Complicação do TCTH em que as células do doador atacam os tecidos do receptor
Evento Desfecho de interesse que ocorre em um momento específico do tempo (ex.: óbito, recidiva)
Fração curada Proporção de indivíduos em que o evento de interesse aparentemente não ocorrerá no longo prazo
Função de hazard h(t) Intensidade instantânea de risco entre os pacientes que ainda não apresentaram o evento
Função de sobrevivência S(t) Probabilidade de permanecer sem o evento após o tempo t
Gradient Boosting Família de algoritmos que constroem modelos em sequência, cada um corrigindo os erros do anterior
Hazard Ratio (HR) Razão entre o hazard de dois grupos — medida central do modelo de Cox. HR > 1: maior risco; HR < 1: fator protetor
Hiperparâmetro Configuração do algoritmo definida antes do treinamento (ex.: número de árvores no RSF)
IBS (Integrated Brier Score) Brier Score integrado ao longo do tempo — mede a qualidade das probabilidades previstas em todos os instantes
IPCW Ponderação inversa pela probabilidade de censura — técnica para corrigir o viés das observações censuradas em certas métricas
Kaplan-Meier Método não paramétrico para estimar a função de sobrevivência a partir de dados com censura
Learner No vocabulário do mlr3, o objeto que representa um algoritmo de aprendizado de máquina
Log-rank (teste) Teste estatístico para comparar curvas de sobrevivência entre dois ou mais grupos
mlr3proba Pacote R do ecossistema mlr3 específico para análise de sobrevida
Overfitting (sobreajuste) Quando o modelo aprende os dados de treinamento com detalhes excessivos (incluindo ruído) e generaliza mal para novos dados
Predição crank Tipo de predição que ordena pacientes por risco (usado para C-index)
Predição distr Tipo de predição que estima a distribuição de sobrevida ao longo do tempo (usado para Brier Score/IBS)
Random Survival Forest (RSF) Extensão do Random Forest para dados de sobrevida — constrói centenas de árvores de decisão e combina suas previsões
Regularização Técnica que penaliza modelos muito complexos para reduzir overfitting (ex.: LASSO, Ridge)
Risco acumulado H(t) Integral do hazard até o tempo t — mede o risco total acumulado desde o início
Riscos competitivos Situação em que outro evento (ex.: óbito por causa não relacionada) impede a observação do evento de interesse
Riscos proporcionais Pressuposto do modelo de Cox — o Hazard Ratio entre grupos permanece constante ao longo do tempo
Task No vocabulário do mlr3, o objeto que encapsula o problema de aprendizado (dados + variável resposta + tipo)
TCTH Transplante de Células-Tronco Hematopoiéticas (também chamado transplante de medula óssea)
Validação cruzada k-fold Técnica de avaliação que divide os dados em k partes, usando cada parte como teste em diferentes iterações
Viés-variância (dilema) Compromisso fundamental em AM: modelos simples têm alto viés (subajuste); modelos complexos têm alta variância (sobreajuste)

16 Referências

COX, D. R. Regression Models and Life-Tables. Journal of the Royal Statistical Society: Series B, [s. l.], v. 34, n. 2, p. 187–220, 1972.
GRAF, E.; SCHMOOR, C.; SAUERBREI, W.; SCHUMACHER, M. Assessment and comparison of prognostic classification schemes for survival data. Statistics in Medicine, [s. l.], v. 18, n. 17-18, p. 2529–2545, 1999.
HASTIE, T.; TIBSHIRANI, R.; FRIEDMAN, J. The Elements of Statistical Learning: Data Mining, Inference, and Prediction. 2nd. ed. New York: Springer, 2009.
ISHWARAN, H.; KOGALUR, U. B.; BLACKSTONE, E. H.; LAUER, M. S. Random survival forests. The Annals of Applied Statistics, [s. l.], v. 2, n. 3, p. 841–860, 2008.
KAPLAN, E. L.; MEIER, P. Nonparametric Estimation from Incomplete Observations. Journal of the American Statistical Association, [s. l.], v. 53, n. 282, p. 457–481, 1958.
MANTEL, N. Evaluation of survival data and two new rank order statistics arising in its consideration. Cancer Chemotherapy Reports, [s. l.], v. 50, n. 3, p. 163–170, 1966.
UNO, H.; CAI, T.; PENCINA, M. J.; D’AGOSTINO, R. B.; WEI, L. J. On the C-statistics for evaluating overall adequacy of risk prediction procedures with censored survival data. Statistics in Medicine, [s. l.], v. 30, n. 10, p. 1105–1117, 2011.