1. Introducción

La temperatura es una variable crítica para la planificación urbana, agrícola y energética en Cali, influyendo en la demanda eléctrica y la salud pública. Debido a su ubicación en el Valle del Cauca, la ciudad presenta un clima tropical cálido con baja variabilidad estacional anual (IDEAM, s.f.), aunque sujeta a fluctuaciones diarias asociadas a la radiación solar y la humedad relativa. El reto del pronóstico a corto plazo radica en determinar si esta estabilidad aparente oculta autocorrelaciones significativas que un modelo univariado pueda explotar para predecir el futuro, o si el comportamiento se aproxima al ruido blanco

Utilizando registros horarios de 2018 agregados a frecuencia diaria, este informe evalúa la capacidad de los modelos ARIMA para capturar la estructura de dependencia de los datos. El estudio busca responder:

¿Permiten los modelos ARIMA ajustados con datos de enero a noviembre de 2018 pronosticar con precisión la temperatura de diciembre del mismo año? Esta distinción entre el ajuste estadístico y la capacidad predictiva real es el eje central del análisis (Hyndman & Athanasopoulos, 2018), validando si el modelo es una herramienta robusta para la toma de decisiones.

2. Metodología

En esta sección se detalla el origen de los datos, el procesamiento de las variables y el enfoque matemático implementado para el modelamiento y pronóstico.

Los datos estadísticos provienen de registros meteorológicos oficiales de la ciudad de Cali, Colombia, correspondientes al año 2018. La base de datos original (Compartir2.xlsx) cuenta con un total de 8,760 observaciones medidas en una periodicidad horaria (las 24 horas de los 365 días del año).

Para evaluar la capacidad predictiva real del modelo frente a eventos futuros, los datos se dividieron en dos conjuntos independientes:

Conjunto de Entrenamiento (Train): Comprende desde el 1 de enero hasta el 7 de diciembre de 2018, acumulando un total de 341 observaciones históricas utilizadas para estimar los parámetros.

Conjunto de Validación (Test): Comprende desde el 8 de diciembre hasta el 31 de diciembre de 2018, con un total de 24 observaciones reservadas exclusivamente para medir la precisión del pronóstico fuera de muestra.

Tratamiento de datos faltantes: Debido a fallas puntuales de registro (que representan el 5.74% del total anual), los valores ausentes en el conjunto de entrenamiento se estimaron mediante un método de interpolación lineal (na_interpolation). Esto permitió mantener la continuidad y la tendencia natural de la serie sin alterar la estructura temporal.

Estrategia de Modelamiento (Modelos ARIMA)

Para el análisis y pronóstico de la temperatura se adoptó la metodología de Box-Jenkins mediante modelos ARIMA (p, d, q) (Modelo Autorregresivo Integrado de Media Móvil). El procedimiento se ejecutó bajo las siguientes etapas algorítmicas:

  1. Evaluación de Estacionariedad: Se verificó si las propiedades estadísticas de la serie (media y varianza) son estables en el tiempo. Para ello, se aplicó formalmente la prueba estadística de Dickey-Fuller Aumentada (ADF). Si el valor p es menor al 5%, la serie se considera estacionaria en su forma original (parámetro de diferenciación d=0).

  2. Identificación del Orden del Modelo: Se analizaron visualmente las funciones de Autocorrelación Estructural (ACF) y Autocorrelación Parcial (PACF) sobre los datos de entrenamiento para sugerir los órdenes autorregresivos (p) y de media móvil (q). De forma complementaria, se usó la función algorítmica auto.arima() para realizar una búsqueda automática orientada a minimizar los criterios de información de Akaike (AIC) y Bayesiano (BIC).

  3. Evaluación de Modelos Competidores: Con el propósito de hallar el modelo óptimo, se contrastó la estructura sugerida automáticamente frente a un modelo alternativo que incorpora una diferencia de primer orden (d=1). El criterio definitivo de selección fue la minimización del indicador AIC, que premia la bondad de ajuste y penaliza el exceso de parámetros (principio de parsimonia).

  4. Validación Estadística de Residuos: Tras la estimación de los coeficientes, se ejecutó un diagnóstico riguroso sobre los errores del modelo mediante la prueba de independencia de Ljung-Box. Se verificó formalmente que los residuos operen como un proceso de ruido blanco, garantizando que los errores sean independientes, no correlacionados y que se distribuyan de forma aproximadamente normal con media cero.

  5. Generación de Pronóstico: Empleando el modelo estadístico final validado, se proyectaron las 24 observaciones correspondientes al mes de diciembre y se compararon numéricamente contra los valores reales mediante métricas globales de error (MAE, RMSE y MAPE).

En cuanto a la descripción de las Variables, a continuación, se detalla las variables analizadas:

Temperatura Promedio Diaria: Es la variable principal del estudio (variable dependiente). Representa el promedio de la temperatura en la ciudad de Cali durante cada día del año 2018. Se mide en grados Celsius (°C). Esta es la variable que queremos modelar y pronosticar hacia el futuro.

Fecha: Es la variable de tiempo (variable independiente). Registra el día, el mes y el año de cada medición de temperatura. Sirve para organizar cronológicamente los datos en una serie de tiempo continua de 365 días.

variables no analizadas:

Velocidad del Viento (VV): Mide la rapidez con la que se mueve el aire. Se expresa en metros por segundo (m/s) o kilómetros por hora (km/h).

Dirección del Viento (DV): Indica la procedencia del viento. Se mide en grados (de 0° a 360°) según los puntos cardinales.

Humedad Relativa (HR): Mide la cantidad de vapor de agua que hay en el aire. Se expresa en porcentaje (%).

Precipitación (PP): Mide la cantidad de lluvia caída. Se expresa en milímetros (mm).

Radiación Solar (RS): Mide la energía del sol que llega a la superficie. Se expresa en vatios por metro cuadrado (W/m^2).

Presión Barométrica (PB): Es el peso que ejerce el aire sobre la superficie de la tierra. Se mide en milibares (mb) o hectopascales (hPa).

Contaminantes del Aire (O3): Son partículas y gases que miden la calidad del aire en la ciudad. Se expresan en microgramos por metro cúbico (mu g/m^3) o partes por millón (ppm).

3. Filtración de datos

Tenemos una base de datos que corresponde a registros meteorológicos de la ciudad de Cali, Colombia, recopilados con una periodicidad horaria durante el año 2018. El conjunto inicial contiene 8 variables asociadas a diferentes condiciones climáticas y ambientales, con un total de 8760 observaciones, correspondientes a las 24 horas del día durante los 365 días del año.

Datos_cali<-read_excel("~/R/Taller 4/Compartir2.xlsx")

Datos_cali

Debido a que el objetivo del estudio es realizar un modelo de pronóstico mediante series de tiempo, se seleccionó como variable de interés la temperatura, expresada en grados Celsius (°C), debido a su importancia para caracterizar el comportamiento climático de la ciudad y analizar su evolución temporal.

datos_temperatura<-Datos_cali%>%
  select(Fecha_Hora=`Fecha & Hora`,
         Temperatura= `Temperatura (C°)`)

datos_diarios <- datos_temperatura %>%
  mutate(
    Fecha = as.Date(`Fecha_Hora`)
  ) %>%
  group_by(Fecha) %>%
  summarise(
    Temperatura = mean(`Temperatura`, na.rm = TRUE)
  )
datos_diarios

Debido a que los registros originales presentaban una alta frecuencia de medición (horaria), se realizó un proceso de agregación temporal mediante el cálculo del promedio de temperatura. Este procedimiento permitió transformar los datos horarios en una serie con menor frecuencia, facilitando la identificación del comportamiento general de la temperatura a lo largo del tiempo.

