Análisis predictivo de la dinámica vertical GNSS en Wanchaq mediante enfoques ARDL, ARIMAX Y VECM

Chullo Puclla Oscar | Colque Caillahua Percy Elbis

Universidad Nacional de San Antonio Abad del Cusco

Situación Problemática

Cinemática Cortical y Señales No Estacionarias

Dinámica de la Litosfera

La corteza terrestre no es una estructura rígida e inamovible. En realidad, se comporta como un medio elástico que se deforma y “rebota” continuamente debido a dos factores principales: el empuje de las placas tectónicas y las variaciones de peso en la superficie (redistribución de masas). Las estaciones GNSS nos permiten registrar esta deformación tridimensional con precisión milimétrica.

Carga Hidrológica Regional: En la región andina, este fenómeno es especialmente crítico. La acumulación masiva de agua en el sistema de la cuenca amazónica genera una fuerza de carga que deforma físicamente la placa sudamericana, induciendo una oscilación vertical (hundimiento estacional) en la corteza (Bevis et al. 2005).

El Desafío Local: Estación CS01 (Wanchaq)

La estación CS01 en Cusco captura fielmente esta cinemática, pero extraer la señal pura es un reto estadístico complejo:

  • El Ruido de la Altura (H): La componente vertical es intrínsecamente inestable. No presenta un simple error aleatorio, sino un ruido Flicker que complica la estimación de incertidumbres (Williams et al. 2004).

  • Discontinuidades: Los fallos de equipo o cortes de energía generan vacíos (gaps) que introducen sesgos en los algoritmos clásicos de análisis de series temporales (Sun et al. 2023).

Propuesta del Trabajo

Ir más allá del monitoreo tradicional: aplicar modelos predictivos avanzados sobre la componente de altura (H) en Wanchaq y comprender su compleja dinámica estacional no lineal.

Estación CS01-WANCHAQ: movimiento del suelo en Cusco

¿Qué y cómo se mide?

Los receptores GNSS calculan su posición tridimensional rastreando el tiempo de viaje de las señales satelitales. Estos datos brutos se obtienen en el sistema global XYZ (referenciado al centro de masa de la Tierra).

Para su interpretación geofísica, se proyectan a un plano local NEU (North, East, Up), permitiendo medir desplazamientos en una escala milimétrica:

  • Norte (N): Desplazamiento latitudinal. Refleja una tendencia lineal constante producto de la compresión tectónica de la placa Sudamericana.
  • Este (E): Desplazamiento longitudinal. Suele presentar mayor dispersión (ruido local) y un comportamiento horizontal menos lineal.
  • Altura (H / U): Movimiento vertical (subsidiencia o levantamiento). Es el foco del estudio; captura oscilaciones anuales causadas por las variaciones masivas de carga hidrológica (precipitaciones en los Andes).

La estación CS01 de Cusco forma parte de la Red de Operación Continua SIRGAS-CON, la cual establece el marco de referencia geodésico para las Américas.

Preparación de datos

Imputación de datos

Se descartaron métodos clásicos (como la interpolación lineal o medias móviles) debido a que tienden a “aplanar” la varianza y destruir los ciclos naturales de la serie, lo cual es perjudicial para la componente vertical (Altura) que es fuertemente influenciada por la carga hidrológica estacional y térmica.

Se reemplazaron los métodos de interpolación clásicos por el modelo SAITS (Self-Attention-based Imputation for Time Series), modelo respaldado por evaluaciones recientes en escenarios de pérdida severa de datos reales (Poette et al. 2026):

  • Ajuste Multivariado: Aprovecha la correlación simultánea entre el Norte, Este y Altura para inferir datos faltantes.
  • Preservación de Varianza: Reconstruye el ruido y la estacionalidad sin “suavizar” artificialmente la serie (evita sesgos).
  • Manejo de Gaps Largos: Su arquitectura Transformer infiere probabilísticamente huecos de varias semanas consecutivas.
Mostrar código Python (SAITS)
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
from pypots.imputation import SAITS
df = pd.read_csv('Libro1 - copia.csv', sep=';')
# Extraer fechas y convertirlas a datetime
fechas = pd.to_datetime(df['fecha'], format='%d/%m/%Y')
datos_numericos = df.drop(columns=['fecha'])
scaler = StandardScaler()
datos_escalados = scaler.fit_transform(datos_numericos)
n_pasos = datos_escalados.shape[0]
n_variables = datos_escalados.shape[1]
datos_3d = datos_escalados.reshape(1, n_pasos, n_variables)
saits_modelo = SAITS(
    n_steps=n_pasos,
    n_features=n_variables,
    n_layers=1,          
    d_model=64,         
    d_ffn=64,            
    n_heads=4,            
    d_k=64,               
    d_v=64,               
    dropout=0.2,          
    epochs=150             
)
dataset_para_saits = {"X": datos_3d}
saits_modelo.fit(dataset_para_saits)
resultado_imputacion = saits_modelo.impute(dataset_para_saits)
datos_imputados_escalados = resultado_imputacion.reshape(n_pasos, n_variables)
datos_finales = scaler.inverse_transform(datos_imputados_escalados)
df_imputado = pd.DataFrame(datos_finales, columns=datos_numericos.columns)
df_imputado.insert(0, 'fecha', fechas)
fig, axes = plt.subplots(3, 1, figsize=(12, 10), sharex=True)
axes[0].plot(df_imputado['fecha'], df_imputado['north'], color='royalblue', linewidth=1.5, marker='o', markersize=3)
axes[0].set_title('Series de Tiempo GPS (Imputación Deep Learning: SAITS)', fontsize=14, fontweight='bold')
axes[0].set_ylabel('North (m)', fontsize=12)
axes[0].grid(True, linestyle='--', alpha=0.6)
axes[1].plot(df_imputado['fecha'], df_imputado['east'], color='crimson', linewidth=1.5, marker='o', markersize=3)
axes[1].set_ylabel('East (m)', fontsize=12)
axes[1].grid(True, linestyle='--', alpha=0.6)
axes[2].plot(df_imputado['fecha'], df_imputado['height'], color='forestgreen', linewidth=1.5, marker='o', markersize=3)
axes[2].set_ylabel('Height (m)', fontsize=12)
axes[2].set_xlabel('Fecha', fontsize=12)
axes[2].grid(True, linestyle='--', alpha=0.6)
plt.tight_layout()
plt.show()

Resultados

División de datos

Partición Cronológica

La naturaleza cronológica de los datos GNSS exige una división que preserve el orden temporal. Dado que las observaciones presentan una periodicidad semanal, se ha definido un esquema de partición que permita capturar la tendencia y estacionalidad a largo plazo en el entrenamiento, reservando el periodo más reciente para una evaluación ciega de la capacidad predictiva.

Se ha optado por una división técnica de 85/15:

  • 85% Entrenamiento (Train): Garantiza la captura robusta de la tendencia tectónica.
  • 15% Prueba (Test): Validación out-of-sample en un escenario futuro no visto por el modelo.
