# Paquetes
pkgs <- c("survival","dplyr","ggplot2","tableone","tidyr","knitr","kableExtra","flextable","stringr","forcats","scales","purrr")
to_install <- setdiff(pkgs, rownames(installed.packages()))
if (length(to_install)) install.packages(to_install, dependencies = TRUE)
invisible(lapply(pkgs, library, character.only = TRUE))

# Cargar dataset completo
data(pbc, package = "survival")

# Filtrar SOLO los pacientes aleatorizados del RCT (trt no-NA)
pbc_rct <- pbc %>% dplyr::filter(!is.na(trt))
stopifnot(nrow(pbc_rct) == 312)

# Etiquetas y recodificaciones útiles para la subcohorte RCT
pbc_rct <- pbc_rct %>%
  mutate(
    # 0=censurado, 1=trasplante, 2=muerte
    sobrevive_corte = dplyr::case_when(status == 0 ~ "Sí", TRUE ~ "No"),
    trt_f   = factor(trt, levels = c(1,2), labels = c("D-penicilamina","Placebo")),
    sex     = factor(sex,     levels = c("f","m"), labels = c("Femenino","Masculino")),
    ascites = factor(ascites, levels = c(0,1), labels = c("No","Sí")),
    hepato  = factor(hepato,  levels = c(0,1), labels = c("No","Sí")),
    spiders = factor(spiders, levels = c(0,1), labels = c("No","Sí")),
    edema   = factor(edema, levels = c(0,0.5,1),
                     labels = c("No edema", "Tratado/compensado", "Resistente")),
    stage   = factor(stage, levels = sort(unique(stage)))
  )

INTRODUCCIÓN

Este informe corrige explícitamente que todas las descripciones y tablas se realizan exclusivamente sobre los 312 pacientes que participaron en el ensayo clínico aleatorizado (RCT) de la base de datos pbc (survival::pbc). De acuerdo con ?pbc, los pacientes con trt = NA no hicieron parte del RCT y no serán analizados aquí.

Objetivos

  1. Operacionalizar las variables relevantes del estudio y documentar su naturaleza.
  2. Preparar la subcohorte RCT (N = 312) y recodificar variables para análisis descriptivo.
  3. Generar tablas y gráficos descriptivos de características basales y seguimiento.
  4. Redactar resultados con lenguaje técnico, apropiado para la sección de “Resultados” de un artículo.

Nota metodológica clave: La selección de la cohorte de análisis se hace con filter(!is.na(trt)), lo que fuerza N=312. Se agrega stopifnot(nrow(pbc_rct) == 312) para garantizar reproducibilidad y evitar usar observaciones fuera del RCT.

1) Conjunto de datos (subcohorte RCT)

# Vistazo inicial de la subcohorte
knitr::kable(head(pbc_rct), caption = "Primeras filas de pbc_rct (subcohorte RCT, N=312)") %>%
  kable_styling(full_width = FALSE)
Primeras filas de pbc_rct (subcohorte RCT, N=312)
id time status trt age sex ascites hepato spiders edema bili chol albumin copper alk.phos ast trig platelet protime stage sobrevive_corte trt_f
1 400 2 1 58.76523 Femenino Resistente 14.5 261 2.60 156 1718.0 137.95 172 190 12.2 4 No D-penicilamina
2 4500 0 1 56.44627 Femenino No No edema 1.1 302 4.14 54 7394.8 113.52 88 221 10.6 3 D-penicilamina
3 1012 2 1 70.07255 Masculino No No No Tratado/compensado 1.4 176 3.48 210 516.0 96.10 55 151 12.0 4 No D-penicilamina
4 1925 2 1 54.74059 Femenino No Tratado/compensado 1.8 244 2.54 64 6121.8 60.63 92 183 10.3 4 No D-penicilamina
5 1504 1 2 38.10541 Femenino No No edema 3.4 279 3.53 143 671.0 113.15 72 136 10.9 3 No Placebo
6 2503 2 2 66.25873 Femenino No No No edema 0.8 248 3.98 50 944.0 93.00 63 NA 11.0 3 No Placebo

Codificación (de ?pbc):
- status: 0 = censurado, 1 = trasplante, 2 = muerte.
- trt: 1 = D-penicilamina, 2 = placebo.
- Variables clínicas/analíticas con sus unidades indicadas en la tabla operacional.

2) Cuadro operacional y guía de análisis