El promedio fue calculado conservando el orden cronológico de las observaciones, de manera que cada valor representara el comportamiento promedio de la temperatura en el periodo correspondiente. Esta transformación permitió obtener una serie temporal continua y adecuada para la aplicación de modelos ARIMA, los cuales requieren observaciones igualmente espaciadas en el tiempo

4. Resultados descriptivos

Antes de realizar el ajuste del modelo ARIMA, se realizó un análisis exploratorio de la variable temperatura con el objetivo de identificar su comportamiento general, variabilidad y posibles patrones presentes en la serie temporal.

4.1 Estadísticas descriptivas

datos_completos <- Datos_cali %>%
  select(Fecha_Hora = `Fecha & Hora`,
         Temperatura = `Temperatura (C°)`,
         O3 = `O3  (ug/m3)`,
         Viento_vel = `Vel Viento  (m/s)`,
         Humedad = `Humedad (%)`,
         Radiacion = `Radiacion Solar (Watt/M2)`,
         Lluvia = `Lluvia (mm)`)

datos_diarios_completos <- datos_completos %>%
  mutate(Fecha = as.Date(Fecha_Hora)) %>%
  group_by(Fecha) %>%
  summarise(
    Temperatura = mean(Temperatura, na.rm = TRUE),
    O3 = mean(O3, na.rm = TRUE),
    Viento_vel = mean(Viento_vel, na.rm = TRUE),
    Humedad = mean(Humedad, na.rm = TRUE),
    Radiacion = mean(Radiacion, na.rm = TRUE),
    Lluvia = sum(Lluvia, na.rm = TRUE)
  )
estadisticas_temp <- datos_diarios_completos %>%
  summarise(
    n = sum(!is.na(Temperatura)),
    faltantes = sum(is.na(Temperatura)),
    media = mean(Temperatura, na.rm = TRUE),
    de = sd(Temperatura, na.rm = TRUE),
    minimo = min(Temperatura, na.rm = TRUE),
    q1 = quantile(Temperatura, 0.25, na.rm = TRUE),
    mediana = median(Temperatura, na.rm = TRUE),
    q3 = quantile(Temperatura, 0.75, na.rm = TRUE),
    maximo = max(Temperatura, na.rm = TRUE),
    cv = sd(Temperatura, na.rm = TRUE) / mean(Temperatura, na.rm = TRUE)
  )

estadisticas_temp %>%
  kable(digits = 2,
        col.names = c("n", "Faltantes", "Media", "Desv. Est.", "Mínimo", "Q1",
                       "Mediana", "Q3", "Máximo", "CV"),
        caption = "Estadísticas descriptivas de la temperatura diaria (°C) - Cali, 2018") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
  row_spec(0, background = "#6F4328", color = "white")
Estadísticas descriptivas de la temperatura diaria (°C) - Cali, 2018
n Faltantes Media Desv. Est. Mínimo Q1 Mediana Q3 Máximo CV
345 21 28.28 1.21 24.34 27.53 28.42 29.16 30.93 0.04

Lo primero que llama la atención es lo poco que se mueve la temperatura diaria a lo largo del año, ya que la desviación estándar es de apenas 1.21 °C alrededor de una media de 28.28 °C, lo que da un coeficiente de variación de solo 4.3%. Igualmente, esto es coherente con la ubicación geográfica de Cali, pues es una ciudad cercana al Ecuador donde no existen estaciones marcadas como en otras latitudes, por lo que la temperatura promedio diaria tiende a mantenerse dentro de un rango relativamente angosto durante todo el año, sin grandes saltos de una época a otra.

La cercanía entre la media (28.28 °C) y la mediana (28.42 °C) es otro indicio relevante, cuando ambas medidas de tendencia central son tan similares, es señal de que la distribución no está fuertemente sesgada por unos pocos valores extremos, sino que se comporta de manera razonablemente equilibrada alrededor de su centro. La pequeña diferencia entre ellas (la mediana ligeramente por encima de la media) ya insinúa una leve inclinación hacia los valores bajos, algo que se confirmará más adelante al revisar la asimetría de forma más precisa.

El rango intercuartílico —la distancia entre Q1 (27.53 °C) y Q3 (29.16 °C)— es de apenas 1.63 °C, lo que significa que la mitad central de los días del año estuvo contenida en una franja muy estrecha de temperatura. En contraste, si se compara el valor mínimo (24.34 °C) con el máximo (30.93 °C), la diferencia total a lo largo de todo el año es de 6.59 °C: un rango que, aunque mayor que el intercuartílico, sigue siendo modesto para describir el comportamiento térmico de un año completo, y confirma que los días verdaderamente extremos fueron la excepción y no la norma. Por último, de los 366 días de 2018, 21 no cuentan con un valor de temperatura disponible, es decir, un 5.74% del año. Esta cifra por sí sola no compromete la validez de las estadísticas aquí calculadas, dado que el resto de la serie (345 días) ofrece una base suficientemente amplia para describir el comportamiento típico de la temperatura en la ciudad durante el periodo analizado.

4.2 Distribución de la temperatura

Además de los estadísticos resumen, conviene observar la forma completa de la distribución de la temperatura diaria, tanto en conjunto como a través de un diagrama de caja que resuma su dispersión y valores atípicos.

p_hist_temp <- ggplot(datos_diarios_completos, aes(x = Temperatura)) +
  geom_histogram(aes(y = after_stat(density)), bins = 30,
                  fill = "#8B5E3C", alpha = 0.6, color = "white") +
  geom_density(color = "#4A2E1A", linewidth = 1) +
  labs(title = "Distribución de la temperatura promedio diaria - Cali, 2018",
       x = "Temperatura (°C)", y = "Densidad") +
  theme_minimal()

ggplotly(p_hist_temp)
## Warning: Removed 21 rows containing non-finite outside the scale range
## (`stat_bin()`).
## Warning: Removed 21 rows containing non-finite outside the scale range
## (`stat_density()`).

Con el histograma podemos ver de manera visual que la temperatura diaria en Cali no se reparte de manera simétrica alrededor de su promedio, sino que se concentra con bastante fuerza en la franja entre los 27 °C y los 30 °C, y se va “adelgazando” de forma más gradual hacia los valores bajos que hacia los altos. Esto es justamente lo que refleja una asimetría negativa, que la cola izquierda del histograma (hacia temperaturas más frías) se extiende un poco más que la derecha, es decir, los días inusualmente fríos se alejan más del promedio que los días inusualmente cálidos.

Después, en la curva de densidad superpuesta nos ayuda a leer esto sin depender únicamente de la forma de las barras, que pueden variar según cómo se agrupen los datos. Lo que muestra es una sola moda, ubicada alrededor de los 28 °C, sin que aparezcan dos o más picos separados que sugirieran, por ejemplo, dos regímenes distintos de temperatura a lo largo del año. Esto es coherente pues como tal Cali no tiene estaciones marcadas, no hay razón para esperar que la temperatura se agrupe en dos comportamientos claramente distintos (por ejemplo, “época cálida” y “época fría”), sino que fluctúa todo el año alrededor de un mismo nivel central.

p_box_general <- ggplot(datos_diarios_completos, aes(x = "", y = Temperatura)) +
  geom_boxplot(fill = "#8B5E3C", alpha = 0.7, width = 0.3, outlier.color = "#B2182B") +
  coord_flip() +
  labs(title = "Diagrama de caja general de la temperatura diaria",
       x = NULL, y = "Temperatura (°C)") +
  theme_minimal()

ggplotly(p_box_general)
## Warning: Removed 21 rows containing non-finite outside the scale range
## (`stat_boxplot()`).

