---
title: "Análise de Sobrevida e Aprendizado de Máquina em R"
author:
- name: "Prof. Letícia Martins Raposo"
affiliation: "LAMDS — Laboratório de Análise e Modelagem de Dados em Saúde"
date: today
date-format: "DD [de] MMMM [de] YYYY"
lang: pt-BR
format:
html:
theme: cosmo
toc: true
toc-depth: 4
toc-title: "Conteúdo"
toc-location: left
number-sections: true
code-fold: true
code-tools: true
code-copy: true
code-overflow: wrap
highlight-style: github
fig-align: center
fig-cap-location: bottom
callout-appearance: simple
smooth-scroll: true
anchor-sections: true
html-math-method: katex
embed-resources: true
css: |
body { font-size: 1.05rem; line-height: 1.75; }
.callout { border-radius: 8px; }
h1 { color: #1a6faf; border-bottom: 2px solid #1a6faf; padding-bottom: 0.3em; }
h2 { color: #2c5f8a; }
h3 { color: #444; }
.destaque { background: #eef6fb; border-left: 4px solid #1a6faf;
padding: 0.8em 1em; border-radius: 4px; margin: 1em 0; }
execute:
echo: true
eval: true
warning: false
message: false
cache: false
bibliography: references.bib
csl: abnt.csl
---
```{r}
#| label: setup
#| include: false
#| eval: false
pkgs <- c(
"survival", "data.table", "ggplot2", "ggsurvfit", "patchwork",
"viridis", "scales", "ggrepel", "tidyr",
"mlr3", "mlr3verse", "mlr3extralearners", "mlr3tuning", "ranger",
"paradox"
)
novos <- pkgs[!pkgs %in% installed.packages()[,"Package"]]
if (length(novos)) install.packages(novos)
if (!"mlr3proba" %in% installed.packages()[,"Package"])
install.packages("mlr3proba",
repos = c("https://mlr-org.r-universe.dev","https://cloud.r-project.org"))
invisible(lapply(pkgs, library, character.only = TRUE))
library(mlr3proba)
set.seed(2024)
```
---
::: {.callout-note icon="true" title="Sobre 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.
:::
---
# Por Que a Análise de Sobrevida Existe? {#sec-porque}
::: {.callout-note title="📌 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.
:::
## A história de três crianças {#sec-historia}
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.
::: {.callout-tip title="Analogia 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 *já* acumularam.
:::
::: {.callout-important title="⚠️ 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.
:::
## O que é um "evento" na análise de sobrevida? {#sec-evento}
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:
| Á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 |
: Exemplos de eventos em diferentes áreas de aplicação {#tbl-eventos .striped}
::: {.callout-note title="📌 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.
:::
## O que é “tempo até o evento”? {#sec-tempo}
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.
::: {.callout-important title="A 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.
---
# Censura: Lidando com Informação Incompleta {#sec-censura}
::: {.callout-note title="📌 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
:::
## O que é censura? {#sec-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**.
::: {.callout-tip title="Ideia 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.
:::
::: {.callout-note title="📌 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).
:::
### 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.
::: {.callout-important title="Censura 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.
:::
### 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.
::: {.callout-warning title="Pergunta 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.
:::
### 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.
::: {.callout-note title="📌 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 |
:::
### 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.
::: {.callout-note title="📌 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.
:::
### 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.
```{r}
#| label: censo-exemplo
#| code-fold: false
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)
lung_dt[, .N, by = status]
```
Para análises em que o evento deve ser codificado como `1` e a censura como `0`, é necessário recodificar a variável.
```{r}
#| label: recodifica-lung
#| code-fold: false
lung_dt[, status_evento := as.integer(status == 2)]
lung_dt[, .N, by = .(status, status_evento)]
```
### Visualizando censura
```{r}
#| label: fig-censura-visual
#| fig-cap: "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."
#| fig-height: 5.5
#| fig-width: 7.0
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()
)
```
## Por que não simplesmente ignorar os censurados? {#sec-por-que-nao-ignorar}
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.
::: {.callout-note title="Ponto-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.
:::
## Codificando censura no R {#sec-cod-censura}
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.
```{r}
#| label: cod-censura-exemplo
#| code-fold: false
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
with(criancas, Surv(time, status))
```
No objeto produzido por `Surv()`, o sinal `+` indica censura. Assim, `400+` significa que o paciente foi observado por 400 dias sem apresentar o evento.
::: {.callout-warning title="Armadilha 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.
:::
```{r}
#| label: verifica-status
#| code-fold: false
# Recodificação da base lung:
lung_dt[, status_evento := as.integer(status == 2)]
# Conferência:
lung_dt[, .N, by = .(status_original = status, status_evento)]
```
---
# Os Três Pilares Matemáticos da Análise de Sobrevida {#sec-pilares}
::: {.callout-note title="📌 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.
## A função de sobrevivência S(t): "Qual é a chance de ainda estar bem?" {#sec-st}
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.
### 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.
::: {.callout-note title="📌 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.
:::
::: {.callout-tip title="Lendo 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%.
:::
### Visualizando S(t) para diferentes distribuições
```{r}
#| label: fig-st-distribuicoes
#| fig-cap: "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)."
#| fig-height: 4.5
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))
```
## A função de hazard h(t): "Qual é o risco *agora*?" {#sec-ht}
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}$
### 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**.
::: {.callout-tip title="Analogia: 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.
:::
### 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.
```{r}
#| label: fig-hazard-formas
#| fig-cap: "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)."
#| fig-height: 4.5
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))
```
## Risco acumulado H(t) e a conexão com S(t) {#sec-Ht}
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.
::: {.callout-tip title="Analogia: 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.
:::
```{r}
#| label: fig-conexao-S-H
#| fig-cap: "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."
#| fig-height: 4.5
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
```
---
# Estimador de Kaplan-Meier: Descrevendo a Sobrevida sem Pressupostos {#sec-km}
::: {.callout-note title="📌 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.
:::
## A ideia central {#sec-km-ideia}
O estimador de Kaplan-Meier (KM) [@kaplan1958] é 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.
### 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$
::: {.callout-tip title="Exemplo 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.
:::
::: {.callout-note title="📌 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!
:::
::: {.callout-tip title="💡 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.
:::
```{r}
#| label: km-basico
#| code-fold: false
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))
```
```{r}
#| label: fig-km-global-sexo
#| fig-cap: "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."
#| fig-height: 6.5
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")
```
## Como ler e interpretar a curva KM {#sec-km-leitura}
Cada elemento visual da curva tem um significado:
| 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 |
: Guia de leitura da curva de Kaplan-Meier {#tbl-leitura-km .striped}
::: {.callout-important title="Cuidado 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.
:::
## Mediana de sobrevida {#sec-mediana}
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
```{r}
#| label: mediana-sexo
#| code-fold: false
# Mediana de sobrevida por sexo
km_sex <- survfit(Surv(time, status) ~ sex, data = lung_limpo)
print(km_sex) # Exibe mediana e IC 95%
```
::: {.callout-note title="Contextualizando 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.
:::
---
# Teste Log-Rank: Comparando Curvas Entre Grupos {#sec-logrank}
## O que o teste log-rank faz? {#sec-logrank-def}
Quando temos dois ou mais grupos e queremos saber se as curvas de sobrevivência são **estatisticamente diferentes**, usamos o teste log-rank [@mantel1966].
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
::: {.callout-tip title="Analogia: 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.
:::
## Como interpretar o p-valor {#sec-logrank-pvalor}
- **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
::: {.callout-important title="O 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.
:::
```{r}
#| label: logrank-sexo-ecog
#| code-fold: false
# Teste log-rank por sexo
lr_sex <- survdiff(Surv(time, status) ~ sex, data = lung_limpo)
print(lr_sex)
# 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)
```
```{r}
#| label: fig-km-ecog
#| fig-cap: "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."
#| fig-height: 6
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")
```
---
# Modelo de Cox: Quantificando o Efeito das Covariáveis {#sec-cox}
::: {.callout-note title="📌 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.
:::
## Motivação: por que precisamos de um modelo? {#sec-cox-motivacao}
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** [@cox1972] permite analisar o efeito de múltiplas covariáveis sobre o *hazard* ao mesmo tempo.
## A equação do modelo de Cox {#sec-cox-equacao}
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.
::: {.callout-tip title="Analogia: 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.
:::
## Hazard Ratio (HR): o coração da interpretação {#sec-hr}
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$$
### Guia de interpretação
::: {.callout-note title="Como 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%
:::
::: {.callout-note title="📌 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.
:::
::: {.callout-important title="O 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.
:::
## Ajustando o modelo de Cox no R {#sec-cox-r}
```{r}
#| label: cox-ajuste
#| code-fold: false
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)
```
### Lendo a saída do coxph()
```{r}
#| label: cox-leitura
#| code-fold: false
# 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))]
```
```{r}
#| label: fig-forest-cox
#| fig-cap: "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)."
#| fig-height: 4.5
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()
)
```
## O pressuposto de riscos proporcionais {#sec-cox-pressuposto}
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.
::: {.callout-tip title="O 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.
### 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.
```{r}
#| label: fig-cox-zph-teste
#| fig-cap: "Teste formal de riscos proporcionais de Schoenfeld para cada covariável do modelo de Cox. Gráficos sem tendência temporal (linha aproximadamente horizontal) suportam o pressuposto de proporcionalidade. O p-valor abaixo de 0.05 em 'GLOBAL' indica que ao menos uma covariável viola o pressuposto, requerendo correção (termos de interação com tempo, estratificação ou modelos alternativos)."
#| fig-height: 6
cox_ph_teste <- cox.zph(cox_modelo)
print(cox_ph_teste) # Veja o p-valor global e por variável
```
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.
```{r}
#| label: fig-cox-zph-grafico
#| fig-cap: "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."
#| fig-height: 6
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))
```
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.
::: {.callout-warning title="Interpretaçã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.
---
# Aprendizado de Máquina: Do Zero ao Conceito {#sec-ml-fundamentos}
::: {.callout-note title="📌 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.
:::
## O que é aprendizado de máquina? {#sec-ml-o-que-e}
**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 [@hastie2009].
::: {.callout-tip title="Analogia 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.
:::
## Os três tipos de aprendizado {#sec-ml-tipos}
### 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**
### 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
### 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?)
```{r}
#| label: fig-tipos-am
#| fig-cap: "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."
#| fig-height: 4.5
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"))
```
## Por que usar AM na análise de sobrevida? {#sec-ml-porque}
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.
::: {.callout-note title="AM 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.
:::
::: {.callout-note title="📌 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` |
:::
---
# Overfitting, Generalização e Validação {#sec-overfitting}
::: {.callout-note title="📌 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.
:::
## O problema do "decoro" estatístico {#sec-overfitting-def}
**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.
::: {.callout-tip title="Analogia: 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.
:::
## O dilema viés-variância {#sec-bias-variance}
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.
```{r}
#| label: fig-bias-variancia
#| fig-cap: "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."
#| fig-height: 4.5
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())
```
## Estratégias para evitar overfitting {#sec-estrategias-overfitting}
### 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.
::: {.callout-warning title="Cuidado 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.
:::
### 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.
::: {.callout-note title="📌 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.
:::
```{r}
#| fig-cap: "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."
#| fig-height: 4
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())
```
### 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.
### 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
---
# Algoritmos de AM para Sobrevida {#sec-algoritmos}
::: {.callout-note title="📌 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.
:::
## Visão geral {#sec-algo-visao}
Vários algoritmos foram adaptados para lidar com dados de sobrevida censurados. Cada um tem pontos fortes e fracos:
```{r}
#| label: fig-mapa-algoritmos
#| fig-cap: "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."
#| fig-height: 5.5
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))
```
## Random Survival Forest (RSF) {#sec-rsf}
O RSF [@ishwaran2008] é o algoritmo de AM para sobrevida mais amplamente usado na literatura médica. Ele adapta o Random Forest clássico para dados censurados.
### Como uma árvore de decisão de sobrevida funciona
::: {.callout-tip title="Analogia: 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.
:::
::: {.callout-note title="📌 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.
### 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
::: {.callout-note title="RSF 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.
:::
## Gradient Boosting para Sobrevida {#sec-boosting}
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`.
## Cox Penalizado {#sec-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.
---
# Métricas de Avaliação em Sobrevida {#sec-metricas}
::: {.callout-note title="📌 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.
:::
## Por que acurácia não serve? {#sec-metricas-porque}
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
## C-index: quem tem mais risco? {#sec-cindex}
O **C-index** (Índice de Concordância) mede a capacidade do modelo de *ordenar corretamente* os pacientes por risco [@uno2011].
> *"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.
::: {.callout-tip title="Analogia: 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.
:::
| 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) |
: Interpretação do C-index {#tbl-cindex .striped}
::: {.callout-important title="Limitaçã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*.
:::
## Brier Score e Integrated Brier Score: probabilidades corretas? {#sec-ibs}
O **Brier Score** em um tempo $t^*$ mede o **erro quadrático médio das probabilidades de sobrevivência previstas** [@graf1999]. 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$$
::: {.callout-tip title="Analogia: 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%.
:::
| 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` |
: Métricas para avaliação de modelos de sobrevida {#tbl-metricas .striped}
## Linha de base: o modelo de referência {#sec-linha-base}
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.
```{r}
#| label: fig-metricas-visualizacao
#| fig-cap: "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."
#| fig-height: 4
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")
```
---
# Implementação Completa com mlr3proba {#sec-mlr3}
::: {.callout-note title="📌 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.**
:::
::: {.callout-tip title="💡 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.
:::
## O ecossistema mlr3 {#sec-mlr3-eco}
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.
```{r}
#| label: fig-mlr3-ecosistema
#| fig-cap: "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."
#| fig-height: 3.5
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)
)
```
## Passo 1: Instalação e carregamento {#sec-mlr3-install}
```{r}
#| label: mlr3-install
#| code-fold: false
#| eval: false
# 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")
)
```
```{r}
#| label: mlr3-load
#| code-fold: false
library(data.table)
library(survival)
library(mlr3)
library(mlr3proba)
library(mlr3extralearners)
library(ggplot2)
# Reprodutibilidade: fixar semente aleatória
set.seed(2024)
```
## Passo 2: Preparação dos dados {#sec-mlr3-dados}
```{r}
#| label: mlr3-dados
#| code-fold: false
# 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)
)]
```
::: {.callout-warning title="Verifique 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?
:::
## Passo 3: Criando a TaskSurv {#sec-mlr3-task}
::: {.callout-note title="📌 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.
:::
```{r}
#| label: mlr3-task
#| code-fold: false
# 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)
task_lung$feature_names
task_lung$cens_prop() # proporção de censura
task_lung$formula() # fórmula no formato Surv()
```
## Passo 4: Divisão treino-teste {#sec-mlr3-split}
```{r}
#| label: mlr3-split
#| code-fold: false
set.seed(2024)
split <- partition(task_lung, ratio = 0.70)
treino <- split$train
teste <- split$test
cat("Observações no treino:", length(treino), "\n")
cat("Observações no teste: ", length(teste), "\n")
```
## Passo 5: Definindo os modelos (learners) {#sec-mlr3-learners}
::: {.callout-note title="📌 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.
:::
```{r}
#| label: mlr3-learners
#| code-fold: false
# 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"
)
```
## Passo 6: Treinamento {#sec-mlr3-treino}
```{r}
#| label: mlr3-treino
#| code-fold: false
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)
```
## Passo 7: Avaliação {#sec-mlr3-avaliacao}
```{r}
#| label: mlr3-avaliacao
#| code-fold: false
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)
```
## Passo 8: Validação cruzada {#sec-mlr3-cv}
```{r}
#| label: mlr3-cv-final
#| code-fold: false
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")
cat("C-index RSF (CV-5):", round(rr_rsf_c$aggregate(msr("surv.cindex")), 4), "\n")
```
## Passo 9: Ajuste de hiperparâmetros {#sec-mlr3-tuning}
::: {.callout-note title="📌 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*).
:::
```{r}
#| label: mlr3-tuning-final
#| code-fold: false
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")
print(instancia$result_learner_param_vals)
cat("C-index ótimo:", round(instancia$result_y, 4), "\n")
```
---
# Interpretação Integrada e Boas Práticas {#sec-boas-praticas}
## Qual modelo escolher? {#sec-escolha}
| 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 |
: Guia de escolha de modelo por objetivo {#tbl-escolha .striped}
## Checklist para uma análise defensável {#sec-checklist}
::: {.callout-note title="✅ Antes de qualquer análise"}
- [ ] A origem do tempo está claramente definida e justificada?
- [ ] O evento de interesse está operacionalizado sem ambiguidade?
- [ ] `status` foi codificado como 0 (censura) e 1 (evento)?
- [ ] A proporção de censura foi calculada e reportada?
- [ ] O mecanismo de censura foi discutido (informativa vs. não informativa)?
- [ ] Valores ausentes foram tratados de forma explícita?
:::
::: {.callout-note title="✅ Durante a modelagem"}
- [ ] O pressuposto de riscos proporcionais foi testado (para Cox)?
- [ ] O conjunto de teste foi separado antes de qualquer decisão de modelagem?
- [ ] A métrica de avaliação está alinhada ao objetivo?
- [ ] O horizonte temporal τ para o IBS foi justificado clinicamente?
:::
::: {.callout-note title="✅ Na interpretação e relato"}
- [ ] HRs são interpretados na escala de hazard, não de probabilidade?
- [ ] A importância de variáveis no RSF foi apresentada como preditiva, não causal?
- [ ] As versões dos pacotes foram registradas?
- [ ] As limitações do estudo foram discutidas?
:::
---
# Exercícios Práticos {#sec-exercicios}
## 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?
## 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?
## 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?
## 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.
::: {.callout-tip title="Crité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).
:::
## Gabarito comentado (Nível 1) {#sec-gabarito}
::: {.callout-note title="📌 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.
:::
::: {.callout-note title="📌 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.
:::
::: {.callout-note title="📌 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.
:::
---
# Conclusão {#sec-conclusao}
::: {.callout-note title="📌 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.
::: {.callout-note title="Uma 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.
:::
## Cartão de referência rápida {#sec-quickref}
::: {.callout-note title="📌 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:**
- [ ] Recodifiquei o status? (0 = censura, 1 = evento)
- [ ] Verifiquei a proporção de censura?
- [ ] Testei o pressuposto de riscos proporcionais (Cox)?
- [ ] Fixei a semente aleatória (`set.seed()`)?
- [ ] Registrei as versões dos pacotes (`sessionInfo()`)?
:::
## Leituras recomendadas {#sec-leituras}
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:**
- Documentação do mlr3proba: <https://mlr3proba.mlr-org.com>
- Livro online *Applied Machine Learning Using mlr3 in R*: <https://mlr3book.mlr-org.com>
- Vinheta do `ggsurvfit`: <https://www.danieldsjoberg.com/ggsurvfit>
---
# Glossário de Termos {#sec-glossario}
Esta seção reúne os principais termos técnicos utilizados ao longo da apostila, com definições simples e objetivas.
::: {.callout-note title="Como 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.
:::
| 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) |
: Glossário de termos utilizados nesta apostila {#tbl-glossario .striped}
---
# Referências {#sec-referencias}
::: {#refs}
:::