Los datos llegan etiquetados con una fecha. El precio del petróleo ayer, la temperatura de esta mañana, el número de pasajeros el mes pasado… La secuencia temporal no es un adorno: el orden importa, el tiempo informa. Una serie temporal es cualquier variable medida de forma sucesiva y ordenada en el tiempo, y su análisis nos permite entender el pasado, diagnosticar el presente y —lo más valioso— anticipar el futuro.
Nota¿Qué diferencia una serie temporal de datos convencionales?
En estadística clásica asumimos que las observaciones son independientes entre sí. En una serie temporal esto es precisamente falso: lo que ocurrió ayer influye en lo que ocurre hoy. Esta autocorrelación es el problema y, al mismo tiempo, el recurso que explotan todos los métodos de este capítulo.
12.1 Motivación: ¿Para qué necesitamos series temporales?
Pronosticar escenarios es una de las misiones clave de la ciencia de datos. Extrapolar la tendencia y los patrones estacionales nos permite tomar decisiones críticas. Según el texto clásico Introductory Time Series with R de Paul S.P. Cowpertwait y Andrew V. Metcalfe, repasemos cuatro ejemplos reales mundiales:
Protocolo de Kioto: Los argumentos y compromisos internacionales para reducir las emisiones de gases de efecto invernadero se validaron mediante una combinación de física atmosférica, economía y análisis exhaustivo de series temporales.
Flota de Aerolineas: Cuando Singapore Airlines encargó de golpe 20 Boeing 787 y 29 nuevos Airbus, blindando su cuota de mercado futura, la decisión se respaldó puramente en un análisis de series temporales sobre las tendencias de sus pasajeros (veremos luego la mítica serie AirPassengers).
Suministro Energético (Gas): La variación de la demanda depende puramente de la época del año, la temperatura y la velocidad del viento. El análisis de series temporales permite ajustar el stock de gas inyectado a la red previendo a uno, tres o siete días vista.
“Tell us what the future holds, so we may know that you are gods.”
— Isaías 41:23 (citado en Hyndman & Athanasopoulos, 2021)
12.1.1 El problema de la regresión clásica (Dependencia Temporal)
Podríamos pensar en usar una regresión de Mínimos Cuadrados Ordinarios (OLS) clásica que dependa del tiempo \(x\): \(y = a + bx + cx^2\). El problema de los métodos convencionales es que asumen que las observaciones son independientes e idénticamente distribuidas (IID).
En los datos temporales esto jamás se cumple: la temperatura de hoy está tremendamente correlacionada (depende) de la temperatura que hizo ayer. El análisis de series temporales nace, precisamente, para modelar y aprovechar esa autocorrelación temporal en lugar de obviarla.
12.2 Exploración de Datos Climatológicos (EDA)
A lo largo de este capítulo usaremos como proyecto real histórico un dataset de la Agencia Estatal de Meteorología (AEMET) con registros mensuales de Málaga desde 1951. Este dataset tiene todo lo que hace interesante a una serie temporal: tendencia a largo plazo, estacionalidad anual clara y ruido residual.
(Nota: En el Anexo de este mismo capítulo detallamos cómo puedes crear tú mismo este dataset descargando datos actualizados conectándote a AEMET desde R, o descargar los datos de otra localidad).
Antes de predecir, debemos conocer nuestros datos. Cargamos nuestro dataset y filtramos las observaciones que corresponden a resúmenes anuales.
# Leemos los datos de Málaga y eliminamos las filas de resumen anual (mes acaba en "-13")dataset_malaga <-read_csv( here::here("data", "malaga_2026.csv"), show_col_types =FALSE) %>%filter(!str_detect(fecha, "-13")) %>%mutate(# Creamos una columna fecha tipo Date para representar el mesfecha_date =ymd(paste0(fecha, "-01")) )# Visualizamos la evolución histórica con ggplot2ggplot(dataset_malaga, aes(x = fecha_date, y = tm_min)) +geom_line(color ="steelblue", alpha =0.8) +theme_minimal(base_size =13) +labs(title ="Evolución histórica — Temperatura mínima en Málaga (1951-)",subtitle ="El dataset AEMET que usaremos a lo largo de todo el capítulo",x ="Año", y ="Temperatura Mínima (°C)" ) +theme(plot.title =element_text(face ="bold"))
Warning: Removed 10 rows containing missing values or values outside the scale range
(`geom_line()`).
📌 Primera lectura: A simple vista ya distinguimos un ciclo anual claro (el patrón en dientes de sierra). Es posible que visualmente también aprecies cómo los picos mínimos no descienden tanto a partir del año 2000 como lo hacían en las décadas de los 50 o 60.
Tip✍️ Ejercicio Propuesto 1: Exploración de Precipitaciones
Replica el análisis exploratorio anterior, pero ahora para la variable de precipitación mensual (p_mes). Crea el gráfico de evolución histórica. ¿Observas el mismo patrón regular que en las temperaturas o es más errático?
Nota🌧️ El gran pico de los 90: Las inundaciones de Málaga de 1989
Al dibujar tu gráfica de precipitaciones observarás un pico absolutamente anómalo rozando los 500 mm justo antes del año 1990.
Este máximo histórico absoluto se corresponde con el fatídico noviembre de 1989. Entre el 14 y el 15 de ese mes, una violenta “gota fría” (DANA) descargó casi 150 litros/m² en apenas dos horas. Los ríos Campanillas y Guadalhorce se desbordaron, dejando la ciudad anegada y declarada como zona catastrófica.
El saldo total de aquel mes de noviembre en el registro de AEMET (p_mes) fue de 497 mm, lo que representó de golpe casi la lluvia que cae habitualmente en un año entero. Este es un ejemplo brutal de cómo las series temporales capturan eventos extremos (outliers), vitales a la hora de modelar la varianza real de la naturaleza.
12.3 ¿Qué es una serie temporal? Definición y objetos en R
Una serie temporal es una secuencia de observaciones \(\{y_1, y_2, \ldots, y_T\}\) indexadas en el tiempo con un intervalo regular (segundo, hora, día, mes, año…).
Vamos a usar los siguientes paquetes en R:
library(forecast)library(tseries)library(TTR)
12.3.1 El objeto ts en R
R representa las series temporales con la clase ts. Sus parámetros fundamentales son:
Parámetro
Significado
Ejemplo
start
Primer período
c(1951, 1) = enero 1951
frequency
Obs. por año
12 = mensual, 4 = trimestral, 1 = anual
# Existen algunos registros mensuales vacíos (NA), los interpolamos -- imputamostemp_minima_limpia <-na.interp(dataset_malaga$tm_min)# Aquí creamos la serie temporal con Rts_malaga <-ts( temp_minima_limpia,start =c(1951, 1),frequency =12)cat("Clase del objeto: ", class(ts_malaga), "\n")
Para series con estacionalidad que crece con el tiempo, se habla de estacionalidad multiplicativa (como el clásico conjunto AirPassengers incluido en R). Si la variación estacional es estable año tras año, como la temperatura en Málaga, es un modelo aditivo.
Tip✍️ Ejercicio Propuesto 2: Objeto ts para Precipitaciones
Crea la serie temporal (ts) para la variable p_mes, asegurándote de interpolar posibles valores ausentes (NA) tal como hemos hecho con la temperatura (usando na.interp()). Guárdala en una variable llamada ts_precipitacion y elabora un autoplot() rápido para visualizarla.
12.4 Componentes y Descomposición
Toda serie temporal puede descomponerse en partes que, una vez separadas, son mucho más fáciles de entender y modelar.
Componente
Símbolo
Descripción
Tendencia
\(T_t\)
Dirección a largo plazo (creciente, decreciente, estable)
Estacionalidad
\(S_t\)
Patrón que se repite con período fijo (anual, semanal…)
Ruido residual
\(R_t\)
Variación aleatoria no explicada por los anteriores
12.4.1 Descomposición STL de las temperaturas de Málaga
STL (Seasonal and Trend decomposition using Loess) es un método robusto para separar estos componentes.
Figura 12.1: Descomposición STL de las temperaturas mínimas de Málaga. Los cuatro paneles muestran la serie original, la estacionalidad, la tendencia y el residuo.
📌 Lectura de la descomposición:
Tendencia (trend): Se percibe bien que durante las primeras décadas se mantenía estable o incluso bajó hasta los 70s, pero sufre un claro y constante ascenso desde finales de los 70 hasta la actualidad.
Estacionalidad (seasonal): El patrón es regular e invariable. La amplitud permanece estable.
Figura 12.2: Gráfico de subseries: cada panel es un mes a lo largo de 70 años. Las líneas horizontales son la media histórica del mes.
📌 Insight: Vemos que los eneros o febreros han tendido a hacerse cada vez más suaves (hay un incremento de temperaturas en los paneles invernales recientes).
Tip✍️ Ejercicio Propuesto 3: Descomposición de las Precipitaciones
Aplica la descomposición STL (stl(..., s.window = "periodic")) a la serie de precipitaciones (ts_precipitacion) del ejercicio anterior. Genera el gráfico continuo con autoplot(). ¿Es la estacionalidad tan fuerte e invariable como en la temperatura? ¿Qué puedes decir sobre la magnitud del “residuo” o componente aleatorio?
12.5 Métodos de Predicción Básicos
Antes de introducir métodos sofisticados, es fundamental usar métodos de referencia (benchmarks). Todo modelo avanzado solo sirve si mejora este nivel mínimo.
Método
Función en R
Descripción
Cuándo usarlo
Media
meanf()
Predice la media histórica de toda la serie
Serie estacionaria pura
Naïve
naive()
Predice el último valor conocido
Caminos aleatorios (Random Walk)
Naïve estacional
snaive()
Predice el valor del mismo mes del año pasado
Estacionalidad marcada
Deriva
rwf(..., drift=T)
Une el primer y último dato con una recta
Tendencia clara
Para realizar predicciones, usamos el parámetro h (horizon), que define cuántos pasos hacia el futuro queremos estimar. En nuestro caso, h será igual a la longitud del conjunto de test.
# Para la visualización y validación tomaremos los últimos 15 añosts_reciente <-window(ts_malaga, start =c(2005, 1))# Entrenamiento: hasta 2019, Test: 2020 en adelantets_train <-window(ts_reciente, end =c(2019, 12))ts_test <-window(ts_reciente, start =c(2020, 1))h <-length(ts_test)fc_media <-meanf(ts_train, h = h)fc_naive <-naive(ts_train, h = h)fc_snaive <-snaive(ts_train, h = h)fc_drift <-rwf(ts_train, h = h, drift =TRUE)autoplot(ts_train) +autolayer(fc_media, series ="Media", PI =FALSE) +autolayer(fc_naive, series ="Naïve", PI =FALSE) +autolayer(fc_snaive, series ="Naïve Estacional", PI =FALSE) +autolayer(fc_drift, series ="Deriva", PI =FALSE) +autolayer(ts_test, series ="Real (test)", linetype ="dashed") +labs(title ="Métodos Benchmark — Temperaturas Málaga",subtitle ="Validación sobre post-2020", x ="Año", y ="Temperatura mínima (°C)", color ="Método" ) +theme_minimal(base_size =13) +theme(legend.position ="bottom")
Figura 12.3: Comparativa de métodos simples aplicados a las temperaturas de Málaga (últimas décadas para mejor visibilidad).
📌 El Naïve Estacional gana visualmente. Es muy difícil ganarle a la hora de predecir la temperatura de agosto sencillamente repitiendo la que hizo el agosto pasado.
12.6 Suavizado Exponencial de Holt-Winters
La idea central: las observaciones recientes importan más que el pasado remoto.
El método de Holt-Winters abarca los tres componentes estelares: \[\hat{y}_{T+h|T} = \ell_T + h \cdot b_T + s_{T+h-m(k+1)}\] Donde \(\ell_T\) es el nivel, \(b_T\) es la tendencia y \(s_t\) es la estacionalidad.
Este modelo funciona como un sistema de tres capas de memoria superpuestas:
Nivel (\(\ell_T\)): El valor base actual de la serie. Se actualiza con el parámetro \(\alpha\) (alpha). Un \(\alpha\) bajo significa “memoria larga” (confiamos en la historia); un \(\alpha\) alto significa “nerviosismo” (solo importa lo último).
Tendencia (\(b_T\)): La pendiente o velocidad de cambio. Se actualiza con \(\beta\) (beta). Controla cómo de rápido el modelo detecta cambios en la dirección de la serie.
Estacionalidad (\(s_t\)): El patrón cíclico (ej. el calor de agosto). Se actualiza con \(\gamma\) (gamma). Decide si la “forma del año” debe cambiar basándose en los veranos más recientes.
En nuestro caso, obtenemos valores bajos para \(\alpha\) (0.053) y \(\beta\) (0.034), lo que indica un modelo robusto y estable: confía mucho en la trayectoria histórica. El valor de \(\gamma\) (0.221) es algo superior, lo que sugiere que el modelo está “escuchando” con más atención cómo cambian los patrones estacionales en los años más recientes.
NotaNota Histórica: Del control de inventarios al clima global
Charles Holt y la computación de los años 60
El modelo que hoy usamos para auditar el cambio climático nació, curiosamente, en el fragor de la revolución industrial de posguerra. Charles C. Holt desarrolló el suavizado con tendencia en 1957 mientras trabajaba en la Universidad Carnegie Mellon, buscando una forma de que las fábricas no se quedaran sin stock ante cambios en la demanda.
Fue su alumno, Peter Winters, quien en 1960 tuvo la brillante idea de añadir la “capa” de la estacionalidad (el componente \(\gamma\)), permitiendo al modelo entender ciclos naturales. Lo que comenzó como una herramienta para optimizar la producción de piezas de repuesto, acabó convirtiéndose en el estándar mundial para entender fenómenos tan complejos como la meteorología o el tráfico aéreo.
Para obtener la predicción final usamos la función forecast(), que toma el modelo ajustado y el horizonte h. Esta función no solo da el valor puntual, sino que calcula los intervalos de confianza (por defecto al 80% y 95%).
hw_fc <-forecast(hw_fit, h = h)autoplot(hw_fc) +theme_minimal(base_size =13) +labs(title ="Pronóstico con Holt-Winters")
Pronóstico de temperaturas futuras con Holt-Winters.
Un buen modelo debe dejar un ruido blanco como residuo: lo que sobra no puede tener patrones predecibles. Para comprobarlo usamos el test estadístico formal Ljung-Box.
En esencia, el test de Ljung-Box evalúa si los residuos están correlacionados consigo mismos (autocorrelación). La hipótesis nula (H₀) es que NO hay autocorrelación (son ruido blanco). Si el p-valor es mayor que 0.05, no rechazamos H₀, lo que significa que el modelo ha eliminado toda la estructura predecible de los datos, y los residuos son “aburridos” (ruido blanco), que es exactamente lo que queremos.
test_lb <-Box.test(residuals(hw_fc), lag =24, type ="Ljung-Box")cat("p-valor de Ljung-Box =", round(test_lb$p.value, 4), "\n")
p-valor de Ljung-Box = 0.2578
if (test_lb$p.value >0.05) cat("✅ p > 0.05: Residuos indistingibles del ruido blanco.\n") elsecat("⚠️ p < 0.05: Existe autocorrelación residual.\n")
✅ p > 0.05: Residuos indistingibles del ruido blanco.
12.7 Modelos ARIMA: El estándar de la industria
Si Holt-Winters era como un sistema de tres capas de memoria, ARIMA es como un sintonizador de radio de alta precisión. Es un marco de trabajo más robusto que se basa en la estructura de correlación de los datos.
12.7.1 El concepto de Estacionariedad
Para que ARIMA funcione, la serie debe ser estacionaria. Matemáticamente, una serie \(\{y_t\}\) es estacionaria si sus propiedades estadísticas son independientes del instante de tiempo en el que se observen. Se deben cumplir tres pilares:
Media constante:\(E(y_t) = \mu\) para todo \(t\). No hay tendencia.
Varianza constante:\(Var(y_t) = \sigma^2\) para todo \(t\). La dispersión es estable.
Autocovarianza constante:\(Cov(y_t, y_{t-k}) = \gamma_k\). La relación entre dos puntos solo depende de la distancia \(k\) entre ellos, no del tiempo absoluto.
🌊 Intuición: Imagina el mar. Una serie no estacionaria es como una marea subiendo (la media cambia) o una tormenta donde las olas crecen (la varianza cambia). Una serie estacionaria es como el oleaje en calma: las olas suben y bajan, pero siempre alrededor del mismo nivel y con la misma intensidad media.
Si una serie no es estacionaria, aplicamos el Operador de Diferenciación\(\Delta\): \[y'_t = y_t - y_{t-1}\] Esto “estabiliza” la media eliminando la tendencia. El parámetro \(d\) en ARIMA(p,d,q) indica cuántas veces aplicamos este operador. Tras “diferenciar” unas cuantas veces, generalmente una serie temporal se vuelve estacionaria.
12.7.2 Desmontando la ecuación: ARIMA(p, d, q)
El modelo es una combinación lineal de dos procesos fundamentales:
AR (p) — Componente Autorregresivo
El valor actual se explica como una regresión de sus propios valores pasados (lags): \[y_t = c + \phi_1 y_{t-1} + \phi_2 y_{t-2} + \dots + \phi_p y_{t-p} + \epsilon_t\] Donde \(\phi\) son los coeficientes que miden la “memoria” de la serie y \(\epsilon_t\) es ruido blanco. * Intuición: Si \(\phi_1\) es alto, lo que pasó ayer determina fuertemente lo que pasa hoy. Cuando un \(\phi_j\) es significativo, indica que el valor de la serie en el tiempo \(t-j\) tiene un impacto estadísticamente significativo en el valor actual \(y_t\).
MA (q) — Componente de Medias Móviles
En lugar de mirar los valores pasados, miramos los errores de predicción pasados: \[y_t = c + \epsilon_t + \theta_1 \epsilon_{t-1} + \theta_2 \epsilon_{t-2} + \dots + \theta_q \epsilon_{t-q}\] Donde \(\theta\) representa cómo se propagan los “choques” o imprevistos en el tiempo. * Intuición: Si hubo un evento inusual ayer (\(\epsilon_{t-1}\) grande), el modelo MA ajusta la previsión de hoy para absorber ese impacto. Un coeficiente \(\theta_j\) significativo indica que el error de predicción en el tiempo \(t-j\) tiene un impacto estadísticamente significativo en el valor actual \(y_t\).
12.7.3 SARIMA: Añadiendo la Estacionalidad
Cuando hay ciclos (como meses en un año), extendemos el modelo a SARIMA \((p,d,q)(P,D,Q)_m\). Aquí, los términos en mayúsculas aplican la misma lógica pero a pasos de longitud \(m\) (por ejemplo, \(m=12\) para datos mensuales).
12.7.4 Calibración Óptima con auto.arima()
La selección de los órdenes \((p,d,q)\) se realiza minimizando el Criterio de Información de Akaike (AICc), que busca un equilibrio entre el ajuste a los datos y la simplicidad del modelo (parsimonia): \[AIC = -2\log(L) + 2k\] Donde \(L\) es la verosimilitud y \(k\) el número de parámetros. La función auto.arima() automatiza esta búsqueda mediante el algoritmo de Hyndman-Khandakar.
# auto.arima se encarga de la diferenciación (d) y la selección de (p,q)fit_sarima <-auto.arima(ts_train, seasonal =TRUE, stepwise =FALSE)summary(fit_sarima)
Series: ts_train
ARIMA(1,0,0)(2,1,0)[12]
Coefficients:
ar1 sar1 sar2
0.2269 -0.5570 -0.3441
s.e. 0.0770 0.0768 0.0798
sigma^2 = 1.13: log likelihood = -249.84
AIC=507.68 AICc=507.92 BIC=520.17
Training set error measures:
ME RMSE MAE MPE MAPE MASE
Training set 0.07879203 1.017976 0.7929462 0.1605061 6.624677 0.7695838
ACF1
Training set -0.003108932
fc_sarima <-forecast(fit_sarima, h = h)autoplot(fc_sarima) +autolayer(ts_test, series ="Real", linetype ="dashed", color ="black") +labs(title ="Predicción SARIMA Autocalibrado") +theme_minimal()
Figura 12.4: Predicción SARIMA: Observa cómo el modelo captura el ciclo estacional y proyecta la incertidumbre (sombreado) hacia el futuro.
Tip✍️ Ejercicio Propuesto 4: Predicción SARIMA de Precipitaciones
Utiliza el método auto.arima() con seasonal=TRUE para encontrar el modelo óptimo de precipitaciones en Málaga. Sigue la misma partición que hemos hecho (entrenamiento hasta 2019 inclusive, test desde 2020 en adelante usando ts_reciente <- window(ts_precipitacion, start=c(2005,1))). Compara de forma visual tu predicción (el forecast) con la lluvia real ts_test sucedida desde 2020.
12.8 Pipeline Completo y Criterio de Selección
El analista entrena el modelo con datos históricos y valida su eficacia con datos reservados que el algoritmo nunca ha “visto”, simulando así un escenario real de predicción de futuro.
¿Cómo medimos la bondad del ajuste?
Aquí entra en juego el concepto de Error de Predicción. No hay un error único, sino varias formas de medir la desviación entre lo que predijo el modelo y lo que realmente ocurrió. Las más comunes son:
RMSE (Raíz del Error Cuadrático Medio): Sensible a errores grandes (penaliza atípicos).
MAE (Error Absoluto Medio): Promedio del error absoluto en los grados originales (en nuestro caso, grados ºC).
MAPE (Error Porcentual Absoluto Medio): Porcentaje relativo en %, ideal para audiencias corporativas.
La función en R que calcula esto es accuracy(). Calcula métricas de error para el conjunto de entrenamiento (fila 1) y para el conjunto de test (fila 2).
📌 Los modelos ARIMA y Holt-Winters logran mejorar sustancialmente el MAE, bajándolo en muchas ocasiones. Entre ambos, seleccionamos aquel con mejor balance entre robustez de los residuos y bajo error de testeo.
12.8.1 Decisión Clave: ¿ARIMA o Holt-Winters?
Como hemos visto a lo largo del capítulo, no existe un ganador universal:
Característica
ARIMA (SARIMA)
Suavizado Exponencial (Holt-Winters)
Funcionamiento
Predice los valores futuros basándose en rezagos pasados y corrección de errores (lags y MA).
Promedios ponderados donde los valores recientes tienen mayor peso (decae exponencialmente).
Complejidad
Más complejos matemáticamente de calibrar a mano (parámetros p, d, q).
Más intuitivos (Nivel, Tendencia, Estacionalidad).
Cuándo brilla
Series estacionarias sin estacionalidad fuerte per-se (ej. series financieras, bolsa) donde la autocorrelación secuencial es el principal motor.
Series que exhiben un comportamiento estacional claro, tendencial y repetitivo (ej: demanda de vuelos, ventas retail mensuales, temperaturas).
En la práctica industrial, siempre se recomienda probar rápidamente al menos un representante de cada gran familia clásica (y un sub-representante de Machine Learning moderno) como hemos hecho en el punto #8 con la tabla comparativa de Málaga.
12.9 Conclusión: La Huella del Cambio Climático en Málaga (1951-2026)
A lo largo de este tema hemos aprendido a descomponer tendencias y aislar el componente estacional. Más allá de ser un ejercicio matemático, las series temporales son la principal herramienta empírica para auditar el Cambio Climático de manera innegable a nivel local.
Aprovechando que disponemos de un registro ininterrumpido a lo largo de 7 décadas en el aeropuerto de Málaga (malaga_2026.csv), podemos cerrar el capítulo con un análisis concluyente. Si consolidamos la temperatura máxima promedio de cada década, la pérdida de estacionariedad térmica y la tendencia alcista se revelan de forma empírica y cristalina:
library(dplyr)library(lubridate)library(ggplot2)# Construimos la década partiendo del año en la fechadf_clima_anual <- dataset_malaga %>%mutate(decada =floor(year(fecha_date) /10) *10) %>%group_by(decada) %>%summarise(t_max_decada =mean(tm_max, na.rm =TRUE) )ggplot(df_clima_anual, aes(x =factor(decada))) +geom_col(aes(y = t_max_decada), fill ="darkred", alpha =0.8, width =0.6) +geom_smooth(aes(group =1, y = t_max_decada), method ="lm", color ="black", se =FALSE) +geom_text(aes(y = t_max_decada, label =round(t_max_decada, 1)), vjust =-0.5, fontface ="bold", size =4) +# Usamos coord_cartesian para evitar la distorsión total, pero cortando el eje en 20ºC# para apreciar la brutal escalada de la última franjacoord_cartesian(ylim =c(20, max(df_clima_anual$t_max_decada) +0.5)) +theme_minimal(base_size =13) +labs(title ="Evidencia Empírica del Aumento Térmico (1951-2026)",subtitle ="Temperatura Máxima Media Mensual, agrupada por décadas",x ="Década",y ="Temperatura Máxima (°C)" )
Figura 12.5: Evolución de la Temperatura Máxima Media Mensual en Málaga, por décadas.
Para hacer un “zoom” a lo que ha ocurrido en las últimas dos décadas y observar la volatilidad año a año, podemos trazar la curva a nivel anual desde el año 2000:
df_clima_reciente <- dataset_malaga %>%mutate(anio =year(fecha_date)) %>%filter(anio >=2000) %>%group_by(anio) %>%summarise(t_max_anual =mean(tm_max, na.rm =TRUE) )ggplot(df_clima_reciente, aes(x = anio, y = t_max_anual)) +geom_line(color ="darkred", linewidth =1) +geom_point(color ="darkred", size =2) +geom_smooth(method ="lm", color ="black", linetype ="dashed", se =FALSE) +theme_minimal(base_size =13) +labs(title ="Detalle Anual del Aumento Térmico (2000-2026)",subtitle ="Temperatura Máxima Media Mensual, agrupada por años",x ="Año",y ="Temperatura Máxima (°C)" )
Figura 12.6: Evolución de la Temperatura Máxima Media Mensual en Málaga (Desde 2000).
Para comprobar de forma análoga cómo altera el cambio climático al régimen de lluvias (haciéndolo más seco y propiciando eventos más extremos o DANAs aisladas), analizamos el volumen total de precipitación histórica:
df_lluvia_anual <- dataset_malaga %>%mutate(anio =year(fecha_date)) %>%group_by(anio) %>%summarise(prec_total =sum(p_mes, na.rm =TRUE) ) %>%mutate(decada =floor(anio /10) *10) %>%group_by(decada) %>%summarise(media_prec_anual =mean(prec_total, na.rm =TRUE) )ggplot(df_lluvia_anual, aes(x =factor(decada))) +geom_col(aes(y = media_prec_anual), fill ="steelblue", alpha =0.8, width =0.6) +geom_smooth(aes(group =1, y = media_prec_anual), method ="lm", color ="red", linetype ="dashed", se =FALSE) +geom_text(aes(y = media_prec_anual, label =round(media_prec_anual, 0)), vjust =-0.5, fontface ="bold", size =4) +theme_minimal(base_size =13) +labs(title ="La Desecación y Caída de las Precipitaciones (1951-2026)",subtitle ="Precipitación Media Acumulada Anual (mm), agrupada por décadas",x ="Década",y ="Precipitación Media Anual (mm)" )
Figura 12.7: Evolución de la Precipitación Media Anual en Málaga, por décadas.
E igualmente, si hacemos “zoom” a los años a partir del 2000, la recta de regresión confirma que la caída de milímetros acumulados por año es una tendencia viva:
Figura 12.8: Evolución de la Precipitación Total Anual en Málaga (Desde 2000).
Principales conclusiones derivadas desde el prisma de la modelización:
Ruptura de la Media Histórica: Las temperaturas máximas promediadas en la década presente (24.5 °C en los 2020s) superan en dos grados centígrados íntegros a los veranos e inviernos vividos en las décadas de los 50, 60 y 70 (22.5 °C). Por su parte, la precipitación media anual ha caído libremente de los 600 mm de media en los 60s/70s a rozar peligrosamente los 379 mm actuales.
Ciclos más intensos: Modelar la temperatura con técnicas ARIMA obliga a usar diferenciaciones (d=1 u órdenes altos), porque el pasado lejano, como nivel o media (c), sencillamente dejó de predecir el clima actual de la ciudad.
Poder del Dato Público: Bases de datos fiables, granulares y abiertas como las provistas por AEMET demostradas analíticamente en R y visualizadas con ggplot conforman el blindaje científico contra cualquier sesgo al debatir el calentamiento planetario en nuestra propia ciudad.
Validación cruzada mediante Predicción de TS:
Robustez en Holt-Winters: Al ajustar las temperaturas, obtenemos un \(\alpha\) muy bajo (0.053). Esto demuestra que, a pesar del calentamiento, la serie conserva una fuerte inercia histórica. El modelo no necesita “obsesionarse” con el último dato para ser preciso; la estructura climática de Málaga es lo suficientemente sólida como para que el pasado lejano siga siendo un ancla valiosa para el nivel medio.
Evidencia Hídrica en SARIMA: Al pasar de nuevo el auto.arima() sobre nuestras precipitaciones mensuales, este se ve forzado a irse a órdenes auto-regresivos hasta de lag 3, y lo que es más espectacular (o deprimente): detecta una Varianza de Error Residual monstruosa de \(\sigma^2 = 22266\). El abanico de error se le dispara al modelo probando irremediablemente la aleatoriedad hostil producida por sequías + grandes DANAs aisladas sin apenas rima ni motivo cíclico estable.
12.10 Anexo: Descarga directa de datos con climaemet y la API de AEMET
El origen de estos datos (por ejemplo, temp.csv o malaga_2026.csv) no es cerrado. La Agencia Estatal de Meteorología (AEMET) provee acceso público y la vía moderna óptima para explotar sus repositorios es R y la API Open Data, orquestado con librerías del ecosistema de la comunidad rOpenSpain.
El paquete climaemet ofrece una interfaz muy potente y bien documentada para bajarnos las series que deseemos sin usar engorrosos portales de clics.
12.10.1 Instalación y Clave API
Para acceder de un modo automatizado a AEMET es preciso registrarse gratis en su portal Open Data y conseguir una clave API.
# Puedes instalar ambos desde CRANinstall.packages("climaemet")library(climaemet)# 1. Regístrate en https://opendata.aemet.es/centrodedescargas/altaUsuario# 2. Registra tu contraseña en la sesión de R:aemet_api_key("TU_API_KEY_AQUI", install =TRUE)
12.10.2 Extracción de un dataset climatológico mensual/anual
En AEMET, las estaciones meteorológicas se marcan con unos indicativos (por ejemplo, la estación del aeropuerto de Málaga es “6155A”). Podemos ver el listado de las estaciones o bajarnos los datos estacionales.
library(tidyverse)library(climaemet)# Extracción de valores climatológicos de una sola estación por años (ej. Málaga Aeropuerto)df_malaga <-aemet_monthly_period(station ="6155A", # El código de la estación de Málagastart =1951# Año de inicio del registro)# Vistazo rápido:head(df_malaga)
La función aemet_monthly_period recupera todo tipo de mediciones muy detalladas: p_max (precipitación máxima), tm_min y tm_max (temperaturas extremas absolutas de media mensual), tm_mes, w_med (velocidad media del viento), inso (horas de insolación). Esto te brinda unas bases enormes para hacer modelado de cualquier variable estacional asociada. A partir de esa caja, pasas la columna de interés a la función ts() para introducirla al pipeline que hemos aprendido de series temporales.
12.11 Referencias
Hyndman, R.J., & Athanasopoulos, G. (2021). Forecasting: Principles and Practice (3ª ed.). OTexts. https://otexts.com/fpp3/
13 Proyecto: Predicción de demanda eléctrica en Victoria (Australia)
13.1 Introducción y Datos
En esta práctica, trabajaremos con datos de demanda eléctrica de Victoria (Australia). Utilizaremos exclusivamente el enfoque de objetos ts y los modelos vistos en clase: Naïve Estacional, Holt-Winters y ARIMA.
Necesitamos descargar el dataset. Para ello usamos el paquete fpp3.
13.2 Fase 1: Preparación de la Serie Temporal
13.2.1 Tarea 1.1: Agregación de Datos
Para simplificar el análisis, agrupa los datos originales (cada 30 min) a nivel diario sumando la demanda de cada jornada.
💡 Pista: Usa group_by(Date) y summarise() para obtener la suma diaria de la demanda.
13.2.2 Tarea 1.2: Creación del Objeto ts
Convierte los datos en un objeto de serie temporal (ts) con frecuencia semanal. Representa la serie resultante.
💡 Pista: Emplea la función ts(..., frequency = 7).
13.3 Fase 2: Diagnóstico y Descomposición
13.3.1 Tarea 2.1: Descomposición STL
Descompone la serie para observar la tendencia y la estacionalidad semanal. ¿Es el patrón semanal constante a lo largo del tiempo?
💡 Pista: Usa stl() con s.window = "periodic".
13.4 Fase 3: Modelización
13.4.1 Tarea 3.1: Partición Entrenamiento/Test
Divide la serie ts dejando los últimos 31 días (diciembre de 2014) como conjunto de test.
💡 Pista: Usa la función window(). Recuerda que h = 31.
13.4.2 Tarea 3.2: Entrenamiento de Modelos
Entrena los tres modelos competitivos explicados: 1. SNAIVE: Naïve estacional. 2. Holt-Winters: Suavizado exponencial triple. 3. ARIMA: Modelo autorregresivo integrado de medias móviles automático.
💡 Pista: Usa las funciones snaive(), HoltWinters() y auto.arima().
13.5 Fase 4: Evaluación y Pronóstico
13.5.1 Tarea 4.1: Comparativa de Precisión
Genera las predicciones para el periodo de test y calcula las métricas RMSE y MAE. ¿Consigue el modelo ARIMA superar al benchmark estacional?
RMSE
MAE
SNAIVE
19729.19
15348.43
Holt-Winters
37322.71
31056.36
ARIMA
19372.17
13805.03
💡 Pista: Usa la función accuracy(). Para Holt-Winters y ARIMA, primero debes generar el objeto de predicción con forecast().
13.5.2 Tarea 4.2: Visualización del Ganador
Dibuja el pronóstico del modelo que haya obtenido el menor error, comparándolo con los datos reales de diciembre.
💡 Pista: Usa autoplot() sobre el forecast ganador y añade autolayer() con la serie de test.