El diagrama de caja resume en una sola figura lo que el histograma muestra con más detalle, pero deja más claro dónde se concentra exactamente el grueso de los datos y cuáles días se alejan de ese comportamiento habitual. La caja en sí el rango intercuartílico va de 27.53 °C a 29.16 °C, es decir, el 50% central de los días del año tuvo una temperatura promedio dentro de un rango de apenas 1.63 °C. Eso es una confirmación adicional de lo que se viene señalando desde la primera tabla de estadísticas descriptivas, la temperatura en Cali es notablemente estable día a día.

Lo más interesante de este gráfico son los dos puntos que aparecen por fuera del bigote izquierdo, marcados como atípicos. Corresponden a los dos días más fríos del año (alrededor de 24.3 °C y 25.0 °C), que se alejan lo suficiente del comportamiento típico del resto del año como para que el criterio estadístico usual. Así, también se puede destacar que solo estos datos atipicos aparecen solo en el extremo frío y no en el cálido, ya que ningún día del año fue “demasiado caliente” según este criterio, lo cual va en la misma línea que la asimetría negativa observada en el histograma. En conjunto, ambos gráficos cuentan la misma historia desde dos ángulos distintos, la temperatura de Cali es bastante predecible la mayor parte del tiempo, y cuando se aleja de lo normal, es más probable que sea hacia el lado frío que hacia el cálido.

4.5 Comportamiento histórico de la serie

p_serie_temp <- ggplot(datos_diarios_completos, aes(x = Fecha, y = Temperatura)) +
  geom_line(color = "#8B5E3C", linewidth = 0.4) +
  labs(title = "Temperatura promedio diaria en Cali - 2018",
       x = "Fecha", y = "Temperatura (°C)") +
  theme_minimal()

ggplotly(p_serie_temp)

El gráfico nos muestra que la temperatura diaria en Cali oscila de forma constante durante todo el año, con subidas y bajadas relativamente bruscas de un día a otro, sin que se distinga una tendencia de fondo, el nivel general de la serie se mantiene prácticamente igual entre el inicio y el final de 2018 (el promedio de los primeros tres meses, 28.08 °C, es casi idéntico al de los últimos tres, 28.10 °C). Esto confirma, ahora desde la propia serie y no solo desde los promedios mensuales, que no hay evidencia de un calentamiento o enfriamiento progresivo a lo largo del año.

Dentro de esa oscilación constante, el gráfico también deja ver los dos extremos del año. El día más frío fue el 1 de enero de 2018, con 24.34 °C, que coincide con uno de los valores marcados como atípicos en el diagrama de caja. El día más cálido, en cambio, fue el 12 de junio de 2018, con 30.93 °C. Ambos puntos están claramente por fuera del rango en el que se mueve la mayoría del año, lo cual es coherente con la dispersión relativamente baja que ya se había identificado en las estadísticas descriptivas, cuando la serie se aleja de su comportamiento habitual, lo hace de forma puntual y notoria, no de manera gradual.

4.6 Patrón mensual

nombres_meses <- c("Ene", "Feb", "Mar", "Abr", "May", "Jun",
                    "Jul", "Ago", "Sep", "Oct", "Nov", "Dic")

patron_mensual <- datos_diarios_completos %>%
  mutate(mes = month(Fecha)) %>%
  group_by(mes) %>%
  summarise(temp_prom = mean(Temperatura, na.rm = TRUE),
            temp_de = sd(Temperatura, na.rm = TRUE),
            .groups = "drop") %>%
  mutate(Mes = factor(nombres_meses[mes], levels = nombres_meses))

p_mensual <- ggplot(patron_mensual, aes(x = Mes, y = temp_prom, group = 1)) +
  geom_ribbon(aes(ymin = temp_prom - temp_de, ymax = temp_prom + temp_de),
              fill = "#8B5E3C", alpha = 0.2) +
  geom_line(color = "#8B5E3C", linewidth = 1) +
  geom_point(color = "#8B5E3C") +
  labs(title = "Temperatura promedio mensual en Cali (banda = ± 1 desviación estándar)",
       x = "Mes", y = "Temperatura promedio (°C)") +
  theme_minimal()

ggplotly(p_mensual)

El gráfico deja ver que el promedio mensual de temperatura no se mueve en una sola dirección a lo largo del año, sino que dibuja un patrón con dos subidas y dos bajadas,un primer pico hacia febrero-marzo, un descenso hacia abril-mayo, una segunda subida que llega a su punto más alto en agosto, y finalmente un descenso hacia noviembre antes de repuntar levemente en diciembre. El mes más cálido en promedio es agosto (29.14 °C) y el más frío es enero (27.14 °C), pero la diferencia entre ambos es de apenas 2 °C, lo cual es consistente con lo discutido antes, el cambio de una época del año a otra es bastante moderado.

Vale la pena notar que el ancho de la banda (la desviación estándar de cada mes) tampoco es igual en todos los meses. Por ejemplo, en enero la banda es notoriamente más amplia que en febrero, lo que indica que enero tuvo una mayor variabilidad día a día —algo coherente con que el día más frío de todo el año (1 de enero) ocurre justo en ese mes—, mientras que en febrero los días que sí tienen dato muestran un comportamiento más uniforme entre sí.

En conjunto todo, el patrón sugiere que la temperatura en Cali no sigue un único ciclo simple de “un pico y un valle” a lo largo del año, sino una oscilación algo más irregular, aunque siempre dentro de un rango bastante similar. Esto nos refuerza la idea de que en una ciudad cercana al Ecuador, no hay una estación claramente dominante que marque el comportamiento anual de la temperatura, sino fluctuaciones moderadas que se repiten de forma menos predecible.

p_box_mensual <- datos_diarios_completos %>%
  mutate(Mes = factor(nombres_meses[month(Fecha)], levels = nombres_meses)) %>%
  ggplot(aes(x = Mes, y = Temperatura, fill = Mes)) +
  geom_boxplot(alpha = 0.8, outlier.color = "#B2182B") +
  labs(title = "Distribución de la temperatura diaria por mes - Cali, 2018",
       x = "Mes", y = "Temperatura (°C)") +
  theme_minimal() +
  theme(legend.position = "none")

ggplotly(p_box_mensual)
## Warning: Removed 21 rows containing non-finite outside the scale range
## (`stat_boxplot()`).

Aunque los promedios mensuales no varían demasiado entre sí, el diagrama de caja por mes revela que la dispersión dentro de cada mes sí cambia de forma notable. Enero y abril son los meses con las cajas más altas y los bigotes más extendidos (un rango intercuartílico de 1.83 °C y 1.81 °C respectivamente), es decir, dentro de esos meses la temperatura varió considerablemente de un día a otro. En contraste, en febrero y junio muestran las cajas más comprimidas (apenas 0.88 °C y 0.87 °C de rango intercuartílico), lo que indica que en esos meses los días fueron entre si bastante parecidos en temperatura.

El gráfico también deja ver que los días atípicos no se reparten de manera uniforme entre los meses, por ejemplo septiembre concentra el mayor número (cuatro), seguido de junio y julio con dos cada uno, mientras que varios meses —entre ellos enero, a pesar de tener tanta dispersión interna— no presentan ningún atípico. Esto último no es contradictorio, pues un mes puede tener una caja ancha (mucha variabilidad “normal” dentro de ese mes) sin que ningún día individual se salga lo suficiente del patrón como para clasificarse como atípico bajo el criterio estadístico.

