Universidad del Valle - Gestión de Datos
Melissa Betancourt España (2415345)
David
Elizalde (2438289)
Oscar Eduardo Males Muñoz (2418427)
Samuel
Ruiz Restrepo (2341122)
Para comprender el comportamiento climático de una región, la temperatura constituye una de las variables de mayor interés, ya que influye en actividades como la agricultura, el transporte, la gestión del riesgo e incluso en la planificación de muchas actividades cotidianas. Debido a que cambia constantemente como respuesta a diferentes condiciones atmosféricas, analizar su comportamiento permite obtener una visión más clara de cómo evoluciona a lo largo del tiempo.
Contar con registros continuos de temperatura hace posible ir más allá de una descripción puntual de los datos. En lugar de observar cada medición de forma aislada, es posible analizar la evolución de la variable durante un periodo determinado, identificar regularidades en su comportamiento y explorar si la información histórica permite anticipar lo que puede ocurrir en los siguientes instantes.
A partir de esta idea surge una pregunta que orienta el desarrollo del trabajo: ¿es posible describir el comportamiento de la temperatura utilizando sus registros históricos y emplear esa información para realizar pronósticos de corto plazo? Resolver esta inquietud implica estudiar la información disponible, reconocer las características que presenta la serie y evaluar si existe un modelo capaz de representar adecuadamente su dinámica.
Con este propósito, en el presente trabajo se analiza la serie de temperatura horaria registrada en la ciudad de Santiago de Cali durante el año 2018. A partir del análisis exploratorio y del ajuste de un modelo ARIMA, se busca describir el comportamiento de la serie y evaluar su capacidad para generar pronósticos de corto plazo sobre la evolución de la temperatura.
Para este propósito se optó por la metodología ARIMA, desarrollada por Box y Jenkins (1970) y ampliamente utilizada en el análisis de variables ambientales con estructura temporal (Hyndman & Athanasopoulos, 2021). Esta metodología no requiere supuestos externos sobre la distribución de los datos y trabaja directamente con la estructura interna de la serie, capturando simultáneamente la dependencia entre observaciones pasadas y los errores de predicción anteriores.
La temperatura, al igual que muchas otras variables ambientales, presenta cambios a medida que transcurre el tiempo. Buscamos describir su comportamiento utilizando herramientas propias del análisis de series de tiempo, con el fin de reconocer patrones y estudiar la evolución de la información registrada.
Una serie de tiempo corresponde a un conjunto de observaciones registradas sobre una misma variable en diferentes momentos. Su principal característica es que cada dato conserva un orden cronológico, ya que representa un instante específico dentro del comportamiento de la variable. Por esta razón, el tiempo pasa a ser un elemento esencial del análisis y no únicamente una referencia para organizar la información.
A diferencia de otros tipos de datos, donde cada observación puede estudiarse de forma independiente, en una serie de tiempo los registros mantienen una relación entre sí. Esto hace que el comportamiento observado en un momento pueda guardar cierta conexión con lo ocurrido anteriormente. Más que analizar valores aislados, el interés está en comprender cómo evoluciona la variable conforme transcurre el tiempo y qué patrones pueden reconocerse a partir de esa evolución.
Durante el análisis es común encontrar comportamientos que se repiten o variaciones que permanecen a lo largo del periodo de estudio. Identificar estas características permite obtener una visión más clara de la dinámica de la serie y sirve como punto de partida para construir modelos que describan su comportamiento. En este sentido, una serie de tiempo no busca únicamente organizar datos en orden cronológico, sino aprovechar esa secuencia para extraer información sobre la forma en que cambia la variable.
Dentro de una serie de tiempo pueden identificarse distintos tipos de comportamiento. La tendencia refleja si la variable presenta una dirección sostenida al alza o a la baja a lo largo del tiempo. La estacionalidad corresponde a patrones que se repiten con una frecuencia fija, como el ciclo diario de temperatura que sube durante el día y baja en la madrugada. La autocorrelación, por su parte, mide en qué medida el valor actual de la serie depende de sus valores pasados, aspecto fundamental para construir modelos que aprovechen esa estructura. Reconocer estas características es el punto de partida para seleccionar el modelo más adecuado.
Para el desarrollo del trabajo se utilizó una base de datos con registros meteorológicos correspondientes al año 2018 para la ciudad de Santiago de Cali. Entre las variables disponibles se seleccionó la temperatura horaria, debido a que sus mediciones fueron registradas de manera continua durante todo el periodo de estudio, permitiendo conservar la secuencia temporal necesaria para este tipo de análisis.
Contar con observaciones distribuidas a lo largo del tiempo hace posible estudiar la evolución de la variable y analizar la forma en que sus valores cambian entre un registro y otro. Antes de plantear cualquier modelo de pronóstico, resulta necesario conocer el comportamiento de la información disponible, verificar su calidad y reconocer las características que presenta la serie. Este proceso permite establecer una base sobre la cual posteriormente pueden aplicarse técnicas de modelado, como el modelo ARIMA, para describir el comportamiento de la temperatura y realizar estimaciones de corto plazo.
La base contiene 8.760 registros horarios que cubren el periodo del 1 de enero al 31 de diciembre de 2018, con una observación por hora. Las variables disponibles incluyen ozono, velocidad y dirección del viento, humedad relativa, radiación solar y lluvia. Se seleccionó la temperatura por ser la variable con mayor continuidad temporal y por presentar un comportamiento cíclico marcado que la hace apropiada para el análisis con modelos ARIMA. Se identificaron 634 valores faltantes, equivalentes al 7.2% de las observaciones, los cuales fueron tratados antes del modelado.
Cali presenta condiciones climáticas particulares propias de un valle interandino: temperatura media anual cercana a los 28 °C, alta radiación solar y régimen bimodal de lluvias (IDEAM, 2018). Estas características hacen que la temperatura horaria presente ciclos diurnos marcados, con valores mínimos en la madrugada y máximos en las horas de mayor insolación, lo que la convierte en una variable con estructura temporal rica y apropiada para el modelado con ARIMA.
Es una técnica utilizada en el análisis de series de tiempo para describir el comportamiento de una variable a partir de la información registrada en periodos anteriores. La idea principal de este modelo es que los valores observados a lo largo del tiempo conservan cierta relación entre sí, de manera que el comportamiento pasado puede aportar información útil para comprender cómo evoluciona la serie y realizar estimaciones sobre su comportamiento futuro.
Su nombre proviene de las siglas AutoRegressive Integrated Moving Average, las cuales hacen referencia a los tres componentes que conforman el modelo. El componente autorregresivo utiliza la información de observaciones anteriores para explicar el valor actual de la serie. El componente integrado corresponde al proceso de diferenciación que, cuando es necesario, ayuda a estabilizar el comportamiento de los datos. Finalmente, el componente de medias móviles incorpora la información contenida en los errores obtenidos en periodos previos, permitiendo representar de mejor manera la dinámica de la serie.
No todas las series de tiempo presentan el mismo comportamiento, por lo que un único modelo ARIMA no resulta adecuado para todos los casos. Cada conjunto de datos posee características propias que hacen necesario evaluar distintas configuraciones antes de elegir aquella que mejor represente la información disponible, en este proceso no solo interesa obtener un buen ajuste, sino también verificar que el modelo describa de forma adecuada la estructura de la serie. Por este motivo, el ajuste de un modelo ARIMA va más allá de estimar algunos parámetros. Antes de construir el modelo es necesario revisar el comportamiento de la serie e identificar si cumple las condiciones requeridas para su análisis. Una vez ajustado, también se evalúa si los residuos conservan algún patrón o si, por el contrario, representan únicamente variaciones aleatorias. Esta revisión permite establecer si el modelo logra explicar la mayor parte de la información contenida en la serie. En el desarrollo de este trabajo se ajustaron diferentes configuraciones del modelo ARIMA y posteriormente se compararon mediante criterios estadísticos de ajuste. A partir de esa comparación se seleccionó el modelo que presentó el mejor desempeño para representar el comportamiento de la temperatura y generar los pronósticos correspondientes.
El modelo ARIMA se parametriza con tres componentes: p, el orden autorregresivo que indica cuántos valores pasados de la serie se usan para predecir el valor actual; d, el número de diferenciaciones necesarias para que la serie sea estacionaria; y q, el orden de media móvil que incorpora los errores de predicción anteriores. La estacionariedad se verificó mediante la prueba Augmented Dickey-Fuller (ADF), cuya hipótesis nula plantea que la serie no es estacionaria, rechazarla con un p-value menor a 0.05 indica que la serie sí lo es. Las funciones ACF y PACF se utilizaron como herramienta visual para identificar los órdenes p y q candidatos: el ACF muestra la correlación de la serie con sus rezagos y el PACF aísla esa correlación controlando los rezagos intermedios. Finalmente, la serie se dividió en una ventana de entrenamiento, de enero a noviembre de 2018, y una ventana de prueba correspondiente a diciembre, con el fin de evaluar la capacidad predictiva del modelo sobre datos que no participaron en su ajuste.
library(kableExtra)
library(gridExtra)
library(lubridate)
library(tidyverse)
library(forecast)
library(tseries)
library(readxl)
library(plotly)
library(knitr)
library(dplyr)
library(xts)datos <- read_excel("Database.T4.xlsx")
# Convertir fechas (readxl las importa como POSIXct automáticamente)
datos$`Fecha & Hora` <- as.POSIXct(datos$`Fecha & Hora`)
# Seleccionar variable de interés
temp <- datos %>%
select(`Fecha & Hora`, `Temperatura (C°)`)
head(temp)## # A tibble: 6 × 2
## `Fecha & Hora` `Temperatura (C°)`
## <dttm> <dbl>
## 1 2018-01-01 01:00:00 25.2
## 2 2018-01-01 02:00:00 24.7
## 3 2018-01-01 03:00:00 24.3
## 4 2018-01-01 04:00:00 24.4
## 5 2018-01-01 05:00:00 24.3
## 6 2018-01-01 06:00:00 23.5
Las estadísticas descriptivas permitieron obtener una primera caracterización de la temperatura registrada durante el año 2018. A partir de medidas como el mínimo, máximo, media, mediana, desviación estándar y varianza, fue posible identificar el rango de variación de la serie y evaluar el grado de dispersión de los datos. Estas medidas proporcionan un panorama general del comportamiento de la temperatura antes de iniciar el proceso de modelado.
Tabla_estadisticas <- data.frame(
Estadístico = c("Mínimo", "1er Cuartil", "Mediana", "Media",
"3er Cuartil", "Máximo", "Desviación estándar",
"Varianza", "Total observaciones", "Total NAs"),
Valor = c(
min(temp$`Temperatura (C°)`, na.rm = TRUE),
quantile(temp$`Temperatura (C°)`, 0.25, na.rm = TRUE),
median(temp$`Temperatura (C°)`, na.rm = TRUE),
mean(temp$`Temperatura (C°)`, na.rm = TRUE),
quantile(temp$`Temperatura (C°)`, 0.75, na.rm = TRUE),
max(temp$`Temperatura (C°)`, na.rm = TRUE),
sd(temp$`Temperatura (C°)`, na.rm = TRUE),
var(temp$`Temperatura (C°)`, na.rm = TRUE),
nrow(temp),
sum(is.na(temp$`Temperatura (C°)`))
)
)
kable(Tabla_estadisticas,
caption = "Tabla 1. Estadísticas descriptivas de la temperatura",digits = 2) %>%
kable_styling(full_width = FALSE,bootstrap_options = c("striped", "hover", "bordered"))| Estadístico | Valor |
|---|---|
| Mínimo | 19.90 |
| 1er Cuartil | 25.30 |
| Mediana | 27.30 |
| Media | 28.28 |
| 3er Cuartil | 31.37 |
| Máximo | 38.50 |
| Desviación estándar | 3.64 |
| Varianza | 13.23 |
| Total observaciones | 8760.00 |
| Total NAs | 634.00 |
A partir de las 8760 observaciones registradas, se encontró que la temperatura presentó un valor mínimo de 19.9 °C y un máximo de 38.5 °C, evidenciando una amplitud térmica de 18.6 °C a lo largo del periodo de estudio. La media fue de 28.28 °C, mientras que la mediana alcanzó los 27.30 °C, lo que indica que la mayor parte de las observaciones se concentraron alrededor de estos valores.
Por otra parte, desviación estándar de 3.729 °C y la varianza de 13.907 °C muestran una variabilidad moderada en la temperatura registrada, el resumen estadístico evidenció la presencia de 634 valores faltantes, lo que hizo necesaria una revisión de la calidad de la información antes de continuar con el análisis.
Tabla_NAs <- data.frame(
Categoria = c("Total de NAs", "No NAs", "Sí NAs"),
Valor = c(
sum(is.na(temp$`Temperatura (C°)`)),
table(is.na(temp$`Temperatura (C°)`))[1],
table(is.na(temp$`Temperatura (C°)`))[2]
)
)
# Mostrar tabla con estilo
kable(
Tabla_NAs,
caption = "Tabla 2. Conteo de valores faltantes en la temperatura",
digits = 0
) %>%
kable_styling(
full_width = FALSE,
bootstrap_options = c("striped", "hover", "bordered")
)| Categoria | Valor | |
|---|---|---|
| Total de NAs | 634 | |
| FALSE | No NAs | 8126 |
| TRUE | Sí NAs | 634 |
# Imputación por interpolación lineal
temp$`Temperatura (C°)` <- na.interp(temp$`Temperatura (C°)`)
temp$`Temperatura (C°)` <- as.numeric(temp$`Temperatura (C°)`)
cat("NAs después de imputación:", sum(is.na(temp$`Temperatura (C°)`)), "\n")## NAs después de imputación: 0
La revisión de la calidad de los datos permitió identificar la presencia de 634 valores faltantes dentro de las 8760 observaciones de la serie, lo que representa aproximadamente el 7.2 % del total de los registros. Debido a que los valores ausentes pueden afectar el análisis y el ajuste de modelos de series de tiempo, se realizó un proceso de imputación mediante interpolación lineal utilizando la función na.interp().
ht <- ggplot(temp, aes(x = `Temperatura (C°)`)) +
geom_histogram(aes(y = ..count..), bins = 30,
fill = "#00B4D8", color = "white", alpha = 0.8) +
geom_vline(aes(xintercept = mean(temp$`Temperatura (C°)`, na.rm = TRUE)),
color = "red", linetype = "dashed", linewidth = 1) +
geom_vline(aes(xintercept = median(temp$`Temperatura (C°)`, na.rm = TRUE)),
color = "blue", linetype = "dotted", linewidth = 1) +
labs(title = "Distribución de la temperatura en Cali (2018)",
x = "Temperatura (°C)", y = "Frecuencia") +
theme_minimal()
ggplotly(ht)El histograma permite observar la distribución de la temperatura registrada durante el año 2018 en la ciudad de Santiago de Cali. Se aprecia que la mayor parte de las observaciones se concentra entre los 24 °C y 27 °C, mientras que otro grupo importante de registros se ubica entre los 32 °C y 34 °C. En contraste, las temperaturas más bajas y las más altas se presentan con una menor frecuencia.
De manera general, la distribución evidencia que la temperatura no se mantiene constante a lo largo del año, sino que presenta diferentes concentraciones de valores. Este comportamiento refleja la variabilidad propia de una serie de registros horarios y ofrece una primera aproximación a la dinámica de la temperatura antes de realizar el análisis mediante el modelo ARIMA.
BT<- ggplot(temp, aes(y=`Temperatura (C°)`)) +
geom_boxplot(fill="#00B4D8", color="#023E8A", outlier.color="red") +
labs(title="Temperatura en Cali (2018)",
x="", y="Temperatura (°C)") +
theme_minimal()
ggplotly(BT)El diagrama de caja permite resumir la distribución de la temperatura a partir de medidas de posición y dispersión. Se observa que la mediana se ubica alrededor de los 27 °C, indicando que aproximadamente la mitad de las observaciones se encuentran por encima de este valor y la otra mitad por debajo. De igual forma, el rango intercuartílico muestra que la mayor concentración de los datos se encuentra entre aproximadamente 25 °C y 32 °C.
Por otra parte, el gráfico no evidencia la presencia de valores atípicos, lo que sugiere que las temperaturas registradas se mantienen dentro de un comportamiento relativamente uniforme durante el periodo analizado. Esto indica que la serie no presenta observaciones extremas que puedan afectar significativamente el proceso de modelado y pronóstico.
temp_mes <- temp %>%
mutate(Mes = month(`Fecha & Hora`, label = TRUE, abbr = FALSE))
HT_mensual <- ggplot(temp_mes, aes(x = Mes, y = `Temperatura (C°)`, fill = Mes)) +
geom_boxplot() +
scale_fill_brewer(palette = "RdYlBu") +
labs(title = "Temperatura por mes en Cali año 2018",
x = "Mes", y = "Temperatura (°C)") +
theme_minimal() +
theme(legend.position = "none")
ggplotly(HT_mensual)Los diagramas de caja mensuales permiten comparar la distribución de la temperatura a lo largo del año. Se observa que, aunque la mediana se mantiene cercana a los 27°C en la mayoría de los meses, la amplitud y la dispersión varían según la época. Algunos meses muestran una mayor concentración de valores en torno a la mediana, mientras que otros presentan una variabilidad más amplia. Esta comparación evidencia la dinámica estacional de la temperatura y complementa el análisis global, mostrando cómo la variabilidad cambia entre periodos.
Durante el mes de febrero se aprecia una concentración marcada de temperaturas alrededor de los 33°C, lo que sugiere episodios puntuales de mayor calor, posiblemente asociados a condiciones atmosféricas específicas o a una menor nubosidad en ciertos días.
Al revisar los datos horarios se puede identificar que los valores más altos de temperatura ocurren consistentemente entre las 13:00 y las 16:00 horas, mientras que los mínimos se registran entre las 5:00 y las 7:00 de la mañana, patrón consistente con el ciclo de radiación solar típico del valle del Cauca (IDEAM, 2018). Esta oscilación diaria representa el principal reto para el modelado con ARIMA estándar.
t_serie <- ggplot(temp, aes(x = `Fecha & Hora`, y = `Temperatura (C°)`)) +
geom_line(color = "#023E8A", linewidth = 0.4) +
labs(title = "Serie temporal de temperatura horaria en Cali (2018)",
x = "Fecha", y = "Temperatura (°C)") +
theme_minimal()
ggplotly(t_serie)La representación de la serie temporal permite analizar la evolución histórica de la temperatura registrada en Santiago de Cali durante el año 2018. A diferencia de los gráficos descriptivos anteriores, esta visualización no solo muestra el rango de valores que puede tomar la variable, sino también la forma en que estos cambian a medida que transcurre el tiempo. Esto resulta de especial importancia en el análisis de series de tiempo, ya que el orden cronológico de las observaciones permite comprender la dinámica de la serie e identificar comportamientos que no pueden apreciarse únicamente mediante medidas descriptivas.
Al observar la evolución de la serie, se aprecia que la temperatura presenta fluctuaciones continuas durante todo el periodo de estudio, comportamiento esperado al tratarse de una variable climática influenciada por factores atmosféricos y por el ciclo diario entre el día y la noche. Aunque las oscilaciones son frecuentes, no se evidencia una tendencia creciente o decreciente sostenida, lo que sugiere que la temperatura conserva un comportamiento relativamente estable alrededor de un rango similar durante el año analizado.
Asimismo, puede observarse que la amplitud de las variaciones se mantiene relativamente constante, sin cambios bruscos o alteraciones prolongadas que indiquen modificaciones importantes en la dinámica de la serie. En conjunto, estas características muestran que la información presenta una estructura temporal definida, aspecto que justifica la aplicación de modelos de series de tiempo como ARIMA para describir el comportamiento de la temperatura y realizar pronósticos de corto plazo.
Al comparar los boxplots mensuales con la serie temporal completa se observa coherencia entre ambas representaciones: los meses con mayor dispersión como febrero y agosto corresponden a periodos de mayor variabilidad en la serie. Por el contrario, meses asociados al régimen lluvioso del valle del Cauca muestran medianas más bajas y menor amplitud, lo que sugiere que las temporadas húmedas moderan tanto el nivel como la variabilidad de la temperatura (IDEAM, 2018).
# Crear serie xts
accion <- xts(temp$`Temperatura (C°)`, order.by = temp$`Fecha & Hora`)
# Ventana de entrenamiento: enero a noviembre 2018
ventana <- window(accion, end = "2018-11-30 23:00:00")
# Ventana de prueba: diciembre 2018
ventana2 <- window(accion, start = "2018-12-01 00:00:00")
Tabla_ventanas <- data.frame(
Ventana = c("Entrenamiento", "Prueba"),
Periodo = c("Enero - Noviembre 2018", "Diciembre 2018"),
Observaciones = c(length(ventana), length(ventana2))
)
kable(Tabla_ventanas,caption = "División de la serie en ventanas de entrenamiento y prueba", digits = 0) %>%
kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover", "bordered"))| Ventana | Periodo | Observaciones |
|---|---|---|
| Entrenamiento | Enero - Noviembre 2018 | 8015 |
| Prueba | Diciembre 2018 | 745 |
La serie se dividió en dos conjuntos. La ventana de entrenamiento, que comprende de enero a noviembre de 2018 con 8.015 observaciones, se utilizó para ajustar los modelos. La ventana de prueba, correspondiente a diciembre de 2018 con 745 observaciones, se reservó exclusivamente para evaluar la capacidad predictiva sobre datos que el modelo no vio durante el ajuste, simulando una aplicación real de pronóstico.
df_ventana <- data.frame(
Fecha = index(ventana),
Temperatura = coredata(ventana)
)
e_ventana <- ggplot(df_ventana, aes(x = Fecha, y = Temperatura)) +
geom_line(color = "#023E8A", linewidth = 0.4) +
labs(title = "Temperatura horaria - Ventana de entrenamiento (ene-nov 2018)",
x = "Fecha", y = "Temperatura (°C)") +
theme_minimal()
ggplotly(e_ventana)p_acf <- ggAcf(ventana) + ggtitle("ACF - Serie original de temperatura")+
theme_minimal()
ggplotly(p_acf)adf_result <- adf.test(as.numeric(ventana))
Tabla_adf <- data.frame(
Estadístico = round(adf_result$statistic, 3),
`Valor p` = round(adf_result$p.value, 4),
`N.rezagos` = adf_result$parameter,
H.nula = "La serie no es estacionaria",
H.alternativa = adf_result$alternative
)
kable(Tabla_adf,
caption = "Resultados de la prueba de Dickey-Fuller aumentada",align = "c") %>%
kable_styling(full_width = FALSE,bootstrap_options = c("striped", "hover", "bordered"))| Estadístico | Valor.p | N.rezagos | H.nula | H.alternativa | |
|---|---|---|---|---|---|
| Dickey-Fuller | -5.364 | 0.01 | 20 | La serie no es estacionaria | stationary |
Antes de ajustar cualquier modelo fue necesario revisar cómo se comportaba la serie de temperatura. Para ello se realizó la prueba ADF y se observó el gráfico ACF original, buscando tener una primera idea de la información con la que se iba a trabajar. Más que elegir un modelo desde este punto, el interés estaba en conocer si la serie presentaba un comportamiento adecuado para continuar con el análisis.
La prueba ADF arrojó un valor p = 0.01, resultado que indica que la serie puede considerarse estacionaria. Esto permitió continuar con el proceso de modelado sin necesidad de realizar transformaciones adicionales sobre la información original. Contar con una serie que cumple esta condición desde el comienzo simplifica las etapas posteriores, ya que el análisis puede centrarse en describir la dinámica de la temperatura y no en corregir el comportamiento de la serie.
Por su parte, el gráfico ACF permitió observar cómo se relacionan las observaciones a medida que aumenta la distancia entre ellas. En los primeros rezagos la relación es mayor y, conforme estos aumentan, dicha dependencia comienza a disminuir de forma gradual. Aunque este gráfico representa únicamente una primera aproximación, sí deja ver que la información conserva una estructura temporal y que los registros no se comportan como valores completamente independientes.
Aunque la prueba ADF indicó que la serie original podía considerarse estacionaria (p = 0.01), en el desarrollo del trabajo también se analizó la serie diferenciada. Esta decisión se tomó porque los modelos ARIMA evaluados incorporan una diferenciación de primer orden (d = 1) y porque el análisis de la ACF y la PACF sobre la serie diferenciada facilita la identificación de posibles componentes autorregresivos y de medias móviles. En consecuencia, la diferenciación no se realizó porque la serie incumpliera el supuesto de estacionariedad, sino para mantener coherencia con el procedimiento de modelado y con los modelos posteriormente ajustados.
# Diferenciación si es necesario
miserie <- diff(ventana) %>% na.omit()
dt <- autoplot(miserie) +
ggtitle("Serie diferenciada de temperatura") +
xlab("Fecha") +
ylab("Δ Temperatura (°C)") +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5, face = "bold"))
ggplotly(dt)adf2 <- adf.test(miserie)
Tabla_adf2 <- data.frame(
Prueba = "ADF - Serie diferenciada",
Hipótesis_nula = "La serie NO es estacionaria",
Hipótesis_alternativa = adf2$alternative,
Estadístico = round(adf2$statistic, 3),
`P-valor` = round(adf2$p.value, 4),
Rezagos = adf2$parameter
)
kable(
Tabla_adf2,
caption = "Resultados de la prueba ADF sobre la serie diferenciada",
align = "c"
) %>%
kable_styling(
bootstrap_options = c("striped", "hover", "condensed"),
full_width = FALSE,
position = "center"
)| Prueba | Hipótesis_nula | Hipótesis_alternativa | Estadístico | P.valor | Rezagos | |
|---|---|---|---|---|---|---|
| Dickey-Fuller | ADF - Serie diferenciada | La serie NO es estacionaria | stationary | -53.483 | 0.01 | 20 |
La prueba ADF aplicada sobre la serie diferenciada también arrojó un p-valor de 0.01, confirmando que la serie diferenciada conserva la condición de estacionariedad. Este resultado es consistente con los modelos ajustados que incorporan una diferenciación de primer orden (d = 1), aunque la serie original ya presentaba evidencia de estacionariedad según la prueba ADF inicial.
p_acf <- ggAcf(miserie) + ggtitle("ACF - Serie diferenciada")
p_acf_interactivo <- ggplotly(p_acf)
p_pacf <- ggPacf(miserie) + ggtitle("PACF - Serie diferenciada")
p_pacf_interactivo <- ggplotly(p_pacf)
subplot(p_acf_interactivo, p_pacf_interactivo, nrows = 1, shareX = TRUE)Después de conocer el comportamiento general de la serie se analizaron los gráficos ACF y PACF de la serie diferenciada. En esta etapa el interés ya no era comprobar si la serie cumplía las condiciones iniciales, sino observar con un poco más de detalle cómo se distribuía la relación entre las observaciones y qué información podían aportar los primeros rezagos para el proceso de modelado.
Al revisar ambos gráficos se aprecia que algunas observaciones anteriores sobresalen más que otras, mientras que la mayoría permanece dentro de los límites esperados. Este comportamiento ofrece una referencia sobre los órdenes que podrían describir de mejor manera la dinámica de la serie y permite reducir las alternativas antes de comenzar el ajuste de los modelos. No significa que los parámetros queden definidos únicamente con estos gráficos, pero sí proporcionan una guía bastante útil para orientar esa decisión.
En el ACF se observan rezagos significativos en las primeras posiciones y una reaparición de valores altos alrededor del rezago 24, lo que refleja el ciclo diario de temperatura propio de una serie horaria. El PACF, por su parte, muestra un corte más marcado después del primer rezago, con algunos valores que vuelven a sobresalir cerca del rezago 24. Estas características orientaron la selección de los modelos manuales y también permitieron anticipar que la dinámica de la serie presenta una estructura cíclica que un modelo ARIMA estándar puede no capturar completamente.
# auto.arima sugiere el mejor modelo automáticamente
modelo_auto <- auto.arima(ventana, seasonal = TRUE)
summary(modelo_auto)## Series: ventana
## ARIMA(4,1,3)
##
## Coefficients:
## ar1 ar2 ar3 ar4 ma1 ma2 ma3
## 1.5851 -0.2081 -0.6081 0.1527 -1.2343 -0.2935 0.5643
## s.e. 0.1094 0.2452 0.1784 0.0387 0.1078 0.2018 0.0966
##
## sigma^2 = 0.5664: log likelihood = -9092.15
## AIC=18200.3 AICc=18200.32 BIC=18256.21
##
## Training set error measures:
## ME RMSE MAE MPE MAPE MASE
## Training set 0.0003124823 0.7522422 0.5378878 -0.05735578 1.917787 0.01882667
## ACF1
## Training set -0.0006722165
El modelo seleccionado automáticamente fue un ARIMA(4,1,3), lo que indica que el valor actual de la temperatura depende de los cuatro valores horarios anteriores y de los tres errores de predicción más recientes, con una diferenciación para estabilizar la serie. Este nivel de complejidad sugiere que la dinámica horaria de la temperatura en Cali requiere capturar dependencias de mediano alcance para ser bien representada.
# Modelos alternativos
modelo1 <- Arima(ventana, order = c(1, 1, 1))
modelo2 <- Arima(ventana, order = c(3, 1, 1))
# Tabla comparativa de AICc
Tcm <- data.frame(
Modelo = c(as.character(modelo_auto), "ARIMA(1,1,1)", "ARIMA(3,1,1)"),
AIC = c(modelo_auto$aic, modelo1$aic, modelo2$aic),
AICc = c(modelo_auto$aicc, modelo1$aicc, modelo2$aicc)
) %>% arrange(AICc)
kable(Tcm,
caption = "Comparación de modelos ARIMA según criterios de información",digits = 2,
align = "c") %>%
kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover", "bordered"))| Modelo | AIC | AICc |
|---|---|---|
| ARIMA(4,1,3) | 18200.30 | 18200.32 |
| ARIMA(3,1,1) | 20301.29 | 20301.30 |
| ARIMA(1,1,1) | 20314.10 | 20314.10 |
Con las configuraciones planteadas se procedió a comparar el comportamiento de cada modelo. Para ello se evaluó el modelo obtenido mediante auto.arima junto con dos configuraciones ajustadas manualmente, ARIMA(1,1,1) y ARIMA(3,1,1). La comparación se realizó utilizando los criterios AIC y AICc, ya que estos permiten establecer cuál de los modelos representa mejor la información sin incorporar una cantidad innecesaria de parámetros.
Los resultados muestran una diferencia bastante clara entre las alternativas evaluadas. El modelo ARIMA(4,1,3) obtuvo los menores valores tanto de AIC (18200.30) como de AICc (18200.32), mientras que los otros dos modelos registraron valores cercanos a 20300. Esta diferencia indica que la configuración seleccionada automáticamente consigue un mejor ajuste sobre la serie de temperatura y representa de forma más adecuada la información disponible.
Aun así, un buen ajuste no siempre garantiza que el modelo sea la mejor alternativa para realizar pronósticos. Por esa razón fue necesario revisar también las métricas de precisión y comprobar si esa ventaja observada mediante los criterios de información se mantenía al evaluar los errores generados durante el ajuste.
acc_auto <- accuracy(modelo_auto)
acc_m1 <- accuracy(modelo1)
acc_m2 <- accuracy(modelo2)
Tabla_accuracy <- data.frame(
Modelo = c("Auto ARIMA(4,1,3)", "ARIMA(1,1,1)", "ARIMA(3,1,1)"),
ME = c(round(acc_auto[1,"ME"], 3), round(acc_m1[1,"ME"], 3), round(acc_m2[1,"ME"], 3)),
RMSE = c(round(acc_auto[1,"RMSE"], 3), round(acc_m1[1,"RMSE"], 3), round(acc_m2[1,"RMSE"], 3)),
MAE = c(round(acc_auto[1,"MAE"], 3), round(acc_m1[1,"MAE"], 3), round(acc_m2[1,"MAE"], 3)),
MPE = c(round(acc_auto[1,"MPE"], 3), round(acc_m1[1,"MPE"], 3), round(acc_m2[1,"MPE"], 3)),
MAPE = c(round(acc_auto[1,"MAPE"], 3), round(acc_m1[1,"MAPE"], 3), round(acc_m2[1,"MAPE"], 3)),
MASE = c(round(acc_auto[1,"MASE"], 3), round(acc_m1[1,"MASE"], 3), round(acc_m2[1,"MASE"], 3)),
ACF1 = c(round(acc_auto[1,"ACF1"], 3), round(acc_m1[1,"ACF1"], 3), round(acc_m2[1,"ACF1"], 3))
)
kable(
Tabla_accuracy,
caption = "Comparación de métricas de precisión para modelos ARIMA",align = "c") %>%
kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover", "bordered"))| Modelo | ME | RMSE | MAE | MPE | MAPE | MASE | ACF1 |
|---|---|---|---|---|---|---|---|
| Auto ARIMA(4,1,3) | 0 | 0.752 | 0.538 | -0.057 | 1.918 | 0.019 | -0.001 |
| ARIMA(1,1,1) | 0 | 0.859 | 0.591 | 0.041 | 2.080 | 0.021 | -0.001 |
| ARIMA(3,1,1) | 0 | 0.858 | 0.591 | 0.044 | 2.079 | 0.021 | 0.004 |
Después de comparar los modelos mediante los criterios AIC y AICc, se revisaron las métricas de precisión obtenidas con la función accuracy. Esta comparación permite observar qué tan cercanos se encuentran los valores estimados respecto a los datos originales y comprobar si el modelo que presentó el mejor ajuste también mantiene un buen desempeño al evaluar sus errores.
Los resultados muestran que el modelo ARIMA(4,1,3) obtuvo los valores más bajos en todas las métricas. Su RMSE de 0.752 frente a 0.859 de los modelos manuales indica que el error de predicción es aproximadamente 14% menor. El MAE de 0.538 señala que la diferencia absoluta promedio entre el valor estimado y el real fue inferior a medio grado centígrado, lo cual es un ajuste preciso para una variable con la variabilidad que presenta la temperatura horaria en Cali. El MASE de 0.019, muy inferior a 1 en los tres casos, confirma que todos los modelos superan ampliamente a un modelo ingenuo. El ACF1 prácticamente nulo sugiere ausencia de autocorrelación de primer orden en los errores, aunque como se verá en la validación de residuales, esto no garantiza ausencia de estructura en rezagos mayores.
##
## Ljung-Box test
##
## data: Residuals from ARIMA(4,1,3)
## Q* = 221.19, df = 3, p-value < 2.2e-16
##
## Model df: 7. Total lags used: 10
##
## Ljung-Box test
##
## data: Residuals from ARIMA(1,1,1)
## Q* = 456.98, df = 8, p-value < 2.2e-16
##
## Model df: 2. Total lags used: 10
##
## Ljung-Box test
##
## data: Residuals from ARIMA(3,1,1)
## Q* = 438.63, df = 6, p-value < 2.2e-16
##
## Model df: 4. Total lags used: 10
La validación de residuales se realizó mediante la función checkresiduals(), que evalúa tres aspectos: el comportamiento de los residuos en el tiempo, su función de autocorrelación y la prueba de Ljung-Box. Esta última contrasta si los residuos son independientes entre sí, lo que indicaría que el modelo logró capturar la estructura de la serie.
En los tres modelos evaluados la prueba de Ljung-Box arrojó un p-valor menor a 2.2e-16, resultado que indica que los residuos aún conservan estructura y no se comportan como ruido blanco. Esto significa que ninguno de los modelos ajustados logra capturar completamente la dinámica de la serie de temperatura.
Este resultado era esperable dado el comportamiento de la serie. Al tratarse de registros horarios, la temperatura presenta un ciclo diario marcado que se repite cada 24 horas, subiendo durante el día y bajando en la madrugada. Este tipo de estructura estacional requiere modelos SARIMA o enfoques adicionales para ser representada adecuadamente. A pesar de esta limitación, el modelo ARIMA(4,1,3) mostró el mejor desempeño relativo entre las alternativas evaluadas, tanto en criterios de información como en métricas de precisión, por lo que fue seleccionado para generar el pronóstico.
pronostico <- forecast(modelo_auto, h = 24, level = 95)
# Construir tabla con todos los decimales
Tabla_pronostico <- data.frame(
Hora = 1:24,
Pronóstico = pronostico$mean,
`L.inferior` = pronostico$lower[,1],
`L.superior` = pronostico$upper[,1]
)
# Mostrar tabla con estilo
kable(
Tabla_pronostico,
caption = "Pronóstico de temperatura para las próximas 24 horas",align = "c") %>%
kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover", "bordered"))| Hora | Pronóstico | L.inferior | L.superior |
|---|---|---|---|
| 1 | 24.12433 | 22.64923 | 25.59944 |
| 2 | 23.91269 | 21.43350 | 26.39189 |
| 3 | 23.88324 | 20.65153 | 27.11495 |
| 4 | 23.98687 | 20.17143 | 27.80232 |
| 5 | 24.22861 | 19.98790 | 28.46933 |
| 6 | 24.57583 | 20.03998 | 29.11167 |
| 7 | 25.00837 | 20.28907 | 29.72767 |
| 8 | 25.49058 | 20.67293 | 30.30823 |
| 9 | 25.99069 | 21.13307 | 30.84831 |
| 10 | 26.47308 | 21.60673 | 31.33943 |
| 11 | 26.90646 | 22.04011 | 31.77281 |
| 12 | 27.26256 | 22.39016 | 32.13496 |
| 13 | 27.51986 | 22.63003 | 32.40969 |
| 14 | 27.66372 | 22.74812 | 32.57933 |
| 15 | 27.68786 | 22.74586 | 32.62986 |
| 16 | 27.59409 | 22.63287 | 32.55532 |
| 17 | 27.39225 | 22.42261 | 32.36188 |
| 18 | 27.09910 | 22.12882 | 32.06938 |
| 19 | 26.73714 | 21.76379 | 31.71049 |
| 20 | 26.33281 | 21.33855 | 31.32706 |
| 21 | 25.91467 | 20.86506 | 30.96427 |
| 22 | 25.51135 | 20.35930 | 30.66340 |
| 23 | 25.14965 | 19.84381 | 30.45550 |
| 24 | 24.85279 | 19.34769 | 30.35789 |
pronostico <- forecast(modelo_auto, h = 24, level = 95)
autoplot(pronostico, include = 168) +
ggtitle("Pronóstico de temperatura en Cali (próximas 24 horas)") +
xlab("Tiempo") + ylab("Temperatura (°C)") +
theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5, face = "bold"),
panel.background = element_rect(fill = "white", color = NA)
) +
scale_fill_manual(values = c("skyblue")) +
scale_color_manual(values = c("blue")) # Comparar pronóstico contra valores reales de diciembre (ventana2)
ap <- accuracy(pronostico, ventana2[1:24])
Tabla_accuracy <- as.data.frame(ap)
kable(
Tabla_accuracy,caption = "Precisión del pronóstico",align = "c") %>%
kable_styling(full_width = FALSE, bootstrap_options = c("striped", "hover", "bordered"))| ME | RMSE | MAE | MPE | MAPE | MASE | ACF1 | |
|---|---|---|---|---|---|---|---|
| Training set | 0.0003125 | 0.7522422 | 0.5378878 | -0.0573558 | 1.917787 | 0.0188267 | -0.0006722 |
| Test set | 0.6542250 | 2.3108532 | 2.0937494 | 1.4739401 | 7.750410 | 0.0732836 | NA |
El modelo ARIMA(4,1,3) generó un pronóstico para las próximas 24 horas con valores que oscilan entre los 24.1 °C y los 27.7 °C aproximadamente, rango que resulta consistente con el comportamiento histórico de la temperatura en Cali. Los intervalos de confianza al 95% se amplían progresivamente conforme avanza el horizonte de pronóstico, lo cual refleja la incertidumbre creciente que es propia de cualquier modelo predictivo. Al comparar el pronóstico contra los valores reales de diciembre mediante la función accuracy(), se obtuvo un RMSE de 2.31 en el conjunto de prueba, frente a 0.75 en el entrenamiento. Esta diferencia indica que el modelo ajusta bien la serie histórica pero presenta mayor dificultad para anticipar valores fuera de la muestra. El MAPE de 7.75% en el conjunto de prueba señala que, en promedio, el pronóstico se aleja aproximadamente un 7.8% del valor real, lo cual es una magnitud relevante para una variable como la temperatura horaria. Este comportamiento es consistente con los resultados de la validación de residuales y refuerza la idea de que la estructura cíclica diaria de la serie no es capturada completamente por el modelo, limitando su capacidad predictiva fuera del periodo de entrenamiento.
El desarrollo del trabajo permitió responder la pregunta planteada al inicio: sí es posible describir el comportamiento de la temperatura a partir de sus registros históricos y construir un modelo que genere pronósticos de corto plazo, aunque con limitaciones importantes que vale la pena señalar.
Desde el análisis descriptivo se identificó que la temperatura en Cali durante 2018 presentó un promedio de 28.28 °C y una desviación estándar de 3.729 °C, lo que refleja una variabilidad moderada sin una tendencia sostenida al alza o a la baja. La prueba ADF confirmó que la serie es estacionaria con un p-valor de 0.01, condición que simplificó el proceso de modelado al no requerir transformaciones adicionales sobre la información original.
En cuanto a los modelos evaluados, el ARIMA(4,1,3) obtenido mediante auto.arima fue claramente superior a las alternativas manuales, con un AICc de 18200.32 frente a valores cercanos a 20300 en los otros dos casos. Esta diferencia no es marginal — indica que el modelo automático captura mejor la estructura de la serie con los parámetros disponibles. Las métricas de precisión sobre el conjunto de entrenamiento refuerzan esta conclusión, con un RMSE de 0.75 y un MAPE de 1.92%.
Sin embargo, los resultados de la validación de residuales muestran que ninguno de los tres modelos logra capturar completamente la dinámica de la serie. La prueba de Ljung-Box arrojó p-valores menores a 2.2e-16 en todos los casos, lo que indica que los residuos conservan estructura y no se comportan como ruido blanco. Esto no es un error del análisis sino una limitación estructural: la temperatura horaria presenta un ciclo diario de 24 horas — valores más altos en la tarde y más bajos en la madrugada — que los modelos ARIMA estándar no están diseñados para representar explícitamente. Esta misma estructura fue visible en el ACF de la serie diferenciada, donde los rezagos alrededor del lag 24 volvían a ser significativos.
Esta limitación se refleja directamente en el pronóstico fuera de muestra. El RMSE aumentó de 0.75 en entrenamiento a 2.31 en el conjunto de prueba, y el MAPE alcanzó el 7.75%. En términos prácticos, esto significa que el modelo se aleja en promedio casi 2 grados del valor real de temperatura, lo cual puede ser aceptable para una referencia general pero insuficiente para aplicaciones que requieran precisión, como alertas de salud pública o planificación agrícola detallada.
Como siguiente paso natural sería recomendable explorar modelos SARIMA con periodo estacional de 24 horas, que incorporen explícitamente el ciclo diario identificado en el ACF y PACF de la serie diferenciada. Adicionalmente, variables como la radiación solar y la humedad relativa, disponibles en la misma base de datos, podrían incorporarse como regresores externos en un modelo ARIMAX, lo que permitiría mejorar tanto el ajuste como la capacidad predictiva fuera de muestra (Hyndman & Athanasopoulos, 2021).
Box, G. E. P., & Jenkins, G. M. (1970). Time Series Analysis: Forecasting and Control. Holden-Day.
Hyndman, R. J., & Athanasopoulos, G. (2021). Forecasting: Principles and Practice (3rd ed.). OTexts. https://otexts.com/fpp3/
IDEAM. (2018). Boletín climatológico mensual. Instituto de Hidrología, Meteorología y Estudios Ambientales de Colombia. http://www.ideam.gov.co
R Core Team. (2024). R: A Language and Environment for Statistical Computing. R Foundation for Statistical Computing. https://www.R-project.org/
Hyndman, R. J., & Khandakar, Y. (2008). Automatic time series forecasting: The forecast package for R. Journal of Statistical Software, 27(3), 1–22.