Mi problema : recientemente conocí a un estadístico que me informó que las splines solo son útiles para explorar datos y están sujetas a un sobreajuste, por lo que no son útiles en la predicción. Prefería explorar con polinomios simples ... Como soy un gran admirador de las splines, y esto va en contra de mi intuición, estoy interesado en descubrir qué tan válidos son estos argumentos, y si hay un gran grupo de anti-spline- activistas por ahí?
Antecedentes : cuando creo mis modelos, trato de seguir a Frank Harrell, Estrategias de modelado de regresión (1). Argumenta que las splines cúbicas restringidas son una herramienta válida para explorar variables continuas. También argumenta que los polinomios son pobres para modelar ciertas relaciones como umbrales, logarítmicos (2). Para probar la linealidad del modelo, sugiere una prueba ANOVA para la spline:
Busqué en Google para sobreajustar con splines, pero no encontré que fuera muy útil (aparte de las advertencias generales sobre no usar demasiados nudos). En este foro parece haber una preferencia por el modelado de splines, Kolassa , Harrell , gung .
Encontré una publicación de blog sobre polinomios, el demonio del sobreajuste que habla sobre la predicción de polinomios. La publicación termina con estos comentarios:
Hasta cierto punto, los ejemplos presentados aquí son trampas: se sabe que la regresión polinómica es altamente no robusta. Mucho mejor en la práctica es usar splines en lugar de polinomios.
Ahora esto me llevó a comprobar cómo funcionarían las splines con el ejemplo:
library(rms)
p4 <- poly(1:100, degree=4)
true4 <- p4 %*% c(1,2,-6,9)
days <- 1:70
set.seed(7987)
noise4 <- true4 + rnorm(100, sd=.5)
reg.n4.4 <- lm(noise4[1:70] ~ poly(days, 4))
reg.n4.4ns <- lm(noise4[1:70] ~ ns(days,4))
dd <- datadist(noise4[1:70], days)
options("datadist" = "dd")
reg.n4.4rcs_ols <- ols(noise4[1:70] ~ rcs(days,5))
plot(1:100, noise4)
nd <- data.frame(days=1:100)
lines(1:100, predict(reg.n4.4, newdata=nd), col="orange", lwd=3)
lines(1:100, predict(reg.n4.4ns, newdata=nd), col="red", lwd=3)
lines(1:100, predict(reg.n4.4rcs_ols, newdata=nd), col="darkblue", lwd=3)
legend("top", fill=c("orange", "red","darkblue"),
legend=c("Poly", "Natural splines", "RCS - ols"))
Da la siguiente imagen:
En conclusión, no he encontrado mucho que me convenza de reconsiderar splines, ¿qué me estoy perdiendo?
- FE Harrell, Estrategias de modelado de regresión: con aplicaciones a modelos lineales, regresión logística y análisis de supervivencia, reimpresión de tapa blanda de tapa dura 1ª ed. 2001. Springer, 2010.
- FE Harrell, KL Lee y BG Pollock, "Modelos de regresión en estudios clínicos: determinación de las relaciones entre predictores y respuesta", JNCI J Natl Cancer Inst, vol. 80, no. 15, págs. 1198–1202, octubre de 1988.
Actualizar
Los comentarios me hicieron preguntarme qué sucede dentro del intervalo de datos pero con curvas incómodas. En la mayoría de las situaciones, no salgo del límite de datos, como lo indica el ejemplo anterior. No estoy seguro de que esto califique como predicción ...
De todos modos, aquí hay un ejemplo en el que creo una línea más compleja que no se puede traducir a un polinomio. Como la mayoría de las observaciones están en el centro de los datos, traté de simular eso también:
library(rms)
cmplx_line <- 1:200/10
cmplx_line <- cmplx_line + 0.05*(cmplx_line - quantile(cmplx_line, .7))^2
cmplx_line <- cmplx_line - 0.06*(cmplx_line - quantile(cmplx_line, .3))^2
center <- (length(cmplx_line)/4*2):(length(cmplx_line)/4*3)
cmplx_line[center] <- cmplx_line[center] +
dnorm(6*(1:length(center)-length(center)/2)/length(center))*10
ds <- data.frame(cmplx_line, x=1:200)
days <- 1:140/2
set.seed(1234)
sample <- round(rnorm(600, mean=100, 60))
sample <- sample[sample <= max(ds$x) &
sample >= min(ds$x)]
sample_ds <- ds[sample, ]
sample_ds$noise4 <- sample_ds$cmplx_line + rnorm(nrow(sample_ds), sd=2)
reg.n4.4 <- lm(noise4 ~ poly(x, 6), data=sample_ds)
dd <- datadist(sample_ds)
options("datadist" = "dd")
reg.n4.4rcs_ols <- ols(noise4 ~ rcs(x, 7), data=sample_ds)
AIC(reg.n4.4)
plot(sample_ds$x, sample_ds$noise4, col="#AAAAAA")
lines(x=ds$x, y=ds$cmplx_line, lwd=3, col="black", lty=4)
nd <- data.frame(x=ds$x)
lines(ds$x, predict(reg.n4.4, newdata=ds), col="orange", lwd=3)
lines(ds$x, predict(reg.n4.4rcs_ols, newdata=ds), col="lightblue", lwd=3)
legend("bottomright", fill=c("black", "orange","lightblue"),
legend=c("True line", "Poly", "RCS - ols"), inset=.05)
Esto da la siguiente trama:
Actualización 2
Desde esta publicación, publiqué un artículo que analiza la no linealidad de la edad en un gran conjunto de datos. El suplemento compara diferentes métodos y he escrito una publicación de blog al respecto .