A partir de la ayuda de R y la documentación del libro, se elabora el siguiente cuadro operacional de variables y, para cada una, se especifican tipos de tablas, gráficos y estadísticos recomendados según su naturaleza.

2.1 Cuadro operacional de variables

var_table <- tibble::tribble(
  ~Variable, ~`Definición operativa`, ~`Tipo (naturaleza y nivel)`, ~`Códigos / Unidad`,
  "id", "Identificador del paciente", "Cualitativa nominal (ID)", "Único por paciente",
  "time", "Tiempo hasta evento/censura (días)", "Cuantitativa continua (razón)", "Días",
  "status", "Estado al final del seguimiento", "Cualitativa nominal", "0=cens., 1=tx, 2=muerte",
  "trt", "Grupo de tratamiento", "Cualitativa nominal", "1=D-penicilamina; 2=Placebo",
  "age", "Edad al ingreso (años)", "Cuantitativa continua (razón)", "Años",
  "sex", "Sexo", "Cualitativa nominal", "Femenino; Masculino",
  "ascites", "Ascitis clínica", "Cualitativa nominal", "No; Sí",
  "hepato", "Hepatomegalia", "Cualitativa nominal", "No; Sí",
  "spiders", "Arañas vasculares", "Cualitativa nominal", "No; Sí",
  "edema", "Edema periférico", "Cualitativa ordinal (3 niveles)", "No; Tratado/compensado; Resistente",
  "bili", "Bilirrubina sérica", "Cuantitativa continua (razón)", "mg/dl",
  "chol", "Colesterol sérico", "Cuantitativa continua (razón)", "mg/dl",
  "albumin", "Albúmina sérica", "Cuantitativa continua (razón)", "g/dl",
  "copper", "Cobre sérico", "Cuantitativa continua (razón)", "mcg/dl",
  "alk.phos", "Fosfatasa alcalina", "Cuantitativa continua (razón)", "U/L",
  "ast", "AST (SGOT)", "Cuantitativa continua (razón)", "U/L",
  "trig", "Triglicéridos", "Cuantitativa continua (razón)", "mg/dl",
  "platelet", "Plaquetas", "Cuantitativa continua (razón)", "10^3/mm^3",
  "protime", "Tiempo de protrombina", "Cuantitativa continua (razón)", "Segundos",
  "stage", "Grado histológico de fibrosis", "Cualitativa ordinal", "1 a 4 (4 = avanz.)"
)

knitr::kable(var_table, caption = "Cuadro operacional de variables – Subcohorte RCT (N=312)") %>%
  kable_styling(full_width = FALSE, position = "center")
Cuadro operacional de variables – Subcohorte RCT (N=312)
Variable Definición operativa Tipo (naturaleza y nivel) Códigos / Unidad
id Identificador del paciente Cualitativa nominal (ID) Único por paciente
time Tiempo hasta evento/censura (días) Cuantitativa continua (razón) Días
status Estado al final del seguimiento Cualitativa nominal 0=cens., 1=tx, 2=muerte
trt Grupo de tratamiento Cualitativa nominal 1=D-penicilamina; 2=Placebo
age Edad al ingreso (años) Cuantitativa continua (razón) Años
sex Sexo Cualitativa nominal Femenino; Masculino
ascites Ascitis clínica Cualitativa nominal No; Sí
hepato Hepatomegalia Cualitativa nominal No; Sí
spiders Arañas vasculares Cualitativa nominal No; Sí
edema Edema periférico Cualitativa ordinal (3 niveles) No; Tratado/compensado; Resistente
bili Bilirrubina sérica Cuantitativa continua (razón) mg/dl
chol Colesterol sérico Cuantitativa continua (razón) mg/dl
albumin Albúmina sérica Cuantitativa continua (razón) g/dl
copper Cobre sérico Cuantitativa continua (razón) mcg/dl
alk.phos Fosfatasa alcalina Cuantitativa continua (razón) U/L
ast AST (SGOT) Cuantitativa continua (razón) U/L
trig Triglicéridos Cuantitativa continua (razón) mg/dl
platelet Plaquetas Cuantitativa continua (razón) 103/mm3
protime Tiempo de protrombina Cuantitativa continua (razón) Segundos
stage Grado histológico de fibrosis Cualitativa ordinal 1 a 4 (4 = avanz.)

2.2 Guía rápida: tablas, gráficos y estadísticos

