El modelo de regresión logística supone que la respuesta es un ensayo de Bernoulli (o más generalmente un binomio, pero por simplicidad, lo mantendremos 0-1). Un modelo de supervivencia supone que la respuesta suele ser un momento de evento (de nuevo, hay generalizaciones de esto que omitiremos). Otra forma de decir eso es que las unidades pasan a través de una serie de valores hasta que ocurre un evento. No es que una moneda se arroje discretamente en cada punto. (Eso podría suceder, por supuesto, pero necesitaría un modelo para medidas repetidas, tal vez un GLMM).
Su modelo de regresión logística toma cada muerte como un lanzamiento de moneda que ocurrió a esa edad y salió de colas. Del mismo modo, considera cada dato censurado como un solo lanzamiento de moneda que ocurrió a la edad especificada y surgió cara. El problema aquí es que eso es inconsistente con lo que realmente son los datos.
Aquí hay algunos gráficos de los datos y la salida de los modelos. (Tenga en cuenta que volteo las predicciones del modelo de regresión logística a la predicción de estar vivo para que la línea coincida con la gráfica de densidad condicional).
library(survival)
data(lung)
s = with(lung, Surv(time=time, event=status-1))
summary(sm <- coxph(s~age, data=lung))
# Call:
# coxph(formula = s ~ age, data = lung)
#
# n= 228, number of events= 165
#
# coef exp(coef) se(coef) z Pr(>|z|)
# age 0.018720 1.018897 0.009199 2.035 0.0419 *
# ---
# Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
#
# exp(coef) exp(-coef) lower .95 upper .95
# age 1.019 0.9815 1.001 1.037
#
# Concordance= 0.55 (se = 0.026 )
# Rsquare= 0.018 (max possible= 0.999 )
# Likelihood ratio test= 4.24 on 1 df, p=0.03946
# Wald test = 4.14 on 1 df, p=0.04185
# Score (logrank) test = 4.15 on 1 df, p=0.04154
lung$died = factor(ifelse(lung$status==2, "died", "alive"), levels=c("died","alive"))
summary(lrm <- glm(status-1~age, data=lung, family="binomial"))
# Call:
# glm(formula = status - 1 ~ age, family = "binomial", data = lung)
#
# Deviance Residuals:
# Min 1Q Median 3Q Max
# -1.8543 -1.3109 0.7169 0.8272 1.1097
#
# Coefficients:
# Estimate Std. Error z value Pr(>|z|)
# (Intercept) -1.30949 1.01743 -1.287 0.1981
# age 0.03677 0.01645 2.235 0.0254 *
# ---
# Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
#
# (Dispersion parameter for binomial family taken to be 1)
#
# Null deviance: 268.78 on 227 degrees of freedom
# Residual deviance: 263.71 on 226 degrees of freedom
# AIC: 267.71
#
# Number of Fisher Scoring iterations: 4
windows()
plot(survfit(s~1))
windows()
par(mfrow=c(2,1))
with(lung, spineplot(age, as.factor(status)))
with(lung, cdplot(age, as.factor(status)))
lines(40:80, 1-predict(lrm, newdata=data.frame(age=40:80), type="response"),
col="red")
Puede ser útil considerar una situación en la que los datos fueron apropiados para un análisis de supervivencia o una regresión logística. Imagine un estudio para determinar la probabilidad de que un paciente sea readmitido en el hospital dentro de los 30 días posteriores al alta bajo un nuevo protocolo o estándar de atención. Sin embargo, todos los pacientes son seguidos hasta el reingreso, y no hay censura (esto no es terriblemente realista), por lo que el tiempo exacto para el reingreso podría analizarse con análisis de supervivencia (a saber, un modelo de riesgos proporcionales de Cox aquí). Para simular esta situación, usaré distribuciones exponenciales con tasas de .5 y 1, y usaré el valor 1 como límite para representar 30 días:
set.seed(0775) # this makes the example exactly reproducible
t1 = rexp(50, rate=.5)
t2 = rexp(50, rate=1)
d = data.frame(time=c(t1,t2),
group=rep(c("g1","g2"), each=50),
event=ifelse(c(t1,t2)<1, "yes", "no"))
windows()
plot(with(d, survfit(Surv(time)~group)), col=1:2, mark.time=TRUE)
legend("topright", legend=c("Group 1", "Group 2"), lty=1, col=1:2)
abline(v=1, col="gray")
with(d, table(event, group))
# group
# event g1 g2
# no 29 22
# yes 21 28
summary(glm(event~group, d, family=binomial))$coefficients
# Estimate Std. Error z value Pr(>|z|)
# (Intercept) -0.3227734 0.2865341 -1.126475 0.2599647
# groupg2 0.5639354 0.4040676 1.395646 0.1628210
summary(coxph(Surv(time)~group, d))$coefficients
# coef exp(coef) se(coef) z Pr(>|z|)
# groupg2 0.5841386 1.793445 0.2093571 2.790154 0.005268299
En este caso, vemos que el valor p del modelo de regresión logística ( 0.163
) fue mayor que el valor p de un análisis de supervivencia ( 0.005
). Para explorar más esta idea, podemos extender la simulación para estimar el poder de un análisis de regresión logística versus un análisis de supervivencia, y la probabilidad de que el valor p del modelo de Cox sea menor que el valor p de la regresión logística . También usaré 1.4 como umbral, para no poner en desventaja la regresión logística usando un corte subóptimo:
xs = seq(.1,5,.1)
xs[which.max(pexp(xs,1)-pexp(xs,.5))] # 1.4
set.seed(7458)
plr = vector(length=10000)
psv = vector(length=10000)
for(i in 1:10000){
t1 = rexp(50, rate=.5)
t2 = rexp(50, rate=1)
d = data.frame(time=c(t1,t2), group=rep(c("g1", "g2"), each=50),
event=ifelse(c(t1,t2)<1.4, "yes", "no"))
plr[i] = summary(glm(event~group, d, family=binomial))$coefficients[2,4]
psv[i] = summary(coxph(Surv(time)~group, d))$coefficients[1,5]
}
## estimated power:
mean(plr<.05) # [1] 0.753
mean(psv<.05) # [1] 0.9253
## probability that p-value from survival analysis < logistic regression:
mean(psv<plr) # [1] 0.8977
Por lo tanto, el poder de la regresión logística es más bajo (aproximadamente 75%) que el análisis de supervivencia (aproximadamente 93%), y el 90% de los valores p del análisis de supervivencia fueron más bajos que los valores p correspondientes de la regresión logística. Tener en cuenta los tiempos de retraso, en lugar de solo menor o mayor que algún umbral, produce más poder estadístico como lo había intuido.