2  Análisis Exploratorio de Series de Tiempo

En este capítulo se introduce la regresión lineal múltiple en el contexto de series de tiempo, así como métodos de selección de modelos y técnicas de análisis exploratorio. Se consideran el tratamiento de series no estacionarias (por ejemplo, mediante detrending o diferenciación), la estabilización de varianza y el suavizamiento no paramétrico.

2.1 Regresión clásica en series de tiempo

Se asume una serie dependiente \(x_t\) influida por series independientes \(z_{t1}, z_{t2}, \ldots, z_{tq}\):

\[ x_t = \beta_0 + \beta_1 z_{t1} + \cdots + \beta_q z_{tq} + w_t, \]

donde \(w_t\) es ruido iid normal con media cero y varianza \(\sigma_w^2\).

2.1.0.1 Ejemplo 2.1: Estimación de tendencia lineal

Se considera la serie del precio del pollo en EE.UU. (2001–2016). Modelo:

\[ x_t = \beta_0 + \beta_1 z_t + w_t, \quad z_t = 2001\frac{7}{12}, \ldots, 2016\frac{6}{12}. \]

Estimadores OLS:

\[ \hat{\beta}_1 = \frac{\sum_{t=1}^n (x_t - \bar{x})(z_t - \bar{z})}{\sum_{t=1}^n (z_t - \bar{z})^2}, \quad \hat{\beta}_0 = \bar{x} - \hat{\beta}_1 \bar{z}. \]

library(astsa)

summary(fit <- lm(chicken ~ time(chicken), na.action=NULL))

Call:
lm(formula = chicken ~ time(chicken), na.action = NULL)

Residuals:
    Min      1Q  Median      3Q     Max 
-8.7411 -3.4730  0.8251  2.7738 11.5804 

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
(Intercept)   -7.131e+03  1.624e+02  -43.91   <2e-16 ***
time(chicken)  3.592e+00  8.084e-02   44.43   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 4.696 on 178 degrees of freedom
Multiple R-squared:  0.9173,    Adjusted R-squared:  0.9168 
F-statistic:  1974 on 1 and 178 DF,  p-value: < 2.2e-16
plot(chicken, ylab="cents per pound")
abline(fit)

2.1.1 Forma matricial del modelo

Definiendo \(z_t = (1, z_{t1}, \ldots, z_{tq})'\) y \(\beta = (\beta_0,\ldots,\beta_q)'\):