Código
library(tidyverse)
library(urca)
gnss <- read.csv("GNSS_NEU.csv", sep=";") # Datos
gnss$fecha <- as.Date(gnss$fecha)
ts_datos <- gnss %>% dplyr::select(north, east, height) %>% as.data.frame()
n_total <- nrow(ts_datos)
n_train <- floor(0.85 * n_total)
train_data <- ts_datos[1:n_train, ]
test_data  <- ts_datos[(n_train + 1):n_total, ]
fecha_train <- gnss$fecha[1:n_train]
fecha_test  <- gnss$fecha[(n_train + 1):n_total]

cat(paste0(
  "Resumen de división:\n",
  "- Entrenamiento (Train): ", nrow(train_data), " observaciones\n",
  "- Prueba (Test):         ", nrow(test_data), " observaciones"
))
Resumen de división:
- Entrenamiento (Train): 326 observaciones
- Prueba (Test):         58 observaciones

¿Las series son estacionarias?

ACF - PACF

Código
par(mfrow = c(3, 2), mar = c(3, 5, 3, 1)) 
variables <- c("north", "east", "height")
for (i in 1:3) {
  var <- variables[i]
  acf(train_data[[var]], lag.max = 50, main = "", col = "darkblue")
  if (i == 1) title(main = "ACF", cex.main = 1.5) 
  mtext(toupper(var), side = 2, line = 3, font = 2, cex = 1.1)
  pacf(train_data[[var]], lag.max = 50, main = "", col = "darkred")
  if (i == 1) title(main = "PACF", cex.main = 1.5)
}

Test de raiz unitaria

# North
Value of test-statistic is: -2.507 8.9308 4.0031 

Critical values for test statistics: 
      1pct  5pct 10pct
tau3 -3.98 -3.42 -3.13
phi2  6.15  4.71  4.05
phi3  8.34  6.30  5.36
# East
Value of test-statistic is: -4.4704 6.8694 9.9959 

Critical values for test statistics: 
      1pct  5pct 10pct
tau3 -3.98 -3.42 -3.13
phi2  6.15  4.71  4.05
phi3  8.34  6.30  5.36
# Height
Value of test-statistic is: -3.0104 3.0819 4.5844 

Critical values for test statistics: 
      1pct  5pct 10pct
tau3 -3.98 -3.42 -3.13
phi2  6.15  4.71  4.05
phi3  8.34  6.30  5.36

\[H_0: \text{No es estacionario}\]

A un 5%:

  • North: \(-2.507>-3.42\). No se rechaza la \(H_0\).
  • East: \(-4.4704<-3.42\). Se rechaza la \(H_0\). Estacionaria en tendencia.
  • Height: \(-3.0104>-3.42\). No se rechaza la \(H_0\).

Primeras diferencias I(1)

Código
ur.df(diff(train_data$north), type="drift") |> summary()
# North
Value of test-statistic is: -15.7488 124.0157 

Critical values for test statistics: 
      1pct  5pct 10pct
tau2 -3.44 -2.87 -2.57
phi1  6.47  4.61  3.79
Código
ur.df(diff(train_data$height), type="drift") |> summary()
# Height
Value of test-statistic is: -14.4498 104.4034 

Critical values for test statistics: 
      1pct  5pct 10pct
tau2 -3.44 -2.87 -2.57
phi1  6.47  4.61  3.79

Resultados de Estacionariedad (Prueba ADF)

  1. Este: Estacionario en niveles \(\rightarrow I(0)\)
  2. Norte: Estacionario en primera diferencia \(\rightarrow I(1)\)
  3. Altura: Estacionario en primera diferencia \(\rightarrow I(1)\)

El modelo VECM exige que todas las variables sean estrictamente \(I(1)\). Debido al orden de integración mixto de las coordenadas GNSS, el enfoque ARDL es la metodología idónea, ya que permite modelar relaciones a largo plazo combinando series \(I(0)\) e \(I(1)\) sin perder información espacial.

ARDL

AutoRegressive Distributed Lag

El ARDL (Autoregresivo de Rezagos Distribuidos) es un modelo econométrico avanzado que permite estimar la dinámica temporal entre variables (Natsiopoulos y Tzeremes 2022). A diferencia de los modelos clásicos, el ARDL se destaca por dos ventajas fundamentales:

  1. Flexibilidad Matemática:

Es la única metodología que no exige que todas las variables tengan el mismo orden de integración, permitiendo combinar perfectamente variables \(I(0)\) (Este) e \(I(1)\) (Norte y Altura).

  1. Análisis Dual:

Estima simultáneamente la dinámica de corrección a corto plazo y la relación de equilibrio (cointegración) a largo plazo en una sola ecuación.

Estacionalidad y selección del modelo ARDL

Modelando la Carga Hidrológica Continua

La componente vertical (Altura) en GNSS exhibe un ciclo periódico de 52 semanas impulsado por la variación estacional de las precipitaciones en los Andes. Para incorporar esta dinámica en el modelo ARDL, se optó por un enfoque armónico:

  • Eficiencia Paramétrica (Transformada de Fourier): En lugar de crear 51 variables dummy semanales (lo cual consumiría demasiados grados de libertad y causaría sobreajuste), la función de Fourier aproxima el ciclo anual utilizando solo dos términos (sin_anual y cos_anual) con \(K=1\) (Dong et al. 2002).
  • Naturaleza del Fenómeno: Los armónicos seno/coseno modelan transiciones suaves y continuas, lo cual representa fielmente la carga y descarga gradual del acuífero, a diferencia de los saltos abruptos de las variables categóricas.
  • Sinergia con el ARDL: Al introducir estos armónicos como variables exógenas determinísticas, “limpiamos” el efecto estacional. Esto permite que los rezagos (lags) del ARDL se enfoquen exclusivamente en capturar los choques tectónicos y la relación de equilibrio a largo plazo.
Código
library(ARDL)
library(forecast)

# 1. Aislar la estacionalidad (Ciclo anual GNSS)
ts_referencia_train <- ts(train_data$height, frequency = 52)
terminos_fourier_train <- fourier(ts_referencia_train, K = 1) 
train_data$sin_anual <- terminos_fourier_train[, 1]
train_data$cos_anual <- terminos_fourier_train[, 2]

Validación Visual del Ciclo Estacional

La extracción del armónico fundamental (\(K=1\)) valida nuestra hipótesis física:

  • Sincronización: La onda (en rojo) captura con precisión los picos y valles anuales, que coinciden con las temporadas de lluvia y estiaje en Cusco.
  • Separación de Señales: Al aislar esta carga hidrológica determinística, garantizamos que los rezagos del modelo ARDL se concentren exclusivamente en predecir la tendencia tectónica subyacente y el ruido estocástico.