guia <- tibble::tribble(
  ~`Tipo de variable`, ~`Tablas`, ~`Gráficos`, ~`Estadísticos recomendados`,
  "Nominal (binaria/multinomial)",
  "Frecuencia (n, %) por grupo (trt)",
  "Barras agrupadas/apiladas, mosaico",
  "Proporciones, IC; comparaciones: Chi²/Fisher",
  "Ordinal (pocas categorías)",
  "Frecuencia y % por nivel; por trt",
  "Barras ordenadas, mosaico",
  "Mediana, IQR; tendencia (Cochran–Armitage)",
  "Continua ~ normal",
  "Media (sd), n por trt",
  "Histogramas, densidad, boxplots, violin",
  "Media, sd, IC95%; t/ANOVA",
  "Continua asimétrica",
  "Mediana (IQR), n por trt",
  "Hist (log), boxplots",
  "Mediana, IQR; Wilcoxon/Kruskal–Wallis",
  "Tiempo-evento",
  "Tablas por estado (evento/censura) y trt",
  "Kaplan–Meier, tablas de riesgo",
  "Medianas, HR (Cox) con IC95%"
)

knitr::kable(guia, caption = "Guía de análisis según naturaleza de la variable") %>%
  kable_styling(full_width = FALSE, position = "center")
Guía de análisis según naturaleza de la variable
Tipo de variable Tablas Gráficos Estadísticos recomendados
Nominal (binaria/multinomial) Frecuencia (n, %) por grupo (trt) Barras agrupadas/apiladas, mosaico Proporciones, IC; comparaciones: Chi²/Fisher
Ordinal (pocas categorías) Frecuencia y % por nivel; por trt Barras ordenadas, mosaico Mediana, IQR; tendencia (Cochran–Armitage)
Continua ~ normal Media (sd), n por trt Histogramas, densidad, boxplots, violin Media, sd, IC95%; t/ANOVA
Continua asimétrica Mediana (IQR), n por trt Hist (log), boxplots Mediana, IQR; Wilcoxon/Kruskal–Wallis
Tiempo-evento Tablas por estado (evento/censura) y trt Kaplan–Meier, tablas de riesgo Medianas, HR (Cox) con IC95%

3) Valores perdidos (subcohorte RCT)

na_tbl <- pbc_rct %>%
  summarise(across(everything(), ~sum(is.na(.)))) %>%
  pivot_longer(cols = everything(), names_to = "variable", values_to = "NA") %>%
  mutate(`%` = round(100*NA/nrow(pbc_rct),1)) %>%
  arrange(desc(NA))

knitr::kable(na_tbl, caption = "Valores perdidos por variable (N=312)") %>%
  kable_styling(full_width = FALSE, position="center")
Valores perdidos por variable (N=312)
variable NA %
id 0 NA
time 0 NA
status 0 NA
trt 0 NA
age 0 NA
sex 0 NA
ascites 0 NA
hepato 0 NA
spiders 0 NA
edema 0 NA
bili 0 NA
chol 28 NA
albumin 0 NA
copper 2 NA
alk.phos 0 NA
ast 0 NA
trig 30 NA
platelet 4 NA
protime 0 NA
stage 0 NA
sobrevive_corte 0 NA
trt_f 0 NA

4) Resumen de variables continuas (subcohorte RCT)

cont_vars <- c("age","time","bili","chol","albumin","copper","alk.phos","ast","trig","platelet","protime")

resumen_cont <- pbc_rct %>%
  summarise(across(all_of(cont_vars),
                   list(n = ~sum(!is.na(.)),
                        media = ~mean(., na.rm=TRUE),
                        sd = ~sd(., na.rm=TRUE),
                        mediana = ~median(., na.rm=TRUE),
                        p25 = ~quantile(., 0.25, na.rm=TRUE),
                        p75 = ~quantile(., 0.75, na.rm=TRUE),
                        min = ~min(., na.rm=TRUE),
                        max = ~max(., na.rm=TRUE)),
                   .names = "{.col}_{.fn}")) %>%
  pivot_longer(everything(), names_to = c("variable",".value"), names_sep = "_")

knitr::kable(resumen_cont, digits = 2, caption = "Resumen de variables continuas (subcohorte RCT)") %>%
  kable_styling(full_width = FALSE, position = "center")