\[ x_t = \beta' z_t + w_t \]

El error cuadrático a minimizar es:

\[ Q = \sum_{t=1}^n (x_t - \beta' z_t)^2 \]

Las ecuaciones normales:

\[ \left(\sum_{t=1}^n z_t z_t' \right)\hat{\beta} = \sum_{t=1}^n z_t x_t \]

con solución

\[ \hat{\beta} = \left(\sum_{t=1}^n z_t z_t' \right)^{-1} \sum_{t=1}^n z_t x_t \]

El SSE minimizado:

\[ SSE = \sum_{t=1}^n (x_t - \hat{\beta}' z_t)^2 \]

2.1.2 Propiedades de los estimadores OLS

  • Insesgados: \(\mathbb{E}(\hat{\beta}) = \beta\).
  • Varianza mínima entre estimadores lineales insesgados.
  • Si \(w_t \sim N(0,\sigma_w^2)\):

\[ \operatorname{cov}(\hat{\beta}) = \sigma_w^2 C, \quad C = \left(\sum_{t=1}^n z_t z_t'\right)^{-1} \]

Estimador insesgado de \(\sigma_w^2\):

\[ s_w^2 = MSE = \frac{SSE}{n-(q+1)} \]

Prueba t:

\[ t = \frac{\hat{\beta}_i - \beta_i}{s_w \sqrt{c_{ii}}} \]

2.1.3 Pruebas de hipótesis y ANOVA

Modelo reducido con \(r<q\) variables:

\[ x_t = \beta_0 + \beta_1 z_{t1} + \cdots + \beta_r z_{tr} + w_t \]

Hipótesis nula: \(H_0:\beta_{r+1}=\cdots=\beta_q=0\).

Estadístico F:

\[ F = \frac{(SSE_r - SSE)/(q-r)}{SSE/(n-q-1)} \]

Coeficiente de determinación:

\[ R^2 = \frac{SSE_0 - SSE}{SSE_0}, \quad SSE_0 = \sum_{t=1}^n (x_t - \bar{x})^2 \]

2.1.4 Selección de modelos: Criterios de información

  • AIC:

\[ AIC = \log \hat{\sigma}_k^2 + \frac{n+2k}{n}, \quad \hat{\sigma}_k^2 = \frac{SSE(k)}{n} \]

  • AICc:

\[ AICc = \log \hat{\sigma}_k^2 + \frac{n+k}{n-k-2} \]

  • BIC:

\[ BIC = \log \hat{\sigma}_k^2 + \frac{k\log n}{n} \]

2.1.4.1 Ejemplo: Contaminación, temperatura y mortalidad

Se estudian los efectos de temperatura (\(T_t\)) y partículas (\(P_t\)) sobre la mortalidad cardiovascular (\(M_t\)). Se proponen cuatro modelos:

\[\begin{align*} M_t &= \beta_0 + \beta_1 t + w_t \tag{2.18}\\ M_t &= \beta_0 + \beta_1 t + \beta_2 (T_t - T.) + w_t \tag{2.19}\\ M_t &= \beta_0 + \beta_1 t + \beta_2 (T_t - T.) + \beta_3 (T_t - T.)^2 + w_t \tag{2.20}\\ M_t &= \beta_0 + \beta_1 t + \beta_2 (T_t - T.) + \beta_3 (T_t - T.)^2 + \beta_4 P_t + w_t \tag{2.21} \end{align*}\]

Mejor modelo (2.21):

\[ \hat{M}_t = 2831.5 - 1.396_{(.10)} t - 0.472_{(.032)}(T_t-74.26) + 0.023_{(.003)}(T_t-74.26)^2 + 0.255_{(.019)} P_t \] ya que:

# Datos y variables (asumimos cmort, tempr, part cargados en el entorno)
library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.2
✔ ggplot2   4.0.0     ✔ tibble    3.3.0
✔ lubridate 1.9.4     ✔ tidyr     1.3.1
✔ purrr     1.1.0     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
# Preparación de covariables
temp  <- tempr - mean(tempr)   # (T_t - T.)
temp2 <- temp^2                # (T_t - T.)^2
trend <- time(cmort)           # t

# Ajuste de los cuatro modelos
m1 <- lm(cmort ~ trend,                       na.action = NULL)                    # (2.18)
m2 <- lm(cmort ~ trend + temp,                na.action = NULL)                    # (2.19)
m3 <- lm(cmort ~ trend + temp + temp2,        na.action = NULL)                    # (2.20)
m4 <- lm(cmort ~ trend + temp + temp2 + part, na.action = NULL)                    # (2.21)

# Cálculo y comparación por BIC
num <- length(cmort)
bic_tbl <- tibble(
  modelo = c("(2.18) Trend",
             "(2.19) Trend + Temp",
             "(2.20) Trend + Temp + Temp^2",
             "(2.21) Trend + Temp + Temp^2 + Part"),
  k      = c(length(coef(m1)),
             length(coef(m2)),
             length(coef(m3)),
             length(coef(m4))),
  BIC    = c(BIC(m1)/num - log(2*pi), 
             BIC(m2)/num - log(2*pi),
             BIC(m3)/num - log(2*pi),
             BIC(m4)/num - log(2*pi))
) %>%
  arrange(BIC)

print(bic_tbl)
# A tibble: 4 × 3
  modelo                                  k   BIC
  <chr>                               <int> <dbl>
1 (2.21) Trend + Temp + Temp^2 + Part     5  4.77
2 (2.20) Trend + Temp + Temp^2            4  5.07
3 (2.19) Trend + Temp                     3  5.17
4 (2.18) Trend                            2  5.40
# Mostrar explícitamente el mejor modelo según BIC (el de menor BIC)
mejor <- bic_tbl %>% slice_min(BIC, n = 1)
cat("Mejor modelo según BIC:", mejor$modelo, "\nBIC:", mejor$BIC, "\n")
Mejor modelo según BIC: (2.21) Trend + Temp + Temp^2 + Part 
BIC: 4.771699 

Ajuste del mejor modelo:

par(mfrow=c(3,1))
plot(cmort, main="Cardiovascular Mortality", xlab="", ylab="")
plot(tempr, main="Temperature", xlab="", ylab="")
plot(part, main="Particulates", xlab="", ylab="")

pairs(cbind(Mortality=cmort, Temperature=tempr, Particulates=part))

temp <- tempr - mean(tempr)
temp2 <- temp^2
trend <- time(cmort)

fit <- lm(cmort ~ trend + temp + temp2 + part, na.action=NULL)
summary(fit)

Call:
lm(formula = cmort ~ trend + temp + temp2 + part, na.action = NULL)

Residuals:
     Min       1Q   Median       3Q      Max 
-19.0760  -4.2153  -0.4878   3.7435  29.2448 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept)  2.831e+03  1.996e+02   14.19  < 2e-16 ***
trend       -1.396e+00  1.010e-01  -13.82  < 2e-16 ***
temp        -4.725e-01  3.162e-02  -14.94  < 2e-16 ***
temp2        2.259e-02  2.827e-03    7.99 9.26e-15 ***
part         2.554e-01  1.886e-02   13.54  < 2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 6.385 on 503 degrees of freedom
Multiple R-squared:  0.5954,    Adjusted R-squared:  0.5922 
F-statistic:   185 on 4 and 503 DF,  p-value: < 2.2e-16
summary(aov(fit))
             Df Sum Sq Mean Sq F value Pr(>F)    
trend         1  10667   10667  261.62 <2e-16 ***
temp          1   8607    8607  211.09 <2e-16 ***
temp2         1   3429    3429   84.09 <2e-16 ***
part          1   7476    7476  183.36 <2e-16 ***
Residuals   503  20508      41                   
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
num <- length(cmort)
AIC(fit)/num - log(2*pi)
[1] 4.721732
BIC(fit)/num - log(2*pi)
[1] 4.771699
AICc <- log(sum(resid(fit)^2)/num) + (num+5)/(num-5-2)
AICc
[1] 4.722062

2.1.4.2 Ejemplo: Regresión con variables rezagadas

Relación entre SOI y Reclutamiento (SOI lidera 6 meses):

\[ R_t = \beta_0 + \beta_1 S_{t-6} + w_t \]

Modelo ajustado:

\[ \hat{R}_t = 65.79 - 44.28_{(2.78)} S_{t-6}, \quad \hat{\sigma}_w = 22.5 \]

fish <- ts.intersect(rec, soiL6=stats::lag(soi,-6), dframe=TRUE)
summary(fit1 <- lm(rec ~ soiL6, data=fish, na.action=NULL))

Call:
lm(formula = rec ~ soiL6, data = fish, na.action = NULL)

Residuals:
    Min      1Q  Median      3Q     Max 
-65.187 -18.234   0.354  16.580  55.790 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)   65.790      1.088   60.47   <2e-16 ***
soiL6        -44.283      2.781  -15.92   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 22.5 on 445 degrees of freedom
Multiple R-squared:  0.3629,    Adjusted R-squared:  0.3615 
F-statistic: 253.5 on 1 and 445 DF,  p-value: < 2.2e-16
library(dynlm)
Loading required package: zoo

Attaching package: 'zoo'
The following objects are masked from 'package:base':

    as.Date, as.Date.numeric
summary(fit2 <- dynlm(rec ~ L(soi,6)))

Time series regression with "ts" data:
Start = 1950(7), End = 1987(9)

Call:
dynlm(formula = rec ~ L(soi, 6))

Residuals:
    Min      1Q  Median      3Q     Max 
-65.187 -18.234   0.354  16.580  55.790 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)   65.790      1.088   60.47   <2e-16 ***
L(soi, 6)    -44.283      2.781  -15.92   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 22.5 on 445 degrees of freedom
Multiple R-squared:  0.3629,    Adjusted R-squared:  0.3615 
F-statistic: 253.5 on 1 and 445 DF,  p-value: < 2.2e-16

2.2 Análisis exploratorio de datos en series de tiempo

En esta sección se estudian técnicas de análisis exploratorio de datos en series de tiempo, con énfasis en la necesidad de estacionariedad para aplicar métodos estadísticos significativos. Se presentan procedimientos para eliminar tendencias, aplicar diferencias, utilizar el operador de rezago, realizar transformaciones, explorar relaciones no lineales mediante matrices de dispersión, y detectar señales periódicas usando regresión.

2.2.1 Estacionariedad y modelo con tendencia

Para realizar inferencia en series de tiempo es crucial que, al menos en un intervalo, las funciones de media y autocovarianza sean estacionarias. Cuando no lo son, se utiliza el modelo con tendencia estacionaria:

\[ x_{t} = \mu_{t} + y_{t} \]

donde \(\mu_{t}\) es la tendencia y \(y_{t}\) es estacionario. Se estima \(\hat{\mu}_{t}\) y se define el residual:

\[ \hat{y}_{t} = x_{t} - \hat{\mu}_{t} \]

2.2.1.1 Ejemplo: Eliminación de tendencia en precios de pollo

Se plantea \(\mu_{t} = \beta_{0} + \beta_{1} t\). Usando mínimos cuadrados se obtiene:

\[ \hat{\mu}_{t} = -7131 + 3.59 t \]

La serie sin tendencia resulta:

\[ \hat{y}_{t} = x_{t} + 7131 - 3.59 t \]

fit <- lm(chicken ~ time(chicken), na.action = NULL)
par(mfrow = c(2,1))
plot(resid(fit), type="o", main="detrended")
plot(diff(chicken), type="o", main="first difference")

par(mfrow = c(3,1))
acf(chicken, 48, main="chicken")
acf(resid(fit), 48, main="detrended")
acf(diff(chicken), 48, main="first difference")

2.2.2 Diferenciación y caminata aleatoria con drift

Si la tendencia es estocástica:

\[ \mu_{t} = \delta + \mu_{t-1} + w_{t} \]

al diferenciar se obtiene:

\[ x_{t} - x_{t-1} = \delta + w_{t} + y_{t} - y_{t-1} \]

Lo que implica estacionariedad de la serie diferenciada.

La primera diferencia se define como:

\[ \nabla x_{t} = x_{t} - x_{t-1} \]

con notación de operador de rezago:

\[ Bx_{t} = x_{t-1}, \quad B^{k}x_{t} = x_{t-k} \]

Entonces:

\[ \nabla x_{t} = (1-B)x_{t} \]

y la segunda diferencia:

\[ \nabla^{2}x_{t} = (1-B)^{2}x_{t} = x_{t} - 2x_{t-1} + x_{t-2} \]

En general:

\[ \nabla^{d} = (1-B)^{d} \]

2.2.2.1 Ejemplo: Diferenciación de precios de pollo

La primera diferencia elimina el ciclo de 5 años, revelando un ciclo anual en el ACF.

2.2.2.2 Ejemplo: Diferenciación de temperatura global

La serie de temperatura global se asemeja a una caminata aleatoria con drift. La diferencia primera produce estacionariedad y un incremento promedio de 0.008 grados por año.

par(mfrow=c(2,1))
plot(diff(gtemp_both), type="o")
mean(diff(gtemp_both)) 
[1] 0.008554913
acf(diff(gtemp_both), 48)

2.2.3 Transformaciones de series

Para estabilizar varianza se suele aplicar:

\[ y_{t} = \log x_{t} \]

o la transformación Box-Cox:

\[ y_{t} = \begin{cases} \frac{x_{t}^{\lambda} - 1}{\lambda} & \lambda \neq 0 \\ \log x_{t} & \lambda = 0 \end{cases} \]

2.2.3.1 Ejemplo: Varvas glaciares

Las transformaciones logarítmicas reducen la no estacionariedad en varianzas.

par(mfrow=c(2,1))
plot(varve, main="varve", ylab="")
plot(log(varve), main="log(varve)", ylab="")

2.2.4 Matrices de dispersión retardadas

Se utilizan para explorar relaciones lineales o no lineales entre \(x_t\) y \(x_{t-h}\).

lag1.plot(soi, 12)  

lag2.plot(soi, rec, 8)  

2.2.4.1 Ejemplo: Regresión con variables retardadas y variable ficticia

El modelo es:

\[ R_{t} = \beta_{0} + \beta_{1}S_{t-6} + \beta_{2}D_{t-6} + \beta_{3}D_{t-6}S_{t-6} + w_{t} \]

donde \(D_{t}=0\) si \(S_{t}<0\) y \(D_{t}=1\) en otro caso.

dummy <- ifelse(soi < 0, 0, 1)
fish <- ts.intersect(rec, soiL6 = stats::lag(soi,-6), dL6 = stats::lag(dummy,-6), dframe=TRUE)
fit <- lm(rec ~ soiL6*dL6, data=fish, na.action=NULL)
summary(fit)

Call:
lm(formula = rec ~ soiL6 * dL6, data = fish, na.action = NULL)

Residuals:
    Min      1Q  Median      3Q     Max 
-63.291 -15.821   2.224  15.791  61.788 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)   74.479      2.865  25.998  < 2e-16 ***
soiL6        -15.358      7.401  -2.075   0.0386 *  
dL6           -1.139      3.711  -0.307   0.7590    
soiL6:dL6    -51.244      9.523  -5.381  1.2e-07 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 21.84 on 443 degrees of freedom
Multiple R-squared:  0.4024,    Adjusted R-squared:  0.3984 
F-statistic: 99.43 on 3 and 443 DF,  p-value: < 2.2e-16

2.2.4.2 Ejemplo: Detección de señal en ruido

Modelo:

\[ x_{t} = A \cos(2\pi \omega t + \phi) + w_{t}, \quad \omega = 1/50 \]

Usando identidades trigonométricas:

\[ x_{t} = \beta_{1}\cos(2\pi t/50) + \beta_{2}\sin(2\pi t/50) + w_{t} \]

set.seed(90210)
x <- 2*cos(2*pi*1:500/50 + .6*pi) + rnorm(500,0,5)
z1 <- cos(2*pi*1:500/50)
z2 <- sin(2*pi*1:500/50)
fit <- lm(x ~ 0 + z1 + z2)
summary(fit)

Call:
lm(formula = x ~ 0 + z1 + z2)

Residuals:
     Min       1Q   Median       3Q      Max 
-14.8584  -3.8525  -0.3186   3.3487  15.5440 

Coefficients:
   Estimate Std. Error t value Pr(>|t|)    
z1  -0.7442     0.3274  -2.273   0.0235 *  
z2  -1.9949     0.3274  -6.093 2.23e-09 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 5.177 on 498 degrees of freedom
Multiple R-squared:  0.07827,   Adjusted R-squared:  0.07456 
F-statistic: 21.14 on 2 and 498 DF,  p-value: 1.538e-09
par(mfrow=c(2,1))
plot.ts(x)
plot.ts(x, col=8, ylab=expression(hat(x)))
lines(fitted(fit), col=2)

2.3 Suavizamiento en el contexto de series de tiempo

En esta sección se estudian distintas técnicas de suavizamiento en series de tiempo. El objetivo es reducir fluctuaciones de corto plazo y resaltar tendencias o patrones cíclicos, como los relacionados con El Niño. Se presentan varios métodos: promedio móvil, suavizamiento por kernel, lowess, splines de suavizamiento y suavizamiento de una serie en función de otra.

2.3.1 Promedio móvil

Para una serie \(x_t\), el suavizamiento mediante promedio móvil simétrico se define como

\[ m_{t} = \sum_{j=-k}^{k} a_{j} x_{t-j}, \quad a_{j} = a_{-j} \geq 0, \quad \sum_{j=-k}^{k} a_{j} = 1. \]

2.3.1.1 Ejemplo: Promedio móvil

Se suaviza la serie SOI mensual con pesos \(a_{0} = a_{\pm 1} = \cdots = a_{\pm 5} = 1/12\) y \(a_{\pm 6} = 1/24\), con \(k=6\).

# Paneles lado a lado con ggplot2 (facet_wrap): 
# 1) Serie SOI con suavizamiento (promedio móvil simétrico)
# 2) Kernel (vector de pesos) usado en el suavizamiento

library(tidyverse)

# --- 1) Suavizamiento por promedio móvil simétrico ---
wgts <- c(.5, rep(1,11), .5)/12               # pesos: a0=a±1=...=a±5=1/12, a±6=1/24
soif <- stats::filter(soi, sides = 2, filter = wgts)

df_main <- tibble(
  time = as.numeric(time(soi)),
  SOI  = as.numeric(soi),
  SOI_suav = as.numeric(soif)
) |>
  pivot_longer(cols = c(SOI, SOI_suav), names_to = "serie", values_to = "valor") |>
  mutate(
    panel = factor("Serie SOI", levels = c("Serie SOI", "Kernel (pesos)")),
    serie = recode(serie, SOI = "SOI", SOI_suav = "SOI (promedio móvil)")
  )

# --- 2) Vector de pesos como "kernel boxcar" para segundo panel ---
nwgts <- c(rep(0, 20), wgts, rep(0, 20))
df_w <- tibble(
  x = seq_along(nwgts),
  valor = nwgts,
  serie = "Pesos",
  panel = factor("Kernel (pesos)", levels = c("Serie SOI", "Kernel (pesos)"))
)

# --- 3) Unimos para facetear en dos paneles ---
df_all <- bind_rows(
  df_main |> select(x = time, valor, serie, panel),
  df_w
)

# --- 4) Gráfico con facet_wrap (dos paneles, escalas libres) ---
ggplot(df_all, aes(x = x, y = valor, color = serie)) +
  geom_line(linewidth = 0.8, na.rm = TRUE) +
  facet_wrap(~ panel, scales = "free", ncol = 2) +
  scale_color_manual(values = c("SOI" = "black", "SOI (promedio móvil)" = "#1f77b4", "Pesos" = "black")) +
  labs(
    x = NULL, y = NULL,
    title = "SOI con promedio móvil y kernel de pesos",
    color = NULL
  ) +
  theme_minimal(base_size = 12) +
  theme(
    legend.position = "bottom",
    strip.text = element_text(face = "bold")
  )

wgts <- c(.5, rep(1,11), .5)/12
soif <- stats::filter(soi, sides=2, filter=wgts)
plot(soi)
lines(soif, lwd=2, col=4)

Este método elimina el ciclo anual y destaca el ciclo de El Niño.

2.3.2 Suavizamiento por kernel

El suavizamiento por kernel usa funciones de peso para cada observación:

\[ m_{t} = \sum_{i=1}^{n} w_{i}(t) x_{i}, \]

donde

\[ w_{i}(t) = \frac{K\left(\frac{t-i}{b}\right)}{\sum_{j=1}^{n} K\left(\frac{t-j}{b}\right)}, \]

y \(K(\cdot)\) es una función kernel, usualmente normal: \(K(z) = \frac{1}{\sqrt{2\pi}} \exp(-z^2/2)\).

2.3.2.1 Ejemplo: Suavizamiento kernel

Se aplica kernel gaussiano con ancho de banda \(b=1\).

# Kernel smoothing (ksmooth con kernel normal) y función gaussiana
library(tidyverse)

# --- 1) Suavizado de la serie SOI ---
df_soi <- tibble(
  time = as.numeric(time(soi)),
  SOI  = as.numeric(soi)
)

df_ksmooth <- tibble(
  time = as.numeric(time(soi)),
  SOI_suav = ksmooth(time(soi), soi, kernel = "normal", bandwidth = 1)$y
)

df_main <- df_soi |>
  left_join(df_ksmooth, by = "time") |>
  pivot_longer(cols = c(SOI, SOI_suav), names_to = "serie", values_to = "valor") |>
  mutate(
    panel = factor("Serie SOI", levels = c("Serie SOI", "Kernel normal")),
    serie = recode(serie, SOI = "SOI", SOI_suav = "SOI (kernel normal)")
  )

# --- 2) Función gaussiana usada como kernel ---
gauss <- function(x) 1/sqrt(2*pi) * exp(-(x^2)/2)
x <- seq(from = -3, to = 3, by = 0.01)

df_gauss <- tibble(
  x = x,
  valor = gauss(x),
  serie = "Kernel normal",
  panel = factor("Kernel normal", levels = c("Serie SOI", "Kernel normal"))
)

# --- 3) Unimos ambos dataframes ---
df_all <- bind_rows(
  df_main |> rename(x = time),
  df_gauss
)

# --- 4) Gráfico con facet_wrap ---
ggplot(df_all, aes(x = x, y = valor, color = serie)) +
  geom_line(linewidth = 0.8, na.rm = TRUE) +
  facet_wrap(~ panel, scales = "free", ncol = 2) +
  scale_color_manual(values = c("SOI" = "black", "SOI (kernel normal)" = "#1f77b4", "Kernel normal" = "black")) +
  labs(
    x = NULL, y = NULL,
    title = "SOI suavizado con kernel normal y función gaussiana",
    color = NULL
  ) +
  theme_minimal(base_size = 12) +
  theme(
    legend.position = "bottom",
    strip.text = element_text(face = "bold")
  )

plot(soi)
lines(ksmooth(time(soi), soi, "normal", bandwidth=1), lwd=2, col=4)

2.3.3 Suavizamiento lowess

Lowess se basa en regresión de vecinos más cercanos ponderados y robustos. La fracción de vecinos elegida controla el nivel de suavizamiento.

2.3.3.1 Ejemplo: Lowess

Se suaviza SOI para destacar el ciclo de El Niño y la tendencia a largo plazo.

plot(soi)
lines(lowess(soi, f=.05), lwd=2, col=4) # ciclo El Niño
lines(lowess(soi), lty=2, lwd=2, col=2) # tendencia

2.3.4 Splines de suavizamiento

Se minimiza un compromiso entre ajuste y suavidad:

\[ \sum_{t=1}^{n} [x_{t} - m_{t}]^{2} + \lambda \int (m_{t}'' )^{2} dt, \]

donde \(m_t\) es un spline cúbico y \(\lambda\) controla la suavidad.

2.3.4.1 Ejemplo: Splines

Se ajusta spline de suavizamiento con distintos valores de spar.

plot(soi)
lines(smooth.spline(time(soi), soi, spar=.5), lwd=2, col=4)  # ciclo El Niño
lines(smooth.spline(time(soi), soi, spar=1), lty=2, lwd=2, col=2)  # tendencia

2.3.5 Suavizamiento de una serie como función de otra

Además del tiempo, puede suavizarse una serie en función de otra.

2.3.5.1 Ejemplo: Mortalidad y temperatura

Se suaviza mortalidad (\(M_t\)) como función de temperatura (\(T_t\)). El resultado muestra que la mortalidad mínima ocurre alrededor de \(83^\circ F\), siendo mayor en temperaturas bajas que en altas.

plot(tempr, cmort, xlab="Temperature", ylab="Mortality")
lines(lowess(tempr, cmort))