Código
# 1. Crear los términos de Fourier
ts_referencia_train <- ts(train_data$height, frequency = 52)
terminos_fourier_train <- fourier(ts_referencia_train, K = 1) 
# 2. Ajustar el modelo estacional para capturar la "Onda" a escala
modelo_estacional <- lm(train_data$height ~ terminos_fourier_train[, 1] + terminos_fourier_train[, 2])
train_data$onda_fourier <- fitted(modelo_estacional)
if (!"fecha" %in% colnames(train_data)) {
  train_data$fecha <- seq(as.Date("2018-07-25"), by = "7 days", length.out = nrow(train_data))
}
ggplot(train_data, aes(x = fecha)) + 
  # Datos originales de fondo (tenues)
  geom_line(aes(y = height, color = "Deformación Bruta"), linewidth = 0.6, alpha = 0.4) +
  # La onda perfecta al frente (gruesa y llamativa)
  geom_line(aes(y = onda_fourier, color = "Ciclo Anual (Fourier K=1)"), linewidth = 1.2) +
  scale_color_manual(values = c("Deformación Bruta" = "#2c3e50", "Ciclo Anual (Fourier K=1)" = "#e74c3c")) +
  theme_bw() +
  labs(
    title = "Aislamiento de la Estacionalidad",
    subtitle = "Extracción de la carga hidrológica anual mediante Serie de Fourier",
    y = "Coordenada Vertical (m)", 
    x = "Fecha de Entrenamiento",
    color = ""
  ) +
  theme(
    legend.position = "bottom", 
    plot.title = element_text(face = "bold", size = 14),
    plot.subtitle = element_text(face = "italic", color = "gray30"),
    legend.text = element_text(face = "bold", size = 11)
  )

Selección Óptima del Modelo ARDL

Resultados del Algoritmo (Criterio BIC)

La búsqueda algorítmica priorizó la parsimonia para evitar el sobreajuste del ruido GNSS, seleccionando un modelo ARDL(2, 1, 0).

Hallazgos Estadísticos Clave:

  • Memoria de la Serie (AR): La altura depende fuertemente de lo ocurrido en las últimas 2 semanas (L(height, 1) y L(height, 2) altamente significativas, \(p < 0.001\)).
  • Interacción Espacial: La componente East muestra un impacto significativo inmediato y retardado. Curiosamente, la componente North no es estadísticamente significativa para predecir la altura (\(p = 0.208\)).
  • Estacionalidad (Fourier): Los términos sin_anual y cos_anual son significativos, validando la absorción de la carga hidrológica estacional.
  • Ajuste Global: El modelo logra explicar el 88.47% de la varianza en la deformación vertical (\(R^2 = 0.884\)).
Código
# Búsqueda algorítmica del mejor ARDL (Criterio BIC)
seleccion_ardl <- auto_ardl(height ~ east + north | sin_anual + cos_anual, 
                            data = train_data, 
                            max_order = c(4, 4, 4), 
                            selection = "BIC")
mejor_ardl <- seleccion_ardl$best_model
summary(mejor_ardl)

Time series regression with "ts" data:
Start = 3, End = 326

Call:
dynlm::dynlm(formula = full_formula, data = data, start = start, 
    end = end)

Residuals:
      Min        1Q    Median        3Q       Max 
-0.008511 -0.001981 -0.000116  0.001847  0.008385 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)  -0.0002729  0.0003797  -0.719   0.4729    
L(height, 1)  0.6305792  0.0525071  12.009  < 2e-16 ***
L(height, 2)  0.2362290  0.0529271   4.463 1.12e-05 ***
east          0.3086212  0.0957503   3.223   0.0014 ** 
L(east, 1)   -0.4805682  0.0953716  -5.039 7.89e-07 ***
north         0.0292012  0.0231634   1.261   0.2084    
sin_anual     0.0006782  0.0003234   2.097   0.0368 *  
cos_anual     0.0009689  0.0002411   4.019 7.32e-05 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 0.002934 on 316 degrees of freedom
Multiple R-squared:  0.8847,    Adjusted R-squared:  0.8821 
F-statistic: 346.3 on 7 and 316 DF,  p-value: < 2.2e-16

Análisis de Cointegración (Bounds Test)

Equilibrio Tectónico a Largo Plazo

Para determinar si las coordenadas GNSS comparten una relación de equilibrio fundamental a pesar de sus dinámicas individuales, se aplicó la prueba de Límites de Pesaran (Bounds F-test), ideal para variables con órdenes de integración mixtos.

Interpretación de Resultados:

  • F-Statistic Robusto: El valor \(F = 9.78\) es altamente significativo (\(p < 0.001\)), superando el límite superior crítico (\(I(1)\)).
  • Confirmación Física: Se rechaza la hipótesis nula de no cointegración. Esto confirma que el movimiento vertical (Height) y horizontal (East, North) no divergen aleatoriamente en el tiempo, sino que están “acoplados” por el régimen tectónico regional.

La existencia de cointegración justifica matemáticamente la transición hacia un Modelo de Corrección de Errores (ECM) para evaluar a qué velocidad la estación corrige sus desviaciones a corto plazo.

Código
library(sandwich)
library(lmtest)
# 1. Prueba de Cointegración (Bounds Test de Pesaran)
prueba_cointegracion <- bounds_f_test(mejor_ardl, case = 3)
print(prueba_cointegracion)

    Bounds F-test (Wald) for no cointegration

data:  d(height) ~ L(height, 1) + L(east, 1) + north + d(L(height, 1)) +     d(east) + sin_anual + cos_anual
F = 9.7808, p-value = 0.0004708
alternative hypothesis: Possible cointegration
null values:
   k    T 
   2 1000 

Multiplicadores de Largo Plazo y Robustez HAC

Dinámica Tectónica a Largo Plazo

1. Estimación Robusta (HAC): Se aplicó la matriz de varianzas y covarianzas de Newey-West. Esto “blinda” los errores estándar contra la heterocedasticidad y la autocorrelación intrínseca de los datos GNSS, garantizando que las inferencias estadísticas sean rigurosas.

2. Acoplamiento Este-Vertical: Se comprobó que el desplazamiento longitudinal (Este) es el principal motor de la deformación vertical a largo plazo (\(p < 0.05\)).

  • Tasa de Subsidencia: Por cada milímetro que la corteza se desplaza permanentemente hacia el Este, la estación Wanchaq experimenta un hundimiento geológico estimado de 1.29 milímetros.

3. Desacoplamiento del Norte: De manera consistente con la dinámica a corto plazo, el desplazamiento latitudinal (Norte) no ejerce una influencia estadísticamente significativa en la vertical de largo plazo (\(p = 0.286\)).

Código
# 2. Modelo de Corrección de Errores (ECM) con Errores Robustos (HAC)
modelo_ecm <- uecm(mejor_ardl)
ecm_robusto <- coeftest(modelo_ecm, vcov. = NeweyWest(modelo_ecm))

# 3. Multiplicadores de Largo Plazo (Impacto real de la placa tectónica)
multiplicadores_robustos <- multipliers(mejor_ardl, vcov_matrix = NeweyWest(mejor_ardl))
print(multiplicadores_robustos)
         Term     Estimate  Std. Error    t value   Pr(>|t|)