Por último, podemos notar igualmente hacia qué lado se inclinan esos atípicos, podemos ver como la mayoría de los puntos marcados —en mayo, septiembre y noviembre— aparecen por debajo de la caja, es decir, corresponden a días inusualmente fríos para ese mes. La única excepción notoria es junio, donde el atípico está por encima de la caja y corresponde, de hecho, al día más caluroso de todo el año. Este patrón —más atípicos hacia el lado frío que hacia el cálido— es consistente con la asimetría negativa identificada antes en el histograma general, los “días raros” del año tienden a ser más fríos de lo esperado, más que excepcionalmente calurosos.

4.7 Relación con otras variables meteorológicas

Aunque el modelo de la siguiente sección se ajusta de manera univariada (solo con la propia serie de temperatura), resulta útil entender con qué otras variables de la base se relaciona, como contexto para interpretar su comportamiento.

corr_temp <- datos_diarios_completos %>%
  select(Temperatura, O3, Viento_vel, Humedad, Radiacion, Lluvia) %>%
  cor(use = "pairwise.complete.obs")

corr_temp %>%
  round(2) %>%
  kable(caption = "**Matriz de correlación entre temperatura y otras variables meteorológicas (escala diaria)**") %>%
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = FALSE) %>%
  row_spec(0, background = "#6F4328", color = "white")
Matriz de correlación entre temperatura y otras variables meteorológicas (escala diaria)
Temperatura O3 Viento_vel Humedad Radiacion Lluvia
Temperatura 1.00 0.57 0.32 -0.89 0.42 -0.46
O3 0.57 1.00 0.33 -0.68 0.28 -0.24
Viento_vel 0.32 0.33 1.00 -0.40 0.04 -0.13
Humedad -0.89 -0.68 -0.40 1.00 -0.40 0.50
Radiacion 0.42 0.28 0.04 -0.40 1.00 -0.14
Lluvia -0.46 -0.24 -0.13 0.50 -0.14 1.00
p_corr_temp <- ggcorrplot(corr_temp, lab = TRUE, type = "lower",
                           colors = c("#2166AC", "white", "#8B5E3C")) +
  labs(title = "Mapa de correlación: temperatura y variables meteorológicas")
## Warning: `aes_string()` was deprecated in ggplot2 3.0.0.
## ℹ Please use tidy evaluation idioms with `aes()`.
## ℹ See also `vignette("ggplot2-in-packages")` for more information.
## ℹ The deprecated feature was likely used in the ggcorrplot package.
##   Please report the issue at <https://github.com/kassambara/ggcorrplot/issues>.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
ggplotly(p_corr_temp)

Para empezar, en el mapa de correlaciones podemos ver que la relación más fuerte que tiene la temperatura con cualquier otra variable es con la humedad relativa (-0.89), una correlación negativa muy marcada, entre más cálido está el aire, más baja tiende a estar la humedad. Esto tiene una explicación física conocida, que es que el aire caliente puede contener más vapor de agua antes de saturarse, por lo que, para una misma cantidad de agua en el ambiente, su humedad relativa resulta menor cuanto más sube la temperatura. Es esto con claridad, la relación más sólida de toda la matriz.

Después de la humedad, las correlaciones más relevantes son con el O3 (0.57) y con la lluvia (-0.46). La relación positiva con el O3 indica que los días más cálidos tienden a coincidir con los días de mayor concentración de este contaminante, mientras que la relación negativa con la lluvia muestra que los días con más precipitación suelen ser, en promedio, un poco más frescos —probablemente porque la nubosidad asociada a la lluvia reduce la radiación solar que llega a la superficie—. La correlación con la radiación solar (0.42) y con la velocidad del viento (0.32) son positivas pero más moderadas, lo que sugiere que ambas variables acompañan el comportamiento de la temperatura sin explicarlo de forma tan directa como la humedad.

En conjunto, el mapa de correlación deja ver que la temperatura no se mueve de forma aislada, sino como parte de un mismo sistema climático, los días cálidos tienden a ser, al mismo tiempo, días más secos, con más sol y menos lluvia, mientras que los días más frescos tienden a presentar el patrón contrario. Ninguna de estas relaciones, sin embargo, es absoluta (ningún valor se acerca a ±1), lo cual es razonable, el clima de un día determinado depende de la combinación de varios factores a la vez, y no de uno solo que lo explique por completo.

5. Serie de tiempos: Temperatura (C°)

La serie seleccionada corresponde a la temperatura promedio diaria de la ciudad de Cali durante el año 2018. Debido a que los datos originales presentaban una frecuencia horaria, se realizó una agregación diaria mediante el cálculo del promedio de las observaciones registradas durante cada fecha. Esta transformación permitió obtener una serie con 365 observaciones, facilitando el análisis del comportamiento temporal y la construcción del modelo ARIMA.

serie_temp <- xts(
  datos_diarios$Temperatura,
  order.by = datos_diarios$Fecha
)
plot(serie_temp, main="Temperatura en cali en 2018")

La gráfica representa el comportamiento temporal de la temperatura promedio diaria registrada en la ciudad de Cali durante el año 2018. Se observa que la serie presenta fluctuaciones alrededor de un nivel promedio relativamente estable, evidenciando variaciones naturales asociadas a las condiciones climáticas diarias. No se identifica una tendencia creciente o decreciente marcada, lo cual indica que la temperatura mantiene un comportamiento aproximadamente constante durante el periodo analizado. Sin embargo, se presentan cambios puntuales entre días consecutivos, reflejando la variabilidad propia de una variable meteorológica. Por otro lado se evidencia huecos, es decir hay fechas donde no se han registrado datos, como es el caso de febrero que se evidencia a simple vista.

temperatura_train <- window(
  serie_temp,
  start = "2018-01-01 ",
  end = "2018-12-07 "
)

temperatura_test <- window(
  serie_temp,
  start = "2018-12-08 ",
  end = "2018-12-31"
)
tablal<-data.frame(length(temperatura_train))
tablal

La division de entrenamiento se realizo desde el 1 de enero hasta el 7 de diciembre del año 2018. Un lapso de tiempo coherente para el modelo que se ajusta con información histórica y pronostica observaciones futuras.

tablale<-data.frame(length(temperatura_test))
tablale

Se definió un pronóstico de 24 observaciones, que abarca desde el 8 hasta el 31 de diciembre de 2018. Esta elección corresponde a las últimas semanas del año y permite evaluar cómo se comporta el modelo en un escenario de corto plazo, verificando su capacidad para anticipar resultados en un periodo reducido y cercano al cierre anual.

temperatura_train <- na_interpolation(temperatura_train)

Debido a que la variable temperatura presenta una dinámica continua en el tiempo, los valores ausentes fueron estimados mediante interpolación lineal, conservando la tendencia natural de la serie sin introducir cambios bruscos.

autoplot(temperatura_train)

La gráfica muestra que la temperatura presenta un comportamiento relativamente estable alrededor de su promedio histórico, con fluctuaciones asociadas a la variabilidad climática diaria. La diferencia entre los valores mínimos y máximos evidencia cambios naturales de temperatura durante el año, sin observarse tendencias pronunciadas.

tabla_adf<-adf.test(temperatura_train)
tabla_adf <- data.frame(
  Prueba = "Augmented Dickey-Fuller (ADF)",
  Estadistico_DF = -3.8404,
  Rezagos = 6,
  p_valor = 0.0173,
  Hipotesis_alternativa = "La serie es estacionaria"
)
tabla_adf %>%
  kable(
    caption = "**Prueba de estacionariedad Augmented Dickey-Fuller (ADF)**",
    digits = 4,
    align = "c"
  ) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover"),
    full_width = FALSE
  ) %>%
  row_spec(
    0,
    background = "#6F4328",
    color = "white"
  )