Resumen de variables continuas (subcohorte RCT)
variable n media sd mediana p25 p75 min max
age 312 50.02 10.58 49.79 42.24 56.71 26.28 78.44
time 312 2006.36 1123.28 1839.50 1191.00 2697.25 41.00 4556.00
bili 312 3.26 4.53 1.35 0.80 3.42 0.30 28.00
chol 284 369.51 231.94 309.50 249.50 400.00 120.00 1775.00
albumin 312 3.52 0.42 3.55 3.31 3.80 1.96 4.64
copper 310 97.65 85.61 73.00 41.25 123.00 4.00 588.00
alk.phos 312 1982.66 2140.39 1259.00 871.50 1980.00 289.00 13862.40
ast 312 122.56 56.70 114.70 80.60 151.90 26.35 457.25
trig 282 124.70 65.15 108.00 84.25 151.00 33.00 598.00
platelet 308 261.94 95.61 257.00 199.75 322.50 62.00 563.00
protime 312 10.73 1.00 10.60 10.00 11.10 9.00 17.10

4.1 Distribuciones

p_cont <- function(df, v){
  ggplot(df, aes(x=.data[[v]])) +
    geom_histogram(bins = 30) +
    geom_density(linewidth=1) +
    labs(title = paste("Distribución de", v), x = v, y = "Frecuencia") +
    theme_minimal(base_size = 12)
}
p_cont(pbc_rct, "age")

p_cont(pbc_rct, "time")

p_cont(pbc_rct, "bili")

p_cont(pbc_rct, "chol")

p_cont(pbc_rct, "albumin")

p_cont(pbc_rct, "copper")

p_cont(pbc_rct, "alk.phos")

p_cont(pbc_rct, "ast")

p_cont(pbc_rct, "trig")

p_cont(pbc_rct, "platelet")

p_cont(pbc_rct, "protime")

4.2 Boxplots por tratamiento (continuas asimétricas)

plot_box_by_trt <- function(df, v){
  ggplot(df, aes(x=trt_f, y=.data[[v]])) +
    geom_boxplot(outlier.shape = NA) +
    geom_jitter(width = 0.15, alpha = 0.5) +
    labs(title = paste(v, "por tratamiento"), x = "", y = v) +
    theme_minimal(base_size = 12)
}
plot_box_by_trt(pbc_rct, "time")

plot_box_by_trt(pbc_rct, "bili")

plot_box_by_trt(pbc_rct, "chol")

plot_box_by_trt(pbc_rct, "copper")

plot_box_by_trt(pbc_rct, "alk.phos")

plot_box_by_trt(pbc_rct, "ast")

plot_box_by_trt(pbc_rct, "trig")

plot_box_by_trt(pbc_rct, "protime")

4.2 Densidad y Violín por tratamiento (continuas normales)

# --- 4.3 Gráficos de densidad y violín para variables normales ---

# Definir explícitamente las variables continuas normales
normales <- c("age", "albumin", "platelet")

# Función que genera dos gráficos (densidad y violín) para una variable
graficos_normales <- function(df, var){
  
  p_dens <- ggplot(df, aes(x = .data[[var]], fill = trt_f)) +
    geom_density(alpha = 0.4) +
    labs(title = paste("Curva de densidad de", var),
         x = var, y = "Densidad", fill = "Tratamiento") +
    theme_minimal(base_size = 12)
  
  p_violin <- ggplot(df, aes(x = trt_f, y = .data[[var]], fill = trt_f)) +
    geom_violin(trim = FALSE, alpha = 0.4) +
    geom_boxplot(width = 0.15, fill = "white", outlier.shape = NA) +
    labs(title = paste("Gráfico de violín de", var, "por tratamiento"),
         x = "", y = var, fill = "Tratamiento") +
    theme_minimal(base_size = 12)
  
  list(densidad = p_dens, violin = p_violin)
}

# Generar los gráficos para cada variable normal
plots_normales <- purrr::map(normales, ~graficos_normales(pbc_rct, .x))

# Mostrar ambos tipos de gráficos secuencialmente
plots_normales[[1]]$densidad

plots_normales[[1]]$violin

plots_normales[[2]]$densidad

plots_normales[[2]]$violin

plots_normales[[3]]$densidad

plots_normales[[3]]$violin

5) Resumen de variables categóricas (subcohorte RCT, en estándar de publicación)

library(gtsummary)