1 (Intercept) -0.002048677 0.002114642 -0.9688056 0.33338309
2        east -1.290972628 0.568321631 -2.2715529 0.02378563
3       north  0.219241738 0.205279630  1.0680151 0.28632893

Desempeño del Modelo: Ajuste In-Sample

Evaluación en Entrenamiento (85%)

El modelo ARDL demuestra una sólida capacidad para replicar la dinámica de deformación vertical histórica:

  • Ajuste Estructural (\(R^2 = 88.21\%\)): El modelo rastrea con precisión la trayectoria real sin sobreajustarse al “ruido blanco” del GNSS, confirmando que la varianza explicada proviene de interacciones geofísicas válidas.
  • Captura de Frecuencias Mixtas: El algoritmo reproduce con éxito tanto las señales de baja frecuencia (macro-tendencia tectónica) como la alta frecuencia (micro-picos y valles de volatilidad hidrológica).
  • Sincronicidad Temporal: A pesar de depender de una memoria temporal de 2 semanas (rezagos), la curva ajustada no presenta un “efecto sombra” o retardo frente a la serie original, indicando una respuesta ágil ante perturbaciones.
Código
# 1. Extraer métricas de precisión
valores_ajustados <- as.numeric(fitted(mejor_ardl))
filas_restantes <- nrow(train_data) - length(valores_ajustados) + 1
valores_reales_train <- train_data$height[filas_restantes:nrow(train_data)]

r_cuadrado <- summary(mejor_ardl)$adj.r.squared

# 2. Gráfica de Ajuste
datos_train_grafica <- train_data[filas_restantes:nrow(train_data), ]
datos_train_grafica$fecha <- fecha_train[filas_restantes:length(fecha_train)]
datos_train_grafica$Pronostico <- valores_ajustados

ggplot(datos_train_grafica, aes(x = fecha)) +
  geom_line(aes(y = height, color = "Altura Real"), linewidth = 0.8, alpha = 0.6) +
  geom_line(aes(y = Pronostico, color = "Ajuste ARDL"), linewidth = 0.8, linetype = "dashed") +
  scale_color_manual(values = c("Altura Real" = "gray60", "Ajuste ARDL" = "darkblue")) +
  theme_minimal() +
  labs(title = "Memoria del Modelo: Ajuste en Datos de Entrenamiento (85%)",
       subtitle = sprintf("Varianza explicada (R-cuadrado Ajustado): %.2f%%", r_cuadrado * 100),
       y = "Desplazamiento Vertical (m)", x = "Fecha", color = "") +
  theme(legend.position = "bottom", plot.title = element_text(face="bold"))

Diagnóstico de Residuos: Validación del Ruido Blanco

Ausencia de Autocorrelación

Para garantizar que el modelo ha extraído toda la información sistemática de la serie y que los estimadores son eficientes, se analizó la estructura de los residuos.

  • Test de Ljung-Box (Lags múltiples): Con un \(p\)-valor de \(0.248\) ( \(> 0.05\)), no se rechaza la hipótesis nula. Esto confirma que no existe autocorrelación global en los primeros 10 rezagos temporales.
  • Test de Breusch-Godfrey (Lag 1): Con un \(p\)-valor de \(0.3649\) ( \(> 0.05\)), se descarta la presencia de correlación serial de primer orden.

Los residuos del modelo se comportan estrictamente como ruido blanco. Esto significa que las variaciones no explicadas por el modelo son perturbaciones puramente aleatorias, garantizando la robustez y validez de las inferencias realizadas.

Código
residuos_ardl <- residuals(mejor_ardl)
Box.test(residuos_ardl, type = "Ljung-Box", lag = 10)

    Box-Ljung test

data:  residuos_ardl
X-squared = 12.582, df = 10, p-value = 0.248
Código
#Box.test(residuos_ardl52, type = "Ljung-Box", lag = 52)
lmtest::bgtest(mejor_ardl)

    Breusch-Godfrey test for serial correlation of order up to 1

data:  mejor_ardl
LM test = 0.82109, df = 1, p-value = 0.3649

Predicción ARDL y Evaluación del Modelo

Una vez validada la estructura del modelo, la cointegración y la limpieza de los residuos, el paso definitivo es medir su capacidad de generalización. Para ello, se proyectó el modelo sobre el 15% de datos recientes (conjunto de prueba), un segmento de información que el algoritmo “nunca ha visto”, lo que garantiza una evaluación sin sesgos por sobreajuste.

Código
library(dplyr)
library(ggplot2)
library(forecast)

# 1. Proyectar la estacionalidad al futuro
terminos_fourier_test <- fourier(ts_referencia_train, K = 1, h = nrow(test_data))
test_data$sin_anual <- as.numeric(terminos_fourier_test[, 1])
test_data$cos_anual <- as.numeric(terminos_fourier_test[, 2])

# 2. EL ARREGLO: Usar bind_rows en lugar de rbind
data_predict <- bind_rows(train_data, test_data) %>%
  mutate(
    height_L1 = dplyr::lag(height, 1),
    height_L2 = dplyr::lag(height, 2),
    east_L1   = dplyr::lag(east, 1)
  )
train_predict <- data_predict[1:nrow(train_data), ]
test_predict  <- data_predict[(nrow(train_data) + 1):nrow(data_predict), ]
# Re-entrenamos la versión LM explícita con los coeficientes correctos
modelo_lm_predict <- lm(height ~ height_L1 + height_L2 + east + east_L1 + north + sin_anual + cos_anual, 
                        data = train_predict)

# 3. Pronóstico y Cálculo de Error
predicciones_test <- predict(modelo_lm_predict, newdata = test_predict)
rmse_test <- sqrt(mean((test_data$height - predicciones_test)^2, na.rm = TRUE))
mae_test <- mean(abs(test_data$height - predicciones_test), na.rm = TRUE)

# 4. Gráfica Final
# --- SOLUCIÓN: Inyectar fechas al test_data ---
if (!"fecha" %in% colnames(test_data)) {
  # Tomamos la última fecha de tu entrenamiento y le sumamos 7 días
  ultima_fecha_train <- max(train_data$fecha)
  test_data$fecha <- seq(ultima_fecha_train + 7, by = "7 days", length.out = nrow(test_data))
}
df_test_grafica <- data.frame(
  fecha = test_data$fecha, 
  Real = test_data$height,
  Pronostico = as.numeric(predicciones_test)
)

ggplot(df_test_grafica, aes(x = fecha)) +
  geom_line(aes(y = Real, color = "Altura Real"), linewidth = 1) +
  geom_line(aes(y = Pronostico, color = "Pronóstico ARDL"), linewidth = 1, linetype = "dashed") +
  scale_color_manual(values = c("Altura Real" = "gray40", "Pronóstico ARDL" = "darkred")) +
  theme_minimal() +
  labs(title = "Predicción en Datos Nuevos (15%)",
       subtitle = sprintf("Precisión de pronóstico: RMSE = %.5f m | MAE = %.5f m", rmse_test, mae_test),
       y = "Desplazamiento Vertical (m)", x = "Fecha", color = "") +
  theme(legend.position = "bottom", plot.title = element_text(face="bold"))

