Herramientas · Bioestadística abierta
Curva de Kaplan-Meier, mediana de supervivencia y prueba de log-rank
Pega el tiempo de seguimiento, el indicador de evento y (si las hay) la variable de grupo, y obtén la curva de Kaplan-Meier con su banda de confianza, la mediana de supervivencia, la supervivencia en los tiempos que te interesen y la comparación entre dos curvas con la prueba de log-rank.
https://udgca1190.com.mx/herramientas/bioestadistica/kaplan-meier
Este enlace no incluye los datos pegados: son demasiado largos para una URL.
Resultados
Pacientes de la primera curva
15
Eventos de la primera curva
12
Mediana de supervivencia (primera curva)
6
3 a 9
IC 95 % · regla de quantile.survfit
Supervivencia en el primer tiempo (primera curva)
38.9 %
15.3 % a 62.2 %
IC 95 % · Greenwood
Supervivencia en el segundo tiempo (primera curva)
11.7 %
0.9 % a 37.6 %
IC 95 % · Greenwood
Pacientes de la segunda curva
15
Eventos de la segunda curva
12
Mediana de supervivencia (segunda curva)
14
7 a 18
IC 95 % · regla de quantile.survfit
Supervivencia en el primer tiempo (segunda curva)
80.0 %
50.0 % a 93.1 %
IC 95 % · Greenwood
Supervivencia en el segundo tiempo (segunda curva)
44.0 %
18.5 % a 67.1 %
IC 95 % · Greenwood
Log-rank (χ²)
5.174
log-rank, rho = 0
Grados de libertad
1
log-rank, rho = 0
Log-rank (p)
0.023
log-rank, rho = 0
Interpretación
Se compararon dos curvas: en el grupo 0, el número de pacientes fue 15 y el de eventos, 12; en el grupo 1, 15 y 12. En el tiempo 7, la probabilidad estimada de seguir libre del evento fue 38.9 % (IC 95 % 15.3 % a 62.2 %) en el grupo 0 y 80.0 % (IC 95 % 50.0 % a 93.1 %) en el grupo 1. En el tiempo 14, la probabilidad estimada de seguir libre del evento fue 11.7 % (IC 95 % 0.9 % a 37.6 %) en el grupo 0 y 44.0 % (IC 95 % 18.5 % a 67.1 %) en el grupo 1.
Mediana de supervivencia: 6 (IC 95 % 3 a 9) en el grupo 0 y 14 (IC 95 % 7 a 18) en el grupo 1. Es el tiempo en el que cada curva cruza el 50 %; cuando una curva termina por encima de 0.5 la mediana no se alcanza y así debe reportarse, nunca como el último tiempo observado.
Prueba de log-rank: χ² = 5.174 con 1 grado de libertad, p = 0.023. Con α = 0.05 se rechaza la hipótesis nula de que las dos curvas son iguales. El valor p dice que la diferencia observada sería rara si no hubiera ninguna, pero no dice cuán grande es: eso lo dicen las medianas, la supervivencia en los tiempos de interés y la separación de las curvas.
El seguimiento se censuró en 6 de 30 pacientes (20.0 %); el reparto por curva fue grupo 0: 3/15 · grupo 1: 3/15. La censura es informativa solo si no guarda relación con el pronóstico: una pérdida de seguimiento concentrada en los pacientes que iban peor sesga la curva hacia arriba, y conviene decir en el manuscrito por qué se perdió el seguimiento.
Las curvas no se cruzan dentro del seguimiento común, que es la situación en la que la prueba de log-rank y un cociente de riesgos único se leen sin reservas.
Explicación
El análisis de supervivencia responde a una pregunta que una proporción no puede responder: cuánto tarda en ocurrir algo. El «evento» no tiene que ser la muerte; puede ser la defervescencia, el alta, la recaída, la seroconversión o el fracaso de un catéter. Lo que caracteriza a estos datos es la censura: al cerrar el estudio hay pacientes que todavía no han presentado el evento, y otros que se perdieron antes de presentarlo. Esos pacientes no son fracasos ni éxitos: aportan la información de que estuvieron libres del evento hasta el día en que dejamos de verlos, y descartarlos sesgaría el resultado.
El estimador de Kaplan-Meier (1958) aprovecha esa información. En cada tiempo en el que ocurre al menos un evento calcula la probabilidad condicional de pasarlo sin evento, 1 − dᵢ/nᵢ, donde nᵢ son los pacientes todavía en riesgo justo antes de ese momento, y multiplica esas probabilidades una tras otra. El resultado es la curva escalonada: baja solo cuando hay eventos y se mantiene plana cuando solo hay censuras, aunque cada censura reduzca el número en riesgo y haga que los escalones siguientes sean más grandes. Por eso la tabla de pacientes en riesgo que va debajo de la gráfica no es decorativa: la cola de la curva, donde quedan dos o tres personas, es la parte menos fiable y la que más engaña a simple vista.
La anchura de la banda de confianza sale de la fórmula de Greenwood (1926), que acumula la incertidumbre de cada escalón. Se calcula en la escala logarítmica y de ahí se lleva a la escala de la supervivencia: por omisión con la transformación log-log de Kalbfleisch y Prentice, que garantiza que los límites se queden entre 0 y 1 y se comporta mejor en las colas, y como alternativa con la escala log, que es la que R usa por omisión y la que aparece en muchos manuales. Es la misma diferencia que en R distingue `conf.type = "log-log"` de `conf.type = "log"`, y por eso el selector viaja al código.
La mediana de supervivencia es el tiempo en el que la curva cruza el 50 %. Se prefiere a la media porque no depende de la cola censurada, y puede no existir: si la curva termina por encima de 0.5, la mediana «no se alcanza» y así hay que reportarla, nunca como el último tiempo observado. Su intervalo de confianza sigue el criterio de Brookmeyer y Crowley (1982): se aplica la misma regla a las bandas inferior y superior, de modo que el límite inferior sale de la banda que baja más deprisa. Cuando la curva se queda exactamente en 0.5 durante un tramo, la mediana es el punto medio de ese tramo; es la regla de `quantile.survfit`, y esta calculadora la reproduce paso por paso.
La prueba de log-rank (Mantel 1966; Peto y Peto 1972) compara dos curvas completas, no dos porcentajes en un momento concreto. En cada tiempo de evento cuenta cuántos eventos habría cabido esperar en un grupo si las dos curvas fueran iguales, acumula la diferencia entre lo observado y lo esperado y la convierte en un χ² con un grado de libertad. Tiene una condición de lectura importante: supone que la diferencia entre los grupos va en la misma dirección todo el tiempo. Si las curvas se cruzan, el log-rank pierde poder y un solo cociente de riesgos deja de resumir lo que pasa; en ese caso conviene comparar la supervivencia en tiempos concretos y decirlo en el manuscrito.
Ecuaciones
- eventos ocurridos exactamente en el tiempo t_i
- pacientes en riesgo justo antes de t_i (los que tienen tiempo mayor o igual que t_i)
- probabilidad estimada de seguir libre del evento en t
- suma de Greenwood acumulada hasta t
- cuantil normal del nivel de confianza elegido
- error estándar en la escala log-log
- error estándar de ln S(t), la raíz de la suma de Greenwood
- mediana de supervivencia
- primer tiempo en el que la curva llega al 50 %
- primer tiempo en el que la curva baja del 50 %
- eventos observados en el primer grupo
- eventos esperados en el primer grupo si las dos curvas fueran iguales
- pacientes del primer grupo en riesgo justo antes de t_i
Código R
# Kaplan-Meier survival curves and the log-rank test - Bioestadistica abierta, UDG-CA-1190
# Runs as is in R, RStudio or webR; prints the results as JSON at the end.
library(survival)
library(jsonlite)
# The three pasted columns, one row per patient:
tiempo <- c(2, 5, 3, 7, 3, 7, 4, 9, 4, 10, 5, 11, 5, 12, 6, 13, 6, 14, 7, 16, 8, 16,
9, 18, 12, 19, 14, 20, 18, 21)
evento <- c(1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1,
0, 1, 1, 0, 0, 0)
grupo <- c(0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 1)
nivel <- 0.95
tipo_ic <- "log-log"
t1 <- 7 # first time of interest; 0 = none asked for
t2 <- 14 # second time of interest; 0 = none asked for
if (length(grupo) == 0) grupo <- rep(0, length(tiempo)) # no group column: a single curve
codigos <- sort(unique(grupo)) # group 1 is the lowest numeric code
d <- data.frame(tiempo, evento, grupo = factor(grupo, levels = codigos))
etiquetas <- levels(d$grupo)
k <- length(etiquetas)
# conf.type is explicit: R defaults to "log", papers usually report "log-log"
sf <- survfit(Surv(tiempo, evento) ~ grupo, data = d, conf.type = tipo_ic, conf.int = nivel)
# survfit drops $strata when the grouping factor has a single level
filas <- if (is.null(sf$strata)) list(seq_along(sf$time)) else split(seq_along(sf$time), rep(1:k, sf$strata))
# Median survival with its CI (Brookmeyer-Crowley; the rule of quantile.survfit:
# first time with S < 0.5, midpoint of the plateau when S is exactly 0.5)
q <- quantile(sf, 0.5)
mediana <- function(g) {
if (g > k) return(c(NA_real_, NA_real_, NA_real_))
if (k == 1) c(q$quantile[[1]], q$lower[[1]], q$upper[[1]]) else c(q$quantile[g, 1], q$lower[g, 1], q$upper[g, 1])
}
# S(t*) with its CI. t = 0 means no time of interest was asked for (S(0) = 1
# is trivial), and past the last observed time of the group S(t) is not
# defined, although extend = TRUE would prolong the curve
s_en <- function(g, t) {
if (g > k || t <= 0 || t > max(d$tiempo[d$grupo == etiquetas[g]])) return(c(NA_real_, NA_real_, NA_real_))
s <- summary(sf, times = t, extend = TRUE)
j <- if (is.null(s$strata)) 1 else which(as.integer(s$strata) == g)
c(s$surv[j], s$lower[j], s$upper[j])
}
# Log-rank test (rho = 0, Mantel-Haenszel). It needs two groups, and it is NOT
# defined when the variance of the statistic is 0: survdiff then either stops
# with a singular system (every event tied at one time, or no group at risk
# when the events happen) or reports "on 0 degrees of freedom" with chisq = 0.
# In both situations chi2, the degrees of freedom and p are NA, not 0 and 1.
chi2 <- NA_real_; gl <- NA_real_; p <- NA_real_
if (k == 2) {
lr <- tryCatch(survdiff(Surv(tiempo, evento) ~ grupo, data = d, rho = 0), error = function(err) NULL)
if (!is.null(lr) && lr$var[1, 1] > 0) {
chi2 <- lr$chisq
gl <- length(lr$n) - 1
p <- pchisq(chi2, gl, lower.tail = FALSE)
}
}
n_de <- function(g) if (g > k) NA_real_ else sum(d$grupo == etiquetas[g])
ev_de <- function(g) if (g > k) NA_real_ else sum(d$evento[d$grupo == etiquetas[g]])
# Life table of one group, as survfit reports it (std.err is the SE of log S)
col <- function(g, campo) if (g > k) numeric(0) else sf[[campo]][filas[[g]]]
res <- list(n_1 = n_de(1), eventos_1 = ev_de(1), mediana_1 = mediana(1),
s_t1_1 = s_en(1, t1), s_t2_1 = s_en(1, t2),
n_2 = n_de(2), eventos_2 = ev_de(2), mediana_2 = mediana(2),
s_t1_2 = s_en(2, t1), s_t2_2 = s_en(2, t2),
chi2_logrank = chi2, gl = gl, p_logrank = p,
t_1 = I(col(1, "time")), riesgo_1 = I(col(1, "n.risk")), ev_1 = I(col(1, "n.event")),
cens_1 = I(col(1, "n.censor")), s_1 = I(col(1, "surv")), ee_1 = I(col(1, "std.err")),
lo_1 = I(col(1, "lower")), hi_1 = I(col(1, "upper")),
t_2 = I(col(2, "time")), riesgo_2 = I(col(2, "n.risk")), ev_2 = I(col(2, "n.event")),
cens_2 = I(col(2, "n.censor")), s_2 = I(col(2, "surv")), ee_2 = I(col(2, "std.err")),
lo_2 = I(col(2, "lower")), hi_2 = I(col(2, "upper")))
cat(toJSON(res, auto_unbox = TRUE, digits = NA))
# Equivalent in RStudio: survminer::ggsurvplot(sf, risk.table = TRUE)
Este es el mismo código que valida la calculadora: cópialo y ejecútalo en R o RStudio para reproducir el resultado.
La verificación con R dentro del navegador llegará en una próxima versión; mientras tanto, copia el código y ejecútalo en RStudio.
Métodos para manuscrito
Las funciones de supervivencia se estimaron con el método de Kaplan-Meier [1] con IC 95 % basados en la fórmula de Greenwood [2] en la escala log-log de Kalbfleisch y Prentice [6]; la mediana y su IC siguen a Brookmeyer y Crowley [5] y las curvas se compararon con la prueba de log-rank [3,4]. Los cálculos se realizaron con la calculadora «Kaplan-Meier y log-rank» de Bioestadística abierta (Cuerpo Académico UDG-CA-1190, https://udgca1190.com.mx/herramientas/bioestadistica/kaplan-meier), verificada contra el paquete survival de R [7] (survfit, quantile.survfit y survdiff).
Párrafo listo para la sección de Métodos; los números entre corchetes remiten a la lista de referencias.
Referencias
- 01 Kaplan EL, Meier P. Nonparametric estimation from incomplete observations. Journal of the American Statistical Association. 1958;53(282):457–481. doi:10.1080/01621459.1958.10501452 Fuente original
- 02 Greenwood M. The natural duration of cancer. Reports on Public Health and Medical Subjects No. 33. London: His Majesty's Stationery Office; 1926. Fuente original
- 03 Mantel N. Evaluation of survival data and two new rank order statistics arising in its consideration. Cancer Chemotherapy Reports. 1966;50(3):163–170. PMID: 5910392 Fuente original
- 04 Peto R, Peto J. Asymptotically efficient rank invariant test procedures. Journal of the Royal Statistical Society. Series A (General). 1972;135(2):185–207. doi:10.2307/2344317 Fuente original
- 05 Brookmeyer R, Crowley J. A confidence interval for the median survival time. Biometrics. 1982;38(1):29–41. doi:10.2307/2530286 Fuente original
- 06 Kalbfleisch JD, Prentice RL. The Statistical Analysis of Failure Time Data. 2.ª ed. Hoboken, NJ: John Wiley & Sons; 2002. doi:10.1002/9781118032985 Complementaria
- 07 Therneau TM. A Package for Survival Analysis in R. R package survival version 3.8-6. CRAN; 2026. Complementaria
- 08 Bland JM, Altman DG. Survival probabilities (the Kaplan-Meier method). BMJ. 1998;317(7172):1572–1580. doi:10.1136/bmj.317.7172.1572 PMID: 9836663 Lectura didáctica
- 09 Bland JM, Altman DG. The logrank test. BMJ. 2004;328(7447):1073. doi:10.1136/bmj.328.7447.1073 PMID: 15117797 Lectura didáctica