LOESS y LOWESS: Suavizado Local de Datos

LOESS (Locally Estimated Scatterplot Smoothing) y LOWESS (Locally Weighted Scatterplot Smoothing) son métodos no paramétricos para ajustar curvas suaves a datos dispersos.

1. Introducción

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

2. Formulación Matemática

2.1. Regresión Local Ponderada

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)

2.2. Función de Peso

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

2.3. Parámetros Clave

  1. Span (\(\alpha\)): Proporción de puntos incluidos en cada ventana local \[ h(x) = \alpha \cdot \text{rango}(x) \]

  2. Grado del polinomio (\(d\)):

    • Lineal (\(d=1\)): \(\hat{y} = \beta_0 + \beta_1x\)
    • Cuadrático (\(d=2\)): \(\hat{y} = \beta_0 + \beta_1x + \beta_2x^2\)

3. Proceso de Ajuste

  1. Para cada punto \(x_0\):
    1. Calcular distancias a todos los \(x_i\)
    2. Determinar los \(k = \alpha n\) vecinos más cercanos
    3. Calcular pesos \(w_i(x_0)\)
    4. Ajustar regresión ponderada local
    5. Predecir \(\hat{y}(x_0)\)
  2. (Sólo LOWESS) Iteración robusta:
    • Calcular residuos \(r_i = y_i - \hat{y}_i\)
    • Calcular pesos robustos \(w_i^{(r)} = B(r_i/6s)\) donde \(B\) es la función bisquare y \(s\) es la mediana de \(|r_i|\)
    • Repetir ajuste con pesos \(w_i \cdot w_i^{(r)}\)

4. Ejemplo en R

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)