LOESS (Locally Estimated Scatterplot Smoothing) y LOWESS (Locally Weighted Scatterplot Smoothing) son métodos no paramétricos para ajustar curvas suaves a datos dispersos.
Ambos métodos: - Son locales: Usan sólo datos cercanos a cada punto - Son no paramétricos: No asumen forma funcional global - Son robustos: Resistentes a valores atípicos (especialmente LOWESS)
La diferencia principal: - LOWESS: Versión original con ponderación robusta - LOESS: Generalización con mayor flexibilidad
Para cada punto \(x_0\), se ajusta un polinomio de grado \(d\) minimizando:
\[ \sum_{i=1}^n w_i(x_0) \left[ y_i - \beta_0 - \beta_1(x_i - x_0) - \cdots - \beta_d(x_i - x_0)^d \right]^2 \]
Donde: - \(w_i(x_0)\) es el peso para la observación \(i\) en \(x_0\) - \(d\) es el grado del polinomio (típicamente 1 o 2)
La función de peso común es la tricúbica:
\[ w_i(x) = \left(1 - \left|\frac{x - x_i}{h(x)}\right|^3\right)^3 \quad \text{para} \quad |x - x_i| < h(x) \]
y \(w_i(x) = 0\) en otro caso, donde: - \(h(x)\) es el ancho de banda local - El ancho de banda controla el grado de suavizado
Span (\(\alpha\)): Proporción de puntos incluidos en cada ventana local \[ h(x) = \alpha \cdot \text{rango}(x) \]
Grado del polinomio (\(d\)):
library(ggplot2)
# Ajuste LOESS básico
set.seed(123)
n <- 200
x <- seq(0, 10, length.out = n)
y_verdadero <- 2*sin(x) + 0.5*x
y_obs <- y_verdadero + rnorm(n, sd = 0.8)
datos <- data.frame(x = x, y = y_obs, y_true = y_verdadero)
Ventajas:
Flexible para formas complejas
No requiere especificación de forma global
Buen balance entre sesgo y varianza
Limitaciones:
Computacionalmente intensivo para grandes datasets
Elección subjetiva de span y grado
Problemas en bordes (boundary effects)
ggplot(datos, aes(x = x)) +
geom_point(aes(y = y), alpha = 0.5, color = "gray50") +
geom_line(aes(y = y_true), color = "red", linewidth = 1) +
labs(title = "Datos Simulados con Relación No Lineal",
subtitle = "Puntos: Datos observados con ruido | Línea roja: Relación verdadera",
y = "y") +
theme_minimal()
# Ajustamos modelos con distinto grado polinomial
fit_lin <- loess(y ~ x, data = datos, span = 0.5, degree = 1)
fit_quad <- loess(y ~ x, data = datos, span = 0.5, degree = 2)
datos$y_hat_linear <- predict(fit_lin)
datos$y_hat_quad <- predict(fit_quad)
ggplot(datos, aes(x = x)) +
geom_point(aes(y = y), alpha = 0.3) +
geom_line(aes(y = y_true), color = "red", linewidth = 1) +
geom_line(aes(y = y_hat_linear), color = "blue", linewidth = 1) +
geom_line(aes(y = y_hat_quad), color = "green", linewidth = 1) +
labs(title = "Comparación de Grados Polinomiales en LOESS (span=0.5)",
subtitle = "Rojo: Verdadero | Azul: Grado 1 (lineal) | Verde: Grado 2 (cuadrático)",
y = "y") +
theme_minimal()
# Añadimos outliers artificiales
datos_out <- datos
outlier_idx <- sample(n, 10)
datos_out$y[outlier_idx] <- datos_out$y[outlier_idx] + sample(c(-3, 3), 10, replace = TRUE)
# Ajuste estándar vs robusto
fit_standard <- loess(y ~ x, data = datos_out, span = 0.5)
fit_robust <- loess(y ~ x, data = datos_out, span = 0.5, family = "symmetric")
datos_out$y_hat_standard <- predict(fit_standard)
datos_out$y_hat_robust <- predict(fit_robust)
ggplot(datos_out, aes(x = x)) +
geom_point(aes(y = y), alpha = 0.5) +
geom_point(data = datos_out[outlier_idx, ], aes(y = y), color = "red", size = 3) +
geom_line(aes(y = y_true), color = "black", linewidth = 1, linetype = "dashed") +
geom_line(aes(y = y_hat_standard), color = "blue", linewidth = 1) +
geom_line(aes(y = y_hat_robust), color = "green", linewidth = 1) +
labs(title = "Comparación de LOWESS Estándar vs Robusto",
subtitle = "Negro: Verdadero | Rojo: Outliers | Azul: Estándar | Verde: Robusto",
y = "y") +
theme_minimal()
datos_out$resid_standard <- datos_out$y - datos_out$y_hat_standard
datos_out$resid_robust <- datos_out$y - datos_out$y_hat_robust
# Residuales vs Ajustados
p1 <- ggplot(datos_out, aes(x = y_hat_standard, y = resid_standard)) +
geom_point() +
geom_hline(yintercept = 0, linetype = "dashed") +
labs(title = "Residuales - Ajuste Estándar") +
theme_minimal()
p2 <- ggplot(datos_out, aes(x = y_hat_robust, y = resid_robust)) +
geom_point() +
geom_hline(yintercept = 0, linetype = "dashed") +
labs(title = "Residuales - Ajuste Robusto") +
theme_minimal()
gridExtra::grid.arrange(p1, p2, ncol = 2)