Análisis Dinámico: Respuesta al Impulso (IRF)

Simulando un Sismo o Desplazamiento

Para entender la resiliencia geológica de la estación, simulamos un “shock” súbito (impulso) de una unidad hacia el Este y rastreamos la reacción vertical en el tiempo:

  • Fase 1: Rebote y Corrección (Semanas 1-2): El impacto inicial genera un rebote elástico hacia arriba. Sin embargo, por el acoplamiento negativo a largo plazo, la corteza sobrecorrige en la segunda semana, provocando un hundimiento agudo (subsidencia transitoria).
  • Fase 2: Relajación Viscoelástica (Semanas 3-20): La memoria autorregresiva del modelo (los rezagos de la Altura) entra en acción. Observamos cómo el estrés se disipa gradualmente, estabilizando la estación hacia su nuevo punto de equilibrio.
  • Conclusión Física: El sistema es estable. Tras un evento anómalo horizontal, la corteza en Cusco tarda aproximadamente 15 a 20 semanas (4 a 5 meses) en absorber el impacto y calmar su deformación vertical.
Código
# RESPUESTA AL IMPULSO (MULTIPLICADORES DINÁMICOS) PARA ARDL
coefs <- coef(modelo_lm_predict)
horizonte <- 20
respuestas <- numeric(horizonte)

# Simulamos un "Shock" unitario en el componente ESTE
# Recordando la estructura: Height ~ Height_L1 + Height_L2 + East + East_L1 ...
impulse_value <- 1 

# Calculamos el efecto inmediato y propagado (Recursión)
for (t in 1:horizonte) {
  if (t == 1) {
    # Efecto inmediato: Coeficiente de 'east'
    respuestas[t] <- coefs["east"] * impulse_value
  } else if (t == 2) {
    # Primer rezago: Coeficiente de 'east_L1' + persistencia de la altura
    respuestas[t] <- (coefs["height_L1"] * respuestas[t-1]) + (coefs["east_L1"] * impulse_value)
  } else {
    # Efecto a largo plazo: Solo queda la persistencia (memoria) de la altura
    respuestas[t] <- (coefs["height_L1"] * respuestas[t-1]) + (coefs["height_L2"] * respuestas[t-2])
  }
}
# 4. Graficar el Multiplicador Dinámico (Convirtiendo metros a milímetros)
df_irf_ardl <- data.frame(Semana = 1:horizonte, Impacto = respuestas * 1000)

ggplot(df_irf_ardl, aes(x = Semana, y = Impacto)) +
  geom_line(color = "darkblue", linewidth = 1) +
  geom_point(color = "darkblue", size = 2) +
  geom_hline(yintercept = 0, linetype = "dashed", color = "red", linewidth = 0.8) +
  theme_minimal() +
  labs(title = "Respuesta Dinámica de la Altura ante un Shock Súbito en el Este",
       subtitle = "Función de Respuesta al Impulso (IRF)",
       x = "Semanas después del evento", 
       y = "Respuesta Transitoria en Altura (mm)") +
  theme(plot.title = element_text(face="bold"))

ARIMAX

ARIMAX Multivariante

Construcción de la Matriz Exógena (\(X\))

Para validar la robustez del ARDL, estructuramos un modelo ARIMAX como línea base (benchmark). Este modelo evalúa la deformación vertical asumiendo que los desplazamientos horizontales son choques externos directos.

  • Integración de Señales: La matriz de regresores (xreg) fusiona la cinemática horizontal (Este, Norte) con la carga hidrológica.
  • Control Determinístico: Se inyectan los términos armónicos de Fourier (\(K=1\)) directamente como regresores exógenos.
  • Desactivación SARIMA (seasonal = FALSE): Como la serie de Fourier ya extrajo matemáticamente la onda anual hidrológica perfecta, desactivamos la búsqueda estacional nativa del algoritmo para evitar redundancia algorítmica y sobreparametrización.
Código
library(forecast)
# 1. Preparar las variables externas (La matriz "X" de regresores)
# Unimos el desplazamiento horizontal y las ondas estacionales de Fourier
xreg_train_sarimax <- cbind(
  east = train_data$east,
  north = train_data$north,
  sin_anual = terminos_fourier_train[, 1],
  cos_anual = terminos_fourier_train[, 2]
)

Búsqueda Algorítmica del ARIMAX Óptimo

Selección por Criterio AICc

En lugar de imponer los parámetros autorregresivos y de media móvil manualmente, empleamos el algoritmo iterativo de Hyndman-Khandakar (auto.arima).

El algoritmo optimizado por el criterio BIC seleccionó una estructura parsimoniosa que revela detalles clave sobre la dinámica de la estación:

  • Estructura Estocástica (d=1, q=1): El modelo determinó que la serie requiere una diferenciación (d=1) para estabilizarse, confirmando que la altura “acumula” deformación en el tiempo. El término de media móvil (ma1 = -0.33) indica que el sistema corrige aproximadamente un tercio de los choques aleatorios de la semana anterior.
  • Confirmación de la Cinemática (Este vs. Norte): * El Este es motor: El coeficiente de east (\(0.2388\)) es estadísticamente significativo, ya que su valor es más del doble de su error estándar (\(0.1000\)).
    • El Norte es independiente: De forma consistente con el modelo ARDL, el desplazamiento north no resulta significativo (su error estándar de \(0.1762\) es casi el doble que su coeficiente).
  • Precisión: El modelo logra un Error Cuadrático Medio (RMSE) de apenas \(0.003\) metros (\(3\) mm) en el conjunto de entrenamiento, un nivel de precisión excelente para datos GNSS semanales.
# 2. Búsqueda algorítmica del mejor modelo SARIMAX
# auto.arima evaluará decenas de modelos y elegirá el de menor criterio AICc
modelo_sarimax <- auto.arima(train_data$height, 
                             xreg = xreg_train_sarimax, 
                             seasonal = FALSE,
                             ic="bic"
                             ) # Fourier ya controla la estacionalidad

# 3. Mostrar el resumen del mejor modelo encontrado
summary(modelo_sarimax)
Series: train_data$height 
Regression with ARIMA(0,1,1) errors 

Coefficients:
          ma1    east    north  sin_anual  cos_anual
      -0.3367  0.2388  -0.0939     0.0075     0.0007
s.e.   0.0626  0.1000   0.1762     0.0014     0.0013

sigma^2 = 9.416e-06:  log likelihood = 1421.94
AIC=-2831.88   AICc=-2831.61   BIC=-2809.17

Training set error measures:
                        ME        RMSE         MAE  MPE MAPE      MASE