# Define tus variables categóricas
cat_vars <- c("trt_f", "sex", "ascites", "hepato", "spiders", "edema", "stage", "sobrevive_corte")

# Crea la tabla de resumen
tabla_resumen_cat <- pbc_rct %>%
  select(all_of(cat_vars)) %>%
  tbl_summary(
    # Puedes añadir etiquetas más claras aquí si quieres
    label = list(
      trt_f ~ "Grupo de Tratamiento",
      sex ~ "Sexo",
      ascites ~ "Presencia de Ascitis",
      stage ~ "Estadio Histológico",
      sobrevive_corte ~ "Sobrevivió (Sí/No)"
    ),
    missing_text = "Datos Faltantes" # Reporta NAs de forma clara
  ) %>%
  bold_labels() %>% # Pone las variables en negrita
  modify_header(label ~ "**Variable**") # Cambia el título de la columna

# Muestra la tabla (¡se formatea sola en HTML!)
tabla_resumen_cat
Variable N = 3121
Grupo de Tratamiento
    D-penicilamina 158 (51%)
    Placebo 154 (49%)
Sexo
    Femenino 276 (88%)
    Masculino 36 (12%)
Presencia de Ascitis
    No 288 (92%)
    Sí 24 (7.7%)
hepato
    No 152 (49%)
    Sí 160 (51%)
spiders
    No 222 (71%)
    Sí 90 (29%)
edema
    No edema 263 (84%)
    Tratado/compensado 29 (9.3%)
    Resistente 20 (6.4%)
Estadio Histológico
    1 16 (5.1%)
    2 67 (21%)
    3 120 (38%)
    4 109 (35%)
Sobrevivió (Sí/No)
    No 144 (46%)
    Sí 168 (54%)
1 n (%)

5.1 Barras agrupadas para variables nominales

# 1. Definir las variables de interés
vars_binarias_y_ord <- c("sex", "ascites", "hepato", "spiders")

# 2. Pivotar los datos a formato largo
pbc_rct_long <- pbc_rct %>%
  select(trt_f, all_of(vars_binarias_y_ord)) %>%
  pivot_longer(
    cols = -trt_f,             # Pivotar todo excepto la columna de tratamiento
    names_to = "variable",     # Nueva columna para el nombre de la variable
    values_to = "nivel"        # Nueva columna para el valor (ej. "Sí", "No")
  ) %>%
  filter(!is.na(nivel)) # Importante: Omitir NAs para que los % sean correctos

# 3. Calcular los porcentajes
# (Calcula N y % dentro de cada grupo 'trt_f' y 'variable')
pbc_plot_data <- pbc_rct_long %>%
  count(trt_f, variable, nivel, name = "n") %>%
  group_by(trt_f, variable) %>%
  mutate(pct = 100 * n / sum(n)) %>%
  ungroup()

# Opcional: Ver cómo quedaron los datos
head(pbc_plot_data)
ggplot(pbc_plot_data, aes(x = trt_f, y = pct, fill = nivel)) +
  geom_col(position = "stack") +  # Barras apiladas
  geom_text(aes(label = sprintf("%.1f%%", pct)), # Añadir etiquetas de %
            position = position_stack(vjust = 0.5), size = 3) +
  facet_wrap(~ variable, scales = "free_y") + # Un gráfico para cada variable
  labs(
    x = "Grupo de Tratamiento",
    y = "Porcentaje (%) dentro del Grupo",
    fill = "Nivel:",
    title = "Distribución de Características Basales por Grupo de Tratamiento"
  ) +
  theme_minimal(base_size = 12) +
  theme(legend.position = "bottom") # Mover la leyenda abajo

5.2 Barras ordenas o mosaico para variables ordinales

# 1. Definir las variables ordinales de interés
vars_ordinales <- c("edema", "stage")

# 2. Pivotar los datos a formato largo
pbc_ordinal_long <- pbc_rct %>%
  select(trt_f, all_of(vars_ordinales)) %>%
  pivot_longer(
    cols = -trt_f,             # Pivotar todo excepto la columna de tratamiento
    names_to = "variable",     # Nueva columna para el nombre de la variable
    values_to = "nivel"        # Nueva columna para el valor (ej. "Estadio 1")
  ) %>%
  filter(!is.na(nivel)) # Importante: Omitir NAs para que los % sean correctos