Prueba de estacionariedad Augmented Dickey-Fuller (ADF)
Prueba Estadistico_DF Rezagos p_valor Hipotesis_alternativa
Augmented Dickey-Fuller (ADF) -3.8404 6 0.0173 La serie es estacionaria

Para evaluar la estacionariedad de la serie de temperatura se aplicó la prueba de Dickey-Fuller aumentada (ADF). La hipótesis nula de esta prueba establece que la serie presenta raíz unitaria, es decir, que no es estacionaria, mientras que la hipótesis alternativa indica que la serie es estacionaria.

Los resultados muestran un estadístico Dickey-Fuller de -3.8404 y un valor p de 0.0173. Debido a que el valor p es menor que el nivel de significancia del 5% (0.0173<0.05), se rechaza la hipótesis nula y se concluye que existe evidencia estadística suficiente para considerar la serie de temperatura como estacionaria.

acf <- ggAcf(temperatura_train) +
  ggtitle("ACF de la temperatura")

ggplotly(acf)
pacf <- ggPacf(temperatura_train) +
  ggtitle("PACF de la temperatura")

ggplotly(pacf)

La función de autocorrelación (ACF) muestra una relación positiva entre las observaciones actuales y los primeros rezagos de la serie, evidenciando la presencia de dependencia temporal en la temperatura. Esta autocorrelación disminuye progresivamente conforme aumenta el número de rezagos, indicando que la influencia de valores pasados se reduce con el tiempo.

Por otro lado, la función de autocorrelación parcial (PACF) presenta un comportamiento donde el primer rezago tiene una influencia significativa, mientras que la mayoría de los rezagos posteriores permanecen dentro de los límites de confianza. Este patrón sugiere la existencia de una estructura autorregresiva de bajo orden.

A partir de este resultado se evaluaron diferentes configuraciones ARIMA con el propósito de identificar la estructura que mejor representara el comportamiento de la temperatura. Inicialmente se utilizó la función auto.arima(), la cual realiza una búsqueda automática de diferentes combinaciones de parámetros (p), (d) y (q), seleccionando el modelo que minimiza criterios de información como AIC y BIC.

Auto arima

tabla_resumen<-auto.arima(temperatura_train, seasonal = FALSE)
tabla_resumen_arima <- data.frame(
  Modelo = "ARIMA(2,0,1)",
  Media = "Sí",
  Sigma2 = 0.7419,
  Log_Likelihood = -431.38,
  AIC = 872.76,
  AICc = 872.94,
  BIC = 891.92
)


tabla_resumen_arima %>%
  kable(
    caption = "**Resumen del modelo ARIMA ajustado**",
    digits = 3,
    align = "c"
  ) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover"),
    full_width = FALSE
  ) %>%
  row_spec(
    0,
    background = "#6F4328",
    color = "white"
  )
Resumen del modelo ARIMA ajustado
Modelo Media Sigma2 Log_Likelihood AIC AICc BIC
ARIMA(2,0,1) 0.742 -431.38 872.76 872.94 891.92

El modelo que arroja la funcion auto.arima() corresponde a un ARIMA(2,0,1) con media diferente de cero, equivalente a ARMA(2,1). Esto indica que la serie fue modelada sin necesidad de aplicar diferenciación, debido a que el modelo considera la serie estacionaria en su forma original. El componente autorregresivo incluye dos rezagos, lo que significa que la temperatura actual depende de los valores observados en los dos periodos anteriores. Además, incorpora un componente de media móvil de orden uno, permitiendo capturar la influencia de errores pasados sobre la estimación actual.

No obstante se analizan mas modelos, entre los que se destaca y con el cual se realiza comparaciones en este estudio, el modelo alternativo y con diferenciacion ARIMA (2,1,1) con el objetivo de analizar si una transformación mediante diferenciación podía mejorar la capacidad predictiva. Este modelo incorpora una diferencia entre observaciones consecutivas antes de realizar la estimación, el modelo considera, dos componentes autorregresivos, Una diferenciación de primer orden y un componente de media móvil.

7. Comparacion y seleccion del modelo

Inicialmente, mediante el procedimiento automático auto.arima() se obtuvo un modelo ARIMA(2,0,1), equivalente a un modelo ARMA(2,1), debido a que presenta un valor de diferenciación igual a cero. Adicionalmente, se busca un modelo alternativo ARIMA(2,1,1), incorporando una diferenciación de primer orden con el objetivo de evaluar si el modelamiento de los cambios de temperatura permitía mejorar el desempeño del pronóstico.

Modelo ARMA(2,1)

modelo_1 <- auto.arima(
  temperatura_train,
  seasonal = FALSE
)

Parámetros estimados del modelo

tabla_coeficientes <- data.frame(
  Parametro = c("AR(1)", "AR(2)", "MA(1)", "Media"),
  Estimacion = c(1.4104, -0.4429, -0.7934, 28.2106),
  Error_estandar = c(0.1239, 0.1047, 0.1009, 0.2914)
)
tabla_coeficientes %>%
  kable(
    caption = "Coeficientes estimados del modelo ARMA(2,1)",
    digits = 4,
    align = "c"
  ) %>%
  kable_styling(
    bootstrap_options = c("striped","hover"),
    full_width = FALSE
  )
Coeficientes estimados del modelo ARMA(2,1)
Parametro Estimacion Error_estandar
AR(1) 1.4104 0.1239
AR(2) -0.4429 0.1047
MA(1) -0.7934 0.1009
Media 28.2106 0.2914

El modelo seleccionado corresponde a modelo ARMA(2,1), debido a que no requiere diferenciación de la serie. El componente autorregresivo de orden dos indica que la temperatura actual está influenciada por los valores observados en los dos periodos anteriores. El primer coeficiente autorregresivo presenta un efecto positivo (1.4104), mientras que el segundo rezago presenta una influencia negativa (-0.4429), mostrando una relación dinámica entre las observaciones pasadas.

El componente de media móvil MA(1) presenta un valor de -0.7934, indicando que los errores cometidos en periodos anteriores influyen en la estimación del valor actual. La media estimada del modelo es 28.2106 °C, representando el nivel promedio alrededor del cual fluctúa la serie.

Criterios de ajuste

tabla_ajuste1 <- data.frame(
  Modelo = "ARMA(2,1)",
  Sigma2 = 0.7419,
  Log_Likelihood = -431.38,
  AIC = 872.76,
  AICc = 872.94,
  BIC = 891.92
)
tabla_ajuste1

Los criterios de información permiten evaluar la calidad del ajuste del modelo considerando tanto la capacidad explicativa como la cantidad de parámetros utilizados. El modelo ARIMA(2,0,1) presenta un AIC de 872.76 y un BIC de 891.92, valores utilizados posteriormente para comparar diferentes especificaciones del modelo. La varianza del error estimada (σ^2=0.7419) indica la dispersión promedio de los residuos alrededor de los valores ajustados.

Errores del conjunto de entrenamiento

tabla_error1 <- data.frame(
  ME = 0.0229,
  RMSE = 0.8563,
  MAE = 0.6631,
  MPE = -0.0112,
  MAPE = 2.3753,
  MASE = 0.9441,
  ACF1 = -0.0084
)
tabla_error1

Las métricas de error permiten evaluar el desempeño del modelo sobre los datos utilizados para su estimación. El error absoluto medio (MAE) fue de 0.6631 °C, indicando que en promedio las predicciones del modelo se alejaron menos de un grado Celsius respecto a los valores observados. Asimismo, el MAPE obtenido fue de 2.375%, lo que indica un bajo porcentaje de error relativo durante el ajuste.