Training set -6.571751e-05 0.003040132 0.002338452 -Inf  Inf 0.9303668
                   ACF1
Training set 0.02083822

Diagnóstico de Residuos ARIMAX: Ruido Blanco

Validación del Modelo de Contraste

Antes de proyectar hacia el futuro, verificamos que el modelo ARIMA(0,1,1) haya extraído toda la información útil de la serie.

  • Prueba de Ljung-Box: El test arroja un \(p\)-valor de \(0.1221\). Al ser mayor a \(0.05\), no se rechaza la hipótesis nula.
  • Interpretación Visual y Estadística: Los residuos se comportan como ruido blanco puro. El gráfico ACF no muestra correlaciones significativas fuera de las bandas de confianza, y la distribución se aproxima a la normalidad.
  • Conclusión: Las variables exógenas (Física) más el componente de media móvil (Estocástica) modelaron exitosamente la dinámica.
Código
# 1. Preparar la matriz XREG para el futuro (Test Data)
checkresiduals(modelo_sarimax)


    Ljung-Box test

data:  Residuals from Regression with ARIMA(0,1,1) errors
Q* = 14.005, df = 9, p-value = 0.1221

Model df: 1.   Total lags used: 10
Código
# IMPORTANTE: Usamos los datos de prueba que el modelo NUNCA ha visto
xreg_test_arimax <- cbind(
  east = test_data$east,
  north = test_data$north,
  sin_anual = test_data$sin_anual,
  cos_anual = test_data$cos_anual
)
pronostico_arimax <- forecast(modelo_sarimax, xreg = xreg_test_arimax)

rmse_arimax <- sqrt(mean((test_data$height - pronostico_arimax$mean)^2, na.rm = TRUE))
mae_arimax <- mean(abs(test_data$height - pronostico_arimax$mean), na.rm = TRUE)

df_grafica_arimax <- data.frame(
  fecha = test_data$fecha,
  Real = test_data$height,
  Pronostico = as.numeric(pronostico_arimax$mean)
)

Capacidad Predictiva del ARIMAX

Análisis del Pronóstico Out-of-Sample

Evaluamos el desempeño del modelo frente a datos desconocidos (periodo 2025), lo que permite medir su utilidad real como herramienta de alerta temprana.

  • Captura de la Periodicidad: El modelo replica con éxito la fase de recuperación y descenso estacional, lo que valida que la integración de las series de Fourier fue correcta.
  • Margen de Error Genérico: Un RMSE de 13 mm y un MAE de 10 mm son valores aceptables para la geodesia regional, indicando que el modelo se mantiene dentro de rangos razonables de precisión.
  • Limitación en la Tendencia Extrema: Se observa que hacia finales de 2025, la “Altura Real” experimenta una caída más acelerada que la predicha. Esto sugiere que factores geodinámicos locales superaron la inercia histórica capturada por el modelo ARIMAX.
Código
ggplot(df_grafica_arimax, aes(x = fecha)) +
  geom_line(aes(y = Real, color = "Altura Real"), linewidth = 1) +
  geom_line(aes(y = Pronostico, color = "Pronóstico ARIMAX"), linewidth = 1, linetype = "dashed") +
  scale_color_manual(values = c("Altura Real" = "gray40", "Pronóstico ARIMAX" = "blue")) +
  theme_minimal() +
  labs(title = "Validación Externa: Pronóstico ARIMAX (Out-of-Sample)",
       subtitle = sprintf("Precisión: RMSE = %.5f m | MAE = %.5f m", rmse_arimax, mae_arimax),
       y = "Desplazamiento Vertical (m)", x = "Fecha", color = "") +
  theme(legend.position = "bottom", plot.title = element_text(face="bold"))

VECM

Interdependencia Tectónica

De la Regresión al Sistema Endógeno

En la geodinámica real, la estación Wanchaq no es un receptor pasivo; el desplazamiento de la corteza es un fenómeno tridimensional interconectado.

  • Endogeneidad: A diferencia del ARDL, el Modelo de Vector de Corrección de Errores (VECM) trata las tres coordenadas (\(H, E, N\)) como variables endógenas. Esto captura cómo un empuje tectónico horizontal “obliga” a un reajuste vertical por elasticidad de la corteza.
  • Fundamento Teórico: Dado que el test de Pesaran (ARDL) confirmó cointegración, el Teorema de Representación de Granger dicta que existe obligatoriamente una estructura de corrección de errores que vincula estas variables a largo plazo.
  • Propósito: El VECM no solo predice; revela la estructura de “atracción” hacia el equilibrio tectónico tras una perturbación.
library(vars)
library(urca)
matriz_gnss <- cbind(
  height = train_data$height,
  east = train_data$east,
  north = train_data$north
)
seleccion_lags <- VARselect(matriz_gnss, lag.max = 10, type = "const")
#cat("\n--- REZAGOS ÓPTIMOS SUGERIDOS ---\n")
#print(seleccion_lags$selection)
K_optimo <- max(seleccion_lags$selection["SC(n)"], 2)
cat("\nRezago (K) elegido para Johansen:", K_optimo, "\n")

Rezago (K) elegido para Johansen: 2 

Prueba de Johansen: Evidencia de Sincronía Tectónica

Identificación del Rango de Cointegración (\(r\))

La prueba de Johansen determina cuántas “vías” de comunicación estables existen entre la altura y los desplazamientos horizontales.

Resultados del Test de Traza:

  • Para \(r=0\), el estadístico (\(67.15\)) supera ampliamente el valor crítico al 1% (\(41.07\)).

  • Para \(r \leq 1\) (\(32.87 > 24.60\)) y \(r \leq 2\) (\(13.53 > 12.97\)), también se rechaza la hipótesis nula con total significancia.

  • Conclusión : Existen 3 relaciones de cointegración. El sistema tiene rango completo.

Las tres coordenadas están integradas en un equilibrio dinámico. Cualquier movimiento en el plano horizontal (Este/Norte) genera un ajuste predecible y mandatorio en la vertical (Altura). La estación Wanchaq muestra una respuesta estructuralmente coherente.

Al despejar la altura para entender la cinemática geodésica: \[Height = -13.449 (East) + 4.771 (North) - 0.046\]

Este vector confirma la endogeneidad del sistema: los desplazamientos hacia el Este están asociados a un reajuste negativo en la vertical (hundimiento/subsidencia en este vector específico), mientras que los movimientos hacia el Norte se asocian positivamente.

Código
# type = "trace": Usamos el estadístico de Traza (el más robusto)
# ecdet = "const": Asumimos intercepto pero sin tendencia en la ecuación (equivalente a tu Case 3)
prueba_johansen <- ca.jo(matriz_gnss, type = "trace", ecdet = "const", K = K_optimo)
cat("\n--- RESULTADOS DE LA PRUEBA DE JOHANSEN ---\n")

--- RESULTADOS DE LA PRUEBA DE JOHANSEN ---
Código
summary(prueba_johansen)

###################### 
# Johansen-Procedure # 
###################### 