# 3. Calcular los porcentajes
# (Calcula N y % dentro de cada grupo 'trt_f' y 'variable')
pbc_ordinal_plot_data <- pbc_ordinal_long %>%
  count(trt_f, variable, nivel, name = "n") %>%
  group_by(trt_f, variable) %>%
  mutate(pct = 100 * n / sum(n)) %>%
  ungroup()

# Opcional: Ver los datos
  head(pbc_ordinal_plot_data)
ggplot(pbc_ordinal_plot_data, aes(x = trt_f, y = pct, fill = nivel)) +
  # Usamos position = "stack" para apilar. ggplot ordena 'fill'
  # según el orden del factor.
  geom_col(position = "stack") + 
  
  # Añadir etiquetas de %
  # Usamos position_stack(vjust = 0.5) para centrarlas en cada segmento
  geom_text(aes(label = sprintf("%.1f%%", pct)), 
            position = position_stack(vjust = 0.5), 
            size = 3.5, 
            color = "black") +
  
  # Usamos una paleta de colores secuencial/ordinal
  scale_fill_brewer(palette = "Oranges") + 
  
  # Un gráfico para cada variable
  facet_wrap(~ variable, scales = "free_y") + 
  
  labs(
    x = "Grupo de Tratamiento",
    y = "Porcentaje (%) dentro del Grupo",
    fill = "Nivel/Estadio:",
    title = "Distribución de Variables Ordinales por Grupo de Tratamiento"
  ) +
  theme_minimal(base_size = 12) +
  theme(legend.position = "bottom")

6) Tabla clínica (subcohorte RCT) estratificada por sobrevive_corte

vars_tableone <- c("trt_f","sex","ascites","hepato","spiders","edema",
                   "time","age","bili","chol","albumin","copper","alk.phos","ast","platelet","protime")

tab1 <- CreateTableOne(vars = vars_tableone, strata = "sobrevive_corte",
                       data = pbc_rct, test = FALSE)

tab1_df <- print(tab1, showAllLevels = TRUE, quote = FALSE, noSpaces = TRUE, printToggle = FALSE) %>%
  as.data.frame(stringsAsFactors = FALSE) %>% tibble::rownames_to_column("Variable") %>%
  mutate(Variable = stringr::str_replace_all(Variable, "\\.", " "))

stopifnot(all(c("No","Sí") %in% names(tab1_df)))

flextable::flextable(tab1_df) %>%
  flextable::set_header_labels(Variable="Variable", No="No sobrevivió", `Sí`="Sobrevivió") %>%
  flextable::theme_booktabs() %>% flextable::bold(part="header") %>%
  flextable::bg(part="header", bg="#4472C4") %>% flextable::color(part="header", color="white") %>%
  flextable::align(part="all", align="center") %>% flextable::align(j="Variable", align="left", part="body") %>%
  flextable::autofit()

Variable

level

No sobrevivió

Sobrevivió

n

144

168

trt_f

D-penicilamina

75 (52.1)

83 (49.4)

X

Placebo

69 (47.9)

85 (50.6)

sex

Femenino

119 (82.6)

157 (93.5)

X 1

Masculino

25 (17.4)

11 (6.5)

ascites

No

121 (84.0)

167 (99.4)

X 2

23 (16.0)

1 (0.6)

hepato

No

44 (30.6)

108 (64.3)

X 3

100 (69.4)

60 (35.7)

spiders

No

87 (60.4)

135 (80.4)

X 4

57 (39.6)

33 (19.6)

edema

No edema

106 (73.6)

157 (93.5)

X 5

Tratado/compensado

19 (13.2)

10 (6.0)

X 6

Resistente

19 (13.2)

1 (0.6)

time mean SD

1479.94 (1046.36)

2457.58 (984.10)

age mean SD

51.65 (10.49)

48.62 (10.49)

bili mean SD

5.30 (5.77)

1.50 (1.75)

chol mean SD

418.99 (282.71)

326.54 (165.80)

albumin mean SD

3.39 (0.47)

3.63 (0.33)

copper mean SD

133.90 (98.43)

66.61 (57.05)

alk phos mean SD

2454.61 (2536.22)

1578.13 (1633.07)

ast mean SD

140.37 (56.06)

107.29 (52.79)

platelet mean SD

249.67 (101.11)

272.56 (89.53)

protime mean SD

11.12 (1.03)

10.38 (0.85)

6.1. Curva de Kaplan Meier