El valor de ACF1 cercano a cero (-0.0084) indica que los residuos presentan poca correlación en el primer rezago, lo que nos dice que el modelo logró capturar adecuadamente la dependencia temporal presente en la serie.

Diagnóstico de residuos

checkresiduals(modelo_1)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(2,0,1) with non-zero mean
## Q* = 2.6558, df = 7, p-value = 0.9149
## 
## Model df: 3.   Total lags used: 10

Después de ajustar el modelo ARMA(2,1), se realizó el análisis de los residuos mediante la función checkresiduals(), con el objetivo de verificar si los errores generados por el modelo presentan algún patrón temporal o si se comportan como un proceso aleatorio.

La serie temporal de residuos del modelo ARMA(2,1) muestra fluctuaciones alrededor de cero, sin observarse patrones sistemáticos a lo largo del tiempo. Esto indica que el modelo logró capturar adecuadamente la estructura principal de la serie de temperatura, dejando únicamente variaciones aleatorias no explicadas por el modelo.

La función de autocorrelación de los residuos evidencia que la mayoría de los coeficientes se encuentran dentro de los límites de confianza, indicando ausencia de dependencia temporal significativa. Aunque se observa una pequeña desviación en uno de los rezagos, esta no representa un comportamiento persistente en los errores.

Asimismo, la distribución de los residuos presenta una concentración alrededor del valor cero, mostrando que los errores del modelo son pequeños en la mayoría de las observaciones. La presencia de algunos valores extremos corresponde a variaciones puntuales de la temperatura que no pudieron ser explicadas completamente por el modelo.

tabla_ljung <- data.frame(
  Prueba = "Ljung-Box",
  Modelo = "ARMA(2,1)",
  Estadistico_Q = 2.6558,
  Grados_libertad = 7,
  Rezagos_evaluados = 10,
  p_valor = 0.9149,
  Decision = "No se rechaza H0"
)
tabla_ljung %>%
  kable(
    caption = "Prueba de independencia de residuos mediante Ljung-Box",
    digits = 4,
    align = "c"
  ) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover"),
    full_width = FALSE
  ) %>%
  row_spec(
    0,
    background = "#6F4328",
    color = "white"
  )
Prueba de independencia de residuos mediante Ljung-Box
Prueba Modelo Estadistico_Q Grados_libertad Rezagos_evaluados p_valor Decision
Ljung-Box ARMA(2,1) 2.6558 7 10 0.9149 No se rechaza H0

Debido a que el valor p (0.9149) es mayor al nivel de significancia del 5%, no se rechaza la hipótesis nula de independencia de los residuos. Por lo tanto, los errores del modelo pueden considerarse ruido blanco.

Modelo ARIMA(2,1,1)

modelo_2 <- Arima(
  temperatura_train,
  order = c(2,1,1)
)

El modelo ajustado para la serie temperatura_train corresponde a un ARIMA(2,1,1), lo que indica que la serie fue diferenciada una vez para lograr estacionariedad en media y que la dinámica temporal se explica mediante dos términos autorregresivos y un término de media móvil. Este tipo de estructura permite capturar tanto la dependencia con observaciones pasadas como la influencia de los errores recientes del modelo ### Parámetros estimados del modelo

tabla_coeficientes <- data.frame(
  Parametro = c("AR(1)", "AR(2)", "MA(1)"),
  Estimacion = c( 0.5343, -0.0070 , -0.8849),
  Error_estandar = c(0.0756 , 0.0647 , 0.0531)
)
tabla_coeficientes %>%
  kable(
    caption = "Coeficientes estimados del modelo ARIMA(2,1,1)",
    digits = 4,
    align = "c"
  ) %>%
  kable_styling(
    bootstrap_options = c("striped","hover"),
    full_width = FALSE
  )
Coeficientes estimados del modelo ARIMA(2,1,1)
Parametro Estimacion Error_estandar
AR(1) 0.5343 0.0756
AR(2) -0.0070 0.0647
MA(1) -0.8849 0.0531

En cuanto a los coeficientes estimados, el parámetro autorregresivo de primer orden (AR1 = 0.5343) resulta positivo y estadísticamente relevante, lo que indica una dependencia moderada con el valor inmediatamente anterior de la serie. Por otro lado, el segundo término autorregresivo (AR2 = -0.0070) es prácticamente nulo, lo que sugiere que su aporte al modelo es mínimo. El parámetro de media móvil (MA1 = -0.8849) presenta una magnitud alta y negativa, lo cual refleja una fuerte corrección de los errores pasados en la dinámica del modelo.

En el modelo ARIMA(2,1,1) no aparece el parámetro de media debido a que la serie fue diferenciada una vez (d = 1). Esta transformación elimina el nivel promedio de la serie y hace que el modelo se enfoque en la dinámica de los cambios.

Criterios de ajuste

tabla_ajuste <- data.frame(
  Modelo = "ARIMA(2,1,1)",
  Sigma2 = 0.7526,
  Log_Likelihood = -432.92,
  AIC = 873.85,
  AICc = 873.96,
  BIC = 889.16
)
tabla_ajuste

Respecto a la varianza del error, el valor estimado de σ² = 0.7526 indica una cantidad moderada de variabilidad no explicada por el modelo, lo cual es razonable para una serie de temperatura. Adicionalmente, los criterios de información obtenidos (AIC = 873.85, AICc = 873.96 y BIC = 889.16) permiten evaluar el ajuste del modelo y servirán como referencia para la comparación con otras especificaciones ARIMA.

Errores del conjunto de entrenamiento

tabla_error <- data.frame(
  ME = 0.0267,
  RMSE = 0.8624,
  MAE = 0.6617,
  MPE = 0.0171,
  MAPE = 2.3694,
  MASE = 0.9422,
  ACF1 = 0.0052
)
tabla_error

En relación con las medidas de error en el conjunto de entrenamiento, el modelo presenta un error medio (ME = 0.0268) cercano a cero, lo que indica ausencia de sesgo sistemático. Asimismo, el RMSE = 0.8624 y el MAE = 0.6618 evidencian errores de predicción relativamente bajos. El MAPE de 2.37% sugiere un buen desempeño predictivo en términos porcentuales, mientras que el MASE = 0.94 indica que el modelo supera ligeramente el desempeño de un modelo ingenuo.

Finalmente, el valor de ACF1 = 0.0052 en los residuos indica que no existe autocorrelación significativa en los errores del modelo, lo cual sugiere que estos se comportan como ruido blanco. En conjunto, estos resultados muestran que el modelo ARIMA(2,1,1) logra capturar adecuadamente la estructura temporal de la serie de temperatura y presenta un desempeño adecuado tanto en ajuste como en capacidad predictiva.

Diagnóstico de residuos

checkresiduals(modelo_2)

## 
##  Ljung-Box test
## 
## data:  Residuals from ARIMA(2,1,1)
## Q* = 2.3647, df = 7, p-value = 0.9369
## 
## Model df: 3.   Total lags used: 10

El diagnóstico de residuos del modelo ARIMA(2,1,1) permite evaluar si el modelo logró capturar adecuadamente la estructura de la serie de tiempo. En el gráfico superior se observa la evolución de los residuos a lo largo del tiempo, los cuales fluctúan alrededor de cero sin presentar una tendencia clara ni cambios sistemáticos en la varianza. Esto sugiere que el modelo no deja patrones evidentes sin explicar y que los errores se comportan de manera aproximadamente aleatoria.