Test type: trace statistic , without linear trend and constant in cointegration 

Eigenvalues (lambda):
[1] 1.004103e-01 5.793508e-02 4.089887e-02 1.526557e-16

Values of teststatistic and critical values of test:

          test 10pct  5pct  1pct
r <= 2 | 13.53  7.52  9.24 12.97
r <= 1 | 32.87 17.85 19.96 24.60
r = 0  | 67.15 32.00 34.91 41.07

Eigenvectors, normalised to first column:
(These are the cointegration relations)

            height.l2     east.l2     north.l2   constant
height.l2  1.00000000  1.00000000  1.000000000  1.0000000
east.l2   13.44910793  2.06055219 -0.328190117  1.4013379
north.l2  -4.77158567 -0.21035438  0.277011525  4.9898976
constant   0.04643199 -0.01991509  0.003772574 -0.1468218

Weights W:
(This is the loading matrix)

            height.l2      east.l2     north.l2     constant
height.d -0.013649914 -0.027329368 -0.044232290 9.113070e-18
east.d   -0.007948208 -0.009530759  0.032242687 3.767177e-18
north.d   0.004251315 -0.011723117  0.004880814 1.320329e-18

Selección del Modelo VECM Óptimo

Criterio de Selección: Capacidad Predictiva

Aunque la prueba de Traza sugiere estadísticamente un rango completo (\(r=3\)), este resultado es un artefacto matemático esperado al introducir series con órdenes de integración mixtos en la metodología clásica de Johansen (la cual asume homogeneidad I(1)). Dado que el enfoque ARDL ya demostró la existencia de una relación de equilibrio válida, se optó por imponer teóricamente un rango de cointegración de r = 1 en el modelo VECM.

  • Estabilidad del Pronóstico: El modelo con una única relación de cointegración (\(r=1\)) demostró mayor robustez en la validación externa, logrando una mejor generalización de la tendencia de subsidencia.
  • Interpretación Física: Se identifica un único vector de equilibrio dominante que vincula la deformación vertical con el desplazamiento horizontal, simplificando la alerta temprana sin perder precisión.
Código
# 1. Convertimos el test de Johansen en un modelo calculable
# Usamos r = 1 (asumiendo 1 relación principal de equilibrio a pesar del over-rejection)
modelo_vecm <- vec2var(prueba_johansen, r = 1)

# 2. Funciones de Impulso-Respuesta (IRF)
# Simulamos un "shock" en el Este y vemos cómo reacciona la Altura 20 periodos adelante
irf_este_altura <- irf(modelo_vecm, impulse = "east", response = "height", 
                       n.ahead = 20, boot = TRUE)
plot(irf_este_altura, main = "Impacto en la Altura tras un 'Shock' hacia el Este")

Desempeño Predictivo del VECM

Análisis de la Predicción Tridimensional

El modelo VECM (\(r=1\)) proyecta la evolución de la altura basándose en la inercia del sistema de corrección de errores.

El VECM genera una trayectoria de “retorno al equilibrio”. Se observa que la predicción (línea roja) actúa como un promedio de largo plazo, ignorando el ruido estacional de alta frecuencia.

Código
# 3. Le pedimos al modelo que pronostique tantos pasos adelante como tenga tu test_data
prediccion_vecm <- predict(modelo_vecm, n.ahead = nrow(test_data))

# Extraemos exclusivamente los valores pronosticados para la Altura
pronostico_altura_vecm <- prediccion_vecm$fcst$height[, "fcst"]

# Calculamos el error final (RMSE y MAE)
rmse_vecm <- sqrt(mean((test_data$height - pronostico_altura_vecm)^2, na.rm = TRUE))
mae_vecm <- mean(abs(test_data$height - pronostico_altura_vecm), na.rm = TRUE)
# Construimos el dataframe para graficar
df_test_vecm <- data.frame(
  fecha = fecha_test, # Usamos el mismo vector de fechas del test que en ARIMAX
  Real = test_data$height,
  Pronostico = pronostico_altura_vecm
)
# Generamos la gráfica con el mismo estilo para que coincida en tu póster
ggplot(df_test_vecm, aes(x = fecha)) +
  geom_line(aes(y = Real, color = "Altura Real"), linewidth = 1) +
  geom_line(aes(y = Pronostico, color = "Pronóstico VECM"), linewidth = 1, linetype = "dashed") +
  # Usamos un color distinto (ej. darkred) para diferenciarlo del ARIMAX en el póster
  scale_color_manual(values = c("Altura Real" = "gray40", "Pronóstico VECM" = "darkred")) +
  theme_minimal() +
  labs(title = "VECM: Predicción Tridimensional en Datos Nuevos (15%)",
       subtitle = sprintf("Precisión de pronóstico: RMSE = %.5f m | MAE = %.5f m", rmse_vecm, mae_vecm),
       y = "Desplazamiento Vertical (m)", x = "Fecha", color = "") +
  theme(legend.position = "bottom", plot.title = element_text(face="bold"))

Diagnóstico de Residuales del VECM

Evaluación de la Calidad Estadística

Tras ajustar el modelo VECM (\(r=1\)), sometemos los residuos a un test de Portmanteau para verificar la presencia de información no capturada. El rechazo de la hipótesis nula en la prueba de Portmanteau (\(p < 0.05\)) y la presencia de autocorrelación en el correlograma no solo obedecen a las restricciones paramétricas del VECM, sino a factores geofísicos ampliamente documentados. Según Williams et al. (2004), las amplitudes del ruido blanco y del ruido de parpadeo (flicker noise) en series GNSS presentan una fuerte dependencia latitudinal, alcanzando máximos en la región ecuatorial y mostrando un sesgo de mayor ruido sistemático en el hemisferio sur (aproximadamente 2–4 mm adicionales de ruido de parpadeo).

Dado que la estación Wanchaq se encuentra en el hemisferio sur (latitud ~13.5° S), la autocorrelación observada en los residuales es la manifestación empírica y esperada de este ruido de color regional. Por lo tanto, el modelo VECM estructural es válido, y el ruido residual refleja fielmente las complejas condiciones atmosféricas y geodésicas de los Andes peruanos, más que un error de especificación del modelo.

Código
library(vars)
# Convertimos el objeto VECM a su forma VAR para los tests
vecm_to_var <- vec2var(prueba_johansen, r = 1) 

# Test de Autocorrelación de residuos
test_serial <- serial.test(vecm_to_var, lags.pt = 10, type = "PT.asymptotic")
print(test_serial)

    Portmanteau Test (asymptotic)

data:  Residuals of VAR object vecm_to_var
Chi-squared = 107.93, df = 75, p-value = 0.007641
$serial

    Portmanteau Test (asymptotic)

data:  Residuals of VAR object vecm_to_var
Chi-squared = 107.93, df = 75, p-value = 0.007641
Código
# Genera múltiples gráficos básicos de R
plot(test_serial, names = "height")

Conclusiones

Comparativa Final: Capacidad de Generalización