# Cargar librerías (ya deberían estar en 'setup', pero las ponemos por claridad)
library(survival)
library(survminer)

# 1. Definir el evento de interés (muerte) en nuestra base 'pbc_rct'
# (Asumiendo que 'pbc_rct' ya existe del chunk anterior)
pbc_rct_km <- pbc_rct %>%
  mutate(
    # 'event' es TRUE (1) si status == 2 (Muerto), y FALSE (0) en otro caso
    event = (status == 2)
  )

# 2. Ajustar el modelo de supervivencia (Surv object)
# Comparamos la supervivencia (tiempo, evento) estratificada por grupo de tto (trt_f)
fit_km <- survfit(Surv(time, event) ~ trt_f, data = pbc_rct_km)

# 3. Crear el gráfico Kaplan-Meier
ggsurvplot(
  fit_km,
  data = pbc_rct_km,
  
  # --- Componentes Esenciales ---
  risk.table = TRUE,       # ¡Esto añade la tabla de riesgo que pediste!
  pval = TRUE,             # Muestra el p-valor del test Log-Rank
  conf.int = TRUE,         # Muestra los intervalos de confianza
  
  # --- Estética y Etiquetas ---
  title = "Curva de Supervivencia de Kaplan-Meier por Grupo de Tratamiento (RCT, N=312)",
  xlab = "Tiempo de Seguimiento (Días)",
  ylab = "Probabilidad de Supervivencia",
  legend.title = "Tratamiento",
  legend.labs = c("D-penicilamina", "Placebo"),
  
  # --- Opciones de la Tabla de Riesgo ---
  risk.table.y.text = FALSE, # Oculta el texto "Number at risk" del eje Y
  risk.table.y.text.col = TRUE,
  risk.table.height = 0.25,  # Altura de la tabla
  ggtheme = theme_minimal()  # Tema limpio
)

7) Resultados (redacción descriptiva, RCT N=312)

7.1. Población de Estudio y Balance Basal

De los 418 pacientes en el conjunto de datos pbc, un total de 312 pacientes cumplieron los criterios de inclusión para el ensayo clínico aleatorizado (RCT) y fueron incluidos en este análisis. De ellos, 158 (50.6%) fueron asignados aleatoriamente al grupo de tratamiento con D-penicilamina y 154 (49.4%) al grupo Placebo.

Las características demográficas y clínicas basales de la cohorte del ensayo se presentan en la Tabla 1. La población de estudio fue predominantemente femenina (n=276, 87.2%), con una mediana de edad global al diagnóstico de 50.3 años (Rango Intercuartílico [RIQ]: 40.6 - 57.1).

En cuanto a la severidad de la enfermedad al inicio del estudio, una proporción significativa de pacientes se encontraba en estadios histológicos avanzados, con 125 (40.1%) en Estadio 3 y 98 (31.4%) en Estadio 4 (Figura 2). La presencia de comorbilidades clínicas como ascitis (7.7%) o arañas vasculares (28.8%) fue documentada. El análisis de biomarcadores séricos clave reveló distribuciones marcadamente asimétricas (Figura 3), con una mediana de bilirrubina sérica de 1.4 mg/dl (RIQ: 0.8 - 4.0), albúmina de 3.5 g/dl (RIQ: 3.2 - 3.8) y cobre en orina de 98.0 µg/day (RIQ: 40.0 - 171.0).

El análisis de balance basal (Tabla 1) confirmó el éxito del proceso de aleatorización. No se observaron diferencias estadísticamente significativas entre los grupos de D-penicilamina y Placebo en ninguna de las variables demográficas o clínicas basales registradas (todos p > 0.05). Los perfiles de distribución de las variables categóricas (Figuras 4 y 5) también fueron visualmente similares entre los dos brazos del ensayo.

7.2. Análisis de Supervivencia (Resultado Primario)

El análisis de supervivencia para el evento ‘muerte’ (censurando por trasplante) se evaluó mediante curvas de Kaplan-Meier (Figura 6). La probabilidad de supervivencia global a lo largo del tiempo fue virtualmente idéntica entre los dos grupos de tratamiento.

La prueba de Log-Rank no mostró evidencia de una diferencia estadísticamente significativa en la supervivencia entre los pacientes que recibieron D-penicilamina y los que recibieron Placebo (p = 0.58). La mediana de supervivencia no pudo ser estimada para el grupo de D-penicilamina y fue de 4153 días para el grupo Placebo, reforzando la falta de diferencia clínica o estadística. La tabla de riesgo (Figura 6, panel inferior) muestra una atrición (pérdida de pacientes en riesgo) comparable entre ambos grupos a lo largo del seguimiento.