En el correlograma (ACF de los residuos) se observa que la mayoría de los rezagos se encuentran dentro de las bandas de significancia, lo que indica ausencia de autocorrelación significativa. Sin embargo, se puede notar un ligero pico negativo alrededor de un rezago alto, aunque este no es suficientemente fuerte como para indicar dependencia estructural relevante. En general, este comportamiento es consistente con residuos que se aproximan a ruido blanco.

El histograma de los residuos muestra una distribución aproximadamente normal, centrada en cero y con forma simétrica. La curva teórica superpuesta ajusta razonablemente bien a la distribución empírica, aunque se observa una leve desviación en las colas, lo cual es común en series reales. Esto indica que el supuesto de normalidad de los errores es aceptable, aunque no perfecto.

tabla_2jung <- data.frame(
  Prueba = "Ljung-Box",
  Modelo = "ARIMA(2,1,1)",
  Estadistico_Q = 2.3647,
  Grados_libertad = 7,
  Rezagos_evaluados = 10,
  p_valor = 0.9369,
  Decision = "No se rechaza H0"
)
tabla_2jung %>%
  kable(
    caption = "Prueba de independencia de residuos mediante Ljung-Box",
    digits = 4,
    align = "c"
  ) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover"),
    full_width = FALSE
  ) %>%
  row_spec(
    0,
    background = "#6F4328",
    color = "white"
  )
Prueba de independencia de residuos mediante Ljung-Box
Prueba Modelo Estadistico_Q Grados_libertad Rezagos_evaluados p_valor Decision
Ljung-Box ARIMA(2,1,1) 2.3647 7 10 0.9369 No se rechaza H0

El test de Ljung-Box aplicado a los residuos del modelo ARIMA(2,1,1) permite evaluar la presencia de autocorrelación global en los errores. Los resultados obtenidos muestran un estadístico Q* = 2.3647 con 7 grados de libertad y un p-value = 0.9369. Dado que el valor p es considerablemente mayor a 0.05, no se rechaza la hipótesis nula de independencia de los residuos. Por lo tanto, no existe evidencia estadísticamente significativa de autocorrelación en los errores del modelo.

Comparación y selección del modelo final

comparacion_modelos <- data.frame(
  
  Modelo = c("ARMA(2,1)",
             "ARIMA(2,1,1)"),
  
  AIC = c(AIC(modelo_1),
          AIC(modelo_2)),
  
  BIC = c(BIC(modelo_1),
          BIC(modelo_2)),
  
  RMSE = c(
    accuracy(modelo_1)[1,"RMSE"],
    accuracy(modelo_2)[1,"RMSE"]
  ),
  
  MAE = c(
    accuracy(modelo_1)[1,"MAE"],
    accuracy(modelo_2)[1,"MAE"]
  ),
  
  MAPE = c(
    accuracy(modelo_1)[1,"MAPE"],
    accuracy(modelo_2)[1,"MAPE"]
  )
)
comparacion_modelos %>%
  kable(
    digits = 3,
    caption = "Comparación de modelos ARIMA evaluados para la temperatura"
  ) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE
  )
Comparación de modelos ARIMA evaluados para la temperatura
Modelo AIC BIC RMSE MAE MAPE
ARMA(2,1) 872.758 891.917 0.856 0.663 2.375
ARIMA(2,1,1) 873.845 889.161 0.862 0.662 2.369
tabla_jung2 <- data.frame(
  Modelo = c("ARMA(2,1)",
             "ARIMA(2,1,1)"),
  
  Q = c(2.6558,
        2.3647),
  
  df = c(7,
        7),
  
  p_value = c(0.9149,
              0.9369),
  
  Diagnostico = c("Residuos independientes",
                  "Residuos independientes")
)

knitr::kable(
  tabla_jung2,
  digits = 4,
  caption = "Prueba Ljung-Box para evaluar independencia de residuos"
)
Prueba Ljung-Box para evaluar independencia de residuos
Modelo Q df p_value Diagnostico
ARMA(2,1) 2.6558 7 0.9149 Residuos independientes
ARIMA(2,1,1) 2.3647 7 0.9369 Residuos independientes

Una vez verificado que ambos modelos cumplen con los supuestos de independencia de residuos, se realizó la comparación mediante criterios de información y medidas de desempeño predictivo. Los modelos evaluados fueron ARIMA(2,1), obtenido mediante la función automática de selección (auto.arima), y ARIMA(2,1,1), construido aplicando una diferenciación adicional a la serie.

Los resultados muestran que ambos modelos presentan un comportamiento similar; sin embargo, existen pequeñas diferencias en los indicadores evaluados. El modelo ARIMA(2,0,1) presentó el menor valor de AIC (872.758) y el menor RMSE (0.856), indicando un mejor equilibrio entre ajuste y complejidad del modelo, además de menores errores cuadráticos durante el entrenamiento.

Por otro lado, el modelo ARIMA(2,1,1) obtuvo valores ligeramente menores en BIC, MAE y MAPE, aunque las diferencias respecto al modelo ARIMA(2,0,1) fueron mínimas. Debido a que ambos modelos presentaron residuos adecuados, la selección final se basó principalmente en la capacidad predictiva y el criterio de ajuste.

En consecuencia, se seleccionó el modelo ARMA(2,1) como modelo final para realizar el pronóstico de temperatura, debido a que presentó un menor AIC y un menor RMSE, indicadores asociados con un mejor ajuste del modelo y una menor magnitud de error en las predicciones.

8. Pronostico

Una vez seleccionado el modelo ARMA(2,1) como el modelo con mejor desempeño entre las alternativas evaluadas, se realizó el pronóstico correspondiente para el periodo de prueba, conformado por los datos de temperatura del mes de diciembre de 2018. La tabla presenta la comparación entre los valores reales registrados y los valores estimados por el modelo, permitiendo evaluar la capacidad predictiva del modelo seleccionado.

pronostico <- forecast(
  modelo_1,
  h = length(temperatura_test)
)
valores_pronostico <- as.numeric(pronostico$mean)

valores_reales <- as.numeric(temperatura_test)
tabla_pronostico <- data.frame(
  Fecha = index(temperatura_test),
  Temperatura_Real = valores_reales,
  Temperatura_Pronosticada = valores_pronostico,
  Error = valores_reales - valores_pronostico
)

tabla_pronostico %>%
  kable(
    caption = "**Comparación entre valores reales y pronosticados del modelo ARIMA(2,0,1)**",
    digits = 3,
    align = "c"
  ) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover"),
    full_width = FALSE
  ) %>%
  row_spec(
    0,
    background = "#6F4328",
    color = "white"
  )
Comparación entre valores reales y pronosticados del modelo ARIMA(2,0,1)
Fecha Temperatura_Real Temperatura_Pronosticada Error
2018-12-08 28.392 27.843 0.548
2018-12-09 28.942 27.834 1.108
2018-12-10 28.771 27.842 0.929
2018-12-11 28.696 27.858 0.838
2018-12-12 29.392 27.876 1.516
2018-12-13 27.883 27.895 -0.012
2018-12-14 27.008 27.914 -0.905
2018-12-15 28.363 27.932 0.431
2018-12-16 28.778 27.949 0.830
2018-12-17 28.929 27.965 0.964
2018-12-18 29.075 27.980 1.095
2018-12-19 29.108 27.994 1.114
2018-12-20 29.071 28.007 1.064
2018-12-21 28.488 28.020 0.468
2018-12-22 27.788 28.032 -0.244
2018-12-23 28.492 28.043 0.449
2018-12-24 28.954 28.053 0.901
2018-12-25 29.762 28.063 1.700
2018-12-26 29.238 28.072 1.166
2018-12-27 29.092 28.080 1.011
2018-12-28 29.779 28.088 1.691
2018-12-29 29.546 28.096 1.450
2018-12-30 29.283 28.103 1.180
2018-12-31 29.688 28.110 1.578