Interpretación de la Competencia de Modelos

El gráfico de validación externa revela el comportamiento de cada arquitectura frente a la dinámica compleja de la estación Wanchaq:

  • ARDL: Es el único que logra capturar tanto la tendencia de caída como la variabilidad de corto plazo. Al tener una estructura de rezagos distribuida, reacciona más rápido a los cambios en el componente “Este”, manteniéndose casi siempre dentro del área sombreada de incertidumbre.

  • ARIMAX-Fourier: Capta perfectamente la estacionalidad, pero se “pierde” en la tendencia. Es un modelo excelente para promediar ciclos anuales, pero insuficiente para capturar aceleraciones tectónicas imprevistas.

  • VECM: Se comporta como un filtro de baja frecuencia. Al buscar el equilibrio de largo plazo, ignora el ruido y la estacionalidad, entregando una línea de tendencia pura. Es útil para estudios de deformación secular, pero pobre para alertas tempranas semanales.

Código
v_real   <- as.numeric(test_data$height)
v_ardl   <- as.numeric(predicciones_test)
v_arimax <- as.numeric(pronostico_arimax$mean)
v_vecm   <- as.numeric(pronostico_altura_vecm)
df_integrado <- data.frame(
  fecha  = test_data$fecha, 
  Real   = v_real,
  ARDL   = v_ardl,
  ARIMAX = v_arimax,
  VECM   = v_vecm,
  ARDL_lwr = v_ardl - (1.96 * rmse_test),
  ARDL_upr = v_ardl + (1.96 * rmse_test)
)
ggplot(df_integrado, aes(x = fecha)) +
  geom_ribbon(aes(ymin = ARDL_lwr, ymax = ARDL_upr, fill = "Incertidumbre ARDL (95%)"), alpha = 0.15) +
  geom_line(aes(y = Real, color = "Observado (Real)"), linewidth = 1.1) +
  geom_line(aes(y = ARDL, color = "ARDL (Dinámico)"), linewidth = 0.9) +
  geom_line(aes(y = ARIMAX, color = "ARIMAX-Fourier"), linewidth = 0.8, linetype = "dashed") +
  geom_line(aes(y = VECM, color = "VECM (Sistémico)"), linewidth = 0.8, linetype = "dotdash") +
  scale_color_manual(values = c(
    "Observado (Real)" = "black", 
    "ARDL (Dinámico)"  = "blue", 
    "ARIMAX-Fourier"   = "darkgreen", 
    "VECM (Sistémico)" = "darkred"
  )) +
  scale_fill_manual(values = c("Incertidumbre ARDL (95%)" = "dodgerblue")) +
  theme_minimal() +
  labs(title = "Comparación de Modelos: Predicción de Altura Wanchaq",
       subtitle = "Evaluación sobre el 15% de datos finales (no vistos)",
       y = "Desplazamiento Vertical (m)", x = "Año",
       color = "Modelos", fill = "") +
  theme(legend.position = "bottom", plot.title = element_text(face="bold", size = 14))

Selección del Modelo Definitivo

El Criterio NSE (Nash-Sutcliffe)

El NSE estandariza la evaluación midiendo si el modelo supera la varianza natural de los datos: * \(NSE = 1\): Predicción perfecta. * \(NSE > 0\): Predictor estadísticamente válido. * \(NSE \le 0\): Peor que usar el promedio histórico.

Veredicto Geofísico

  1. ARDL (El Predictor): Único con \(NSE\) positivo (\(0.54\)). Su flexibilidad para asignar rezagos asimétricos logra un error milimétrico (\(5.7\) mm), ideal para alertas tempranas.
  2. ARIMAX (El Estacional): \(NSE\) negativo. Modela bien el ciclo anual y el ruido, pero es ciego ante cambios de tendencia no lineales.
  3. VECM (El Estructural): Peor desempeño predictivo, pero metodológicamente indispensable. Su objetivo no es proyectar a largo plazo (donde la varianza multivariante explota), sino revelar la interdependencia tectónica tridimensional mediante simulaciones de choque (IRF).
Código
# Función para el Nash-Sutcliffe Efficiency
f_nse <- function(real, pred) {
  1 - (sum((real - pred)^2, na.rm=T) / sum((real - mean(real, na.rm=T))^2, na.rm=T))
}

# Consolidación de la tabla
tabla_final <- data.frame(
  Modelo = c("ARDL", "ARIMAX-Fourier", "VECM"),
  RMSE_m = c(rmse_test, rmse_arimax, rmse_vecm), 
  MAE_m  = c(mae_test, mae_arimax, mae_vecm),
  NSE    = c(f_nse(test_data$height, predicciones_test),
             f_nse(test_data$height, as.numeric(pronostico_arimax$mean)),
             f_nse(test_data$height, as.numeric(pronostico_altura_vecm)))
)
# Redondeo para estética académica
tabla_final[, 2:3] <- round(tabla_final[, 2:3], 5)
tabla_final[, 4]   <- round(tabla_final[, 4], 4)

kableExtra::kable(tabla_final)
Modelo RMSE_m MAE_m NSE
ARDL 0.00570 0.00460 0.5437
ARIMAX-Fourier 0.01310 0.01055 -1.4073
VECM 0.01726 0.01551 -3.1762

Referencias

Bevis, Michael, Douglas Alsdorf, Eric Kendrick, et al. 2005. «Seasonal fluctuations in the mass of the Amazon River system and Earth’s elastic response». Geophysical Research Letters 32 (16): 2005GL023491. https://doi.org/10.1029/2005GL023491.
Dong, D., P. Fang, Y. Bock, M. K. Cheng, y S. Miyazaki. 2002. «Anatomy of apparent seasonal variations from GPS-derived site position time series». Journal of Geophysical Research: Solid Earth 107 (B4): ETG 9-1-ETG 9-16. https://doi.org/https://doi.org/10.1029/2001JB000573.
Natsiopoulos, Kleanthis, y Nickolaos G. Tzeremes. 2022. ARDL bounds test for cointegration: Replicating the Pesaran et al. (2001) results for the UK earnings equation using R. https://doi.org/10.1002/jae.2919.
Poette, Michael, Sandrine Mouysset, Daniel Ruiz, Vincent Pey, Jean-Marc Alliot, y Vincent Minville. 2026. Benchmarking imputation strategies for missing time-series data in critical care using real-world-inspired scenarios. https://doi.org/10.1038/s41598-026-39035-z.
Sun, Xiwen, Tieding Lu, Shunqiang Hu, et al. 2023. «The Relationship of Time Span and Missing Data on the Noise Model Estimation of GNSS Time Series». Remote Sensing 15 (14): 3572. https://doi.org/10.3390/rs15143572.
Williams, Simon D. P., Yehuda Bock, Peng Fang, et al. 2004. «Error analysis of continuous GPS position time series». Journal of Geophysical Research: Solid Earth 109 (B3): 2003JB002741. https://doi.org/10.1029/2003JB002741.