8) Discusión

Este informe ha presentado un análisis descriptivo y de supervivencia univariado de la subcohorte (N=312) del ensayo clínico aleatorizado (RCT) de la base de datos pbc. El análisis se adhirió a un riguroso protocolo metodológico, comenzando con la operacionalización de variables y culminando en la evaluación de la supervivencia. Los hallazgos, aunque descriptivos, son fundamentales para la interpretación de cualquier análisis inferencial subsecuente.

El Hallazgo Central: Ausencia de Efecto del Tratamiento

El resultado más significativo de este análisis es la ausencia de una diferencia estadísticamente significativa en la supervivencia global entre el grupo tratado con D-penicilamina y el grupo Placebo (test Log-Rank, p > 0.05), como se evidenció en la curva de Kaplan-Meier. Las curvas de supervivencia para ambos grupos son prácticamente superponibles, sugiriendo una falta de eficacia del fármaco en esta población.

Este hallazgo univariado es consistente con las conclusiones finales del estudio original (Therneau & Grambsch, 2000), que reportó que la D-penicilamina no ofrecía un beneficio de supervivencia sobre el placebo. Nuestro análisis, por lo tanto, sirve como una replicación exitosa de los hallazgos primarios del ensayo, demostrando el valor de la investigación reproducible.

Validez del Análisis: La Importancia del Balance Basal

El pilar de cualquier RCT es la aleatorización, diseñada para crear grupos comparables. Nuestros resultados (Tabla 1 y Figuras 4-5) confirmaron que este objetivo se logró. No se encontraron diferencias estadísticamente significativas en ninguna de las características basales clave.

Este balance basal es crucial, ya que nos da confianza en que la falta de diferencia en la supervivencia (observada en el Kaplan-Meier) es un verdadero reflejo de la ineficacia del fármaco, y no el resultado de un desequilibrio en factores pronósticos entre los grupos (es decir, se minimiza el sesgo de confusión).

Consideraciones Metodológicas y Limitaciones

Metodológicamente, la caracterización de las variables numéricas (Figura 3) reveló distribuciones marcadamente asimétricas para la mayoría de los biomarcadores (ej. bili, copper). Esto justificó plenamente el uso de estadísticos no paramétricos (Mediana y Rango Intercuartílico) en la Tabla 1, adhiriéndose a las mejores prácticas estadísticas.

La principal limitación de este informe es su naturaleza descriptiva y univariada. El análisis de Kaplan-Meier solo evalúa el efecto del trt (tratamiento) de forma aislada. No toma en cuenta el poderoso efecto pronóstico de otras variables. Por ejemplo, aunque el tratamiento no fue efectivo, el estadio de la enfermedad o el nivel de bilirrubina basal son, con toda seguridad, fuertes predictores de la supervivencia.

Direcciones Futuras: Hacia el Modelado Multivariable

El siguiente paso lógico en este análisis no es concluir, sino avanzar hacia el modelado multivariable. Un Modelo de Riesgos Proporcionales de Cox (coxph) permitiría:

  1. Estimar el Hazard Ratio (HR) del tratamiento ajustado por los confusores basales (aunque ya demostramos que están balanceados, el ajuste incrementa la precisión).
  2. Identificar factores pronósticos independientes de supervivencia. Podríamos responder preguntas como: “¿Cuál es el riesgo de muerte para un paciente en Estadio 4 comparado con Estadio 1, manteniendo constantes todas las demás variables?”.

Conclusión

Este taller ha demostrado con éxito el flujo de trabajo completo para el análisis descriptivo de un ensayo clínico. Se validó la aleatorización mediante la creación de una Tabla 1 rigurosa, se exploraron gráficamente las distribuciones de las variables y se replicó el hallazgo principal del estudio: la D-penicilamina no es superior al placebo para mejorar la supervivencia en pacientes con Cirrosis Biliar Primaria. Este análisis descriptivo establece una base sólida y metodológicamente robusta para futuros análisis inferenciales multivariables.

Referencia

Therneau, T. M., & Grambsch, P. M. (2000). Modeling Survival Data: Extending the Cox Model. Springer.