Los modelos ARIMA ajustados con datos de enero a noviembre de 2018 permiten pronosticar la temperatura de diciembre del mismo año con una precisión razonable, aunque no perfecta. En general, el modelo seleccionado es capaz de reproducir el nivel promedio de la serie, ya que las temperaturas pronosticadas se mantienen cercanas a los valores reales y dentro del mismo rango de variación. Sin embargo, se observan errores diarios que en varios casos oscilan entre 0.4 y 1.7 grados, lo que evidencia que el modelo no logra capturar completamente las fluctuaciones puntuales de la serie, por ejemplo; se observa un patrón importante,en varios días el modelo subestima la temperatura real, cuando la real es 29–29.7°c, el modelo predice alrededor de 28°C esto indica un sesgo leve hacia la subestimación.

En respuesta a la pregunta planteada en el estudio, los modelos ARIMA ajustados con datos de enero a noviembre de 2018 permiten realizar pronósticos de temperatura para diciembre del mismo año con una precisión razonable. No obstante, la predicción no alcanza una precisión absoluta debido a la presencia de variaciones diarias y factores externos que no son incluidos dentro de la estructura del modelo. Por esta razón, el modelo resulta adecuado para estimar el comportamiento general de la temperatura, pero presenta limitaciones para anticipar cambios puntuales de corto plazo.

pronostico <- forecast(modelo_1, h = length(temperatura_test))
pronostico_df <- data.frame(
  Fecha = time(temperatura_test),
  Real = as.numeric(temperatura_test),
  Pronostico = as.numeric(pronostico$mean),
  LI = as.numeric(pronostico$lower[,2]),
  LS = as.numeric(pronostico$upper[,2])
)
ggplot(pronostico_df, aes(x = Fecha)) +
  geom_ribbon(aes(ymin = LI, ymax = LS),
              fill = "#736055",
              alpha = 0.4) +
  geom_line(aes(y = Real, color = "Real"),
            size = 1.2) +
  geom_line(aes(y = Pronostico, color = "Pronóstico"),
            linetype = "dashed",
            size = 1.2) +

  scale_color_manual(values = c(
    "Real" = "black",
    "Pronóstico" = "#66442C"
  )) +

  labs(
    title = "Pronóstico vs Valores reales - Diciembre 2018",
    x = "Fecha",
    y = "Temperatura (°C)",
    color = ""
  ) +

  theme_minimal() +

  theme(
    plot.title = element_text(
      hjust = 0.5,
      face = "bold"
    ),
    legend.position = "right"
  )
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

La gráfica presenta la comparación entre los valores reales de temperatura registrados durante diciembre de 2018 y los valores pronosticados mediante el modelo ARMA(2,1), ajustado utilizando la información correspondiente al periodo enero–noviembre de 2018. La línea continua representa las temperaturas observadas, mientras que la línea discontinua corresponde a los valores estimados por el modelo. La banda sombreada representa el intervalo de confianza asociado al pronóstico, indicando el rango dentro del cual se espera que se encuentren los valores futuros.

Se observa que el modelo ARMA(2,1) mantiene las predicciones alrededor de un valor promedio cercano a 28 °C, logrando representar adecuadamente el nivel general de la temperatura durante diciembre. Sin embargo, la serie pronosticada presenta menor variabilidad respecto a los datos observados, debido a que el modelo captura principalmente la estructura promedio y la dependencia temporal de la serie, pero no reproduce completamente los cambios diarios de mayor magnitud.

Al comparar ambas trayectorias, se evidencia que durante algunos días las temperaturas reales superan los valores pronosticados, especialmente cuando la serie presenta incrementos hacia valores cercanos a 29–29.5 °C. En estos casos, el modelo tiende a subestimar la temperatura, generando diferencias entre aproximadamente 0.4 °C y 1.7 °C. Por otro lado, cuando la temperatura disminuye, el modelo logra aproximarse mejor al comportamiento observado.

En general, el modelo ARMA(2,1) presenta un desempeño adecuado para describir el comportamiento promedio de la temperatura, lo cual se refleja en sus medidas de error obtenidas durante la evaluación. No obstante, debido a la naturaleza fluctuante de la variable, el modelo presenta dificultades para anticipar variaciones repentinas de corto plazo, por lo que su capacidad predictiva es razonable, pero no permite obtener predicciones exactas para cada día.

9. Conclusiones

El análisis de la temperatura diaria de Cali durante 2018 permitió caracterizar una serie con baja variabilidad (CV = 4.3%), sin tendencia marcada y con evidencia estadística de estacionariedad (prueba ADF, p = 0.0173). Esta estabilidad de la serie constituyó un punto de partida favorable para el modelamiento ARIMA, ya que reduce la necesidad de transformaciones complejas y facilita la identificación de una estructura autorregresiva clara.

El modelo ARMA(2,1) equivalente a ARIMA(2,0,1) identificado mediante auto.arima() mostró un buen desempeño en varios frentes. Los residuos no presentaron autocorrelación significativa (prueba de Ljung-Box, p = 0.9149), comportándose de manera consistente con un proceso de ruido blanco, lo cual valida la adecuación del modelo desde el punto de vista estadístico. Asimismo, las métricas de error en el conjunto de entrenamiento (MAE = 0.66 °C, MAPE = 2.37%) reflejan un ajuste preciso, con desviaciones inferiores a un grado Celsius en promedio. El modelo alternativo ARIMA(2,1,1), evaluado como contraste con diferenciación, confirmó estos resultados al presentar también residuos compatibles con ruido blanco (p = 0.9369), lo que sugiere que la estructura de dependencia temporal de la serie fue capturada de forma robusta independientemente de la especificación elegida.

Con respecto a la pregunta de investigación ¿permiten los modelos ARIMA ajustados con datos de enero a noviembre de 2018 pronosticar adecuadamente la temperatura diaria de Cali en diciembre del mismo año? los resultados indican una respuesta afirmativa en términos generales. El modelo seleccionado logró reproducir el nivel promedio de la serie y mantener los pronósticos dentro del rango histórico observado, lo cual es un resultado relevante considerando que se trata de un horizonte de 24 observaciones. Esto confirma que la estructura autorregresiva de bajo orden identificada en los datos de entrenamiento es informativa y se mantiene razonablemente estable al proyectarse hacia el periodo de prueba, respaldando la utilidad de los modelos ARIMA como herramienta de pronóstico de corto plazo para series climáticas con baja variabilidad como la de Cali.

Se observó, adicionalmente, una leve tendencia del modelo a aproximarse a su media estimada en los días donde la temperatura real se ubicó por encima del promedio histórico (segunda mitad de diciembre), lo cual es un comportamiento esperable en modelos ARIMA de bajo orden ante series con autocorrelación moderada. Este hallazgo, más que invalidar el modelo, ofrece una oportunidad de mejora concreta para trabajos futuros.

En conjunto, los resultados respaldan que los modelos ARIMA constituyen una herramienta adecuada para el pronóstico de la temperatura diaria en Cali en el corto plazo, evidenciando un buen desempeño tanto en el ajuste histórico como en la proyección hacia diciembre de 2018, con oportunidades identificadas para refinar aún más la precisión del pronóstico en trabajos posteriores.

10. Bibliografía

Hyndman, R. J., & Athanasopoulos, G. (2018). Forecasting: Principles and practice (2nd ed.). OTexts. https://otexts.com/fpp2/

Instituto de Hidrología, Meteorología y Estudios Ambientales [IDEAM]. (s.f.). Clima. http://www.ideam.gov.co/en/web/tiempo-y-clima/clima