season plotEste documento transforma los datos mensuales del archivo Excel en una serie de tiempo con frecuencia mensual, iniciando en enero de 1980. Además, genera gráficos exploratorios, gráficos de tendencia, análisis por estación climática, análisis por mes específico, descomposición de la serie, autocorrelación y anomalías mensuales. Todos los gráficos se muestran dentro del documento y también se guardan automáticamente en una carpeta local para su posterior uso en informes, artículos o publicaciones en RPubs.
En este bloque se definen las opciones generales del documento, se cargan los paquetes necesarios y se crea una función para guardar los gráficos en alta resolución.
knitr::opts_chunk$set(
echo = TRUE,
message = FALSE,
warning = FALSE,
fig.width = 11,
fig.height = 6,
dpi = 300,
out.width = "100%"
)
# Paquetes requeridos
paquetes <- c(
"readxl", "dplyr", "tidyr", "lubridate", "ggplot2",
"forecast", "zoo", "scales", "stringr", "knitr"
)
paquetes_faltantes <- paquetes[!paquetes %in% rownames(installed.packages())]
if (length(paquetes_faltantes) > 0) {
install.packages(paquetes_faltantes, dependencies = TRUE)
}
invisible(lapply(paquetes, library, character.only = TRUE))
##
## Adjuntando el paquete: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
##
## Adjuntando el paquete: 'lubridate'
## The following objects are masked from 'package:base':
##
## date, intersect, setdiff, union
##
## Adjuntando el paquete: 'zoo'
## The following objects are masked from 'package:base':
##
## as.Date, as.Date.numeric
# Carpeta para guardar los gráficos generados
carpeta_graficos <- "graficos_series_tiempo"
if (!dir.exists(carpeta_graficos)) dir.create(carpeta_graficos)
# Función para guardar gráficos con calidad publicable
guardar_grafico <- function(grafico, nombre, ancho = 12, alto = 7, dpi = 300) {
ggplot2::ggsave(
filename = file.path(carpeta_graficos, nombre),
plot = grafico,
width = ancho,
height = alto,
dpi = dpi,
bg = "white"
)
}
# Paleta institucional sugerida
color_principal <- "#005B96"
color_secundario <- "#F2A900"
color_terciario <- "#2E7D32"
color_alerta <- "#C62828"
color_gris <- "#4D4D4D"
Este bloque lee el archivo Excel desde la ruta indicada. En Windows
se recomienda usar / en lugar de \. Si el
archivo no se encuentra en la ruta definida, R abrirá una ventana para
seleccionarlo manualmente.
ruta_archivo <- "C:/Users/DELL/OneDrive/Documentos/CAMBIO CLIMATICO/Datos_Puerto_Ila.xlsx"
if (!file.exists(ruta_archivo)) {
message("No se encontró el archivo en la ruta indicada. Seleccione el archivo manualmente.")
ruta_archivo <- file.choose()
}
datos_raw <- readxl::read_excel(ruta_archivo, sheet = "Hoja1")
# Revisión rápida de la estructura original
str(datos_raw)
## tibble [504 × 1] (S3: tbl_df/tbl/data.frame)
## $ Suma de Valor: chr [1:504] "372.2" "0" "219.7" "574.3" ...
head(datos_raw)
El archivo contiene una columna de datos mensuales. En este bloque se crea una fecha mensual desde enero de 1988, se identifican año, mes, nombre del mes y estación climática. Para la estación lluviosa se considera diciembre a mayo; para la estación seca, junio a noviembre.
meses_esp <- c(
"Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio",
"Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"
)
serie_df <- datos_raw %>%
dplyr::rename(valor = 1) %>%
dplyr::mutate(valor = as.numeric(valor)) %>%
dplyr::filter(!is.na(valor)) %>%
dplyr::mutate(
fecha = seq.Date(from = as.Date("1980-01-01"), by = "month", length.out = dplyr::n()),
anio = lubridate::year(fecha),
mes = lubridate::month(fecha),
mes_nombre = factor(meses_esp[mes], levels = meses_esp),
estacion = dplyr::case_when(
mes %in% c(12, 1, 2, 3, 4, 5) ~ "Lluviosa (dic-may)",
mes %in% c(6, 7, 8, 9, 10, 11) ~ "Seca (jun-nov)",
TRUE ~ NA_character_
),
# Diciembre se asigna al año climático siguiente para analizar la estación lluviosa completa.
anio_estacion = dplyr::if_else(mes == 12, anio + 1L, anio)
)
knitr::kable(head(serie_df, 12), caption = "Primeros 12 registros de la base procesada")
| valor | fecha | anio | mes | mes_nombre | estacion | anio_estacion |
|---|---|---|---|---|---|---|
| 372.2 | 1980-01-01 | 1980 | 1 | Enero | Lluviosa (dic-may) | 1980 |
| 0.0 | 1980-02-01 | 1980 | 2 | Febrero | Lluviosa (dic-may) | 1980 |
| 219.7 | 1980-03-01 | 1980 | 3 | Marzo | Lluviosa (dic-may) | 1980 |
| 574.3 | 1980-04-01 | 1980 | 4 | Abril | Lluviosa (dic-may) | 1980 |
| 230.7 | 1980-05-01 | 1980 | 5 | Mayo | Lluviosa (dic-may) | 1980 |
| 42.8 | 1980-06-01 | 1980 | 6 | Junio | Seca (jun-nov) | 1980 |
| 7.3 | 1980-07-01 | 1980 | 7 | Julio | Seca (jun-nov) | 1980 |
| 43.8 | 1980-08-01 | 1980 | 8 | Agosto | Seca (jun-nov) | 1980 |
| 6.3 | 1980-09-01 | 1980 | 9 | Septiembre | Seca (jun-nov) | 1980 |
| 87.0 | 1980-10-01 | 1980 | 10 | Octubre | Seca (jun-nov) | 1980 |
| 49.2 | 1980-11-01 | 1980 | 11 | Noviembre | Seca (jun-nov) | 1980 |
| 81.4 | 1980-12-01 | 1980 | 12 | Diciembre | Lluviosa (dic-may) | 1981 |
knitr::kable(tail(serie_df, 12), caption = "Últimos 12 registros de la base procesada")
| valor | fecha | anio | mes | mes_nombre | estacion | anio_estacion |
|---|---|---|---|---|---|---|
| 534.3 | 2021-01-01 | 2021 | 1 | Enero | Lluviosa (dic-may) | 2021 |
| 541.2 | 2021-02-01 | 2021 | 2 | Febrero | Lluviosa (dic-may) | 2021 |
| 728.6 | 2021-03-01 | 2021 | 3 | Marzo | Lluviosa (dic-may) | 2021 |
| 481.6 | 2021-04-01 | 2021 | 4 | Abril | Lluviosa (dic-may) | 2021 |
| 245.5 | 2021-05-01 | 2021 | 5 | Mayo | Lluviosa (dic-may) | 2021 |
| 125.3 | 2021-06-01 | 2021 | 6 | Junio | Seca (jun-nov) | 2021 |
| 41.8 | 2021-07-01 | 2021 | 7 | Julio | Seca (jun-nov) | 2021 |
| 20.5 | 2021-08-01 | 2021 | 8 | Agosto | Seca (jun-nov) | 2021 |
| 25.6 | 2021-09-01 | 2021 | 9 | Septiembre | Seca (jun-nov) | 2021 |
| 17.2 | 2021-10-01 | 2021 | 10 | Octubre | Seca (jun-nov) | 2021 |
| 12.4 | 2021-11-01 | 2021 | 11 | Noviembre | Seca (jun-nov) | 2021 |
| 0.0 | 2021-12-01 | 2021 | 12 | Diciembre | Lluviosa (dic-may) | 2022 |
Aquí se convierte la columna numérica en un objeto ts de
R, con frecuencia 12 porque los datos son mensuales.
serie_ts <- ts(serie_df$valor, start = c(1980, 1), frequency = 12)
serie_ts
## Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
## 1980 372.2 0.0 219.7 574.3 230.7 42.8 7.3 43.8 6.3 87.0 49.2 81.4
## 1981 0.0 0.0 0.0 445.2 0.0 0.0 0.0 0.0 43.9 39.7 18.6 131.1
## 1982 683.4 439.7 252.6 327.6 203.5 16.8 25.9 9.2 20.6 649.5 609.0 799.6
## 1983 762.5 463.0 804.5 452.6 520.0 393.5 390.2 325.1 362.6 183.0 128.3 329.6
## 1984 125.5 630.3 389.2 497.7 221.2 157.6 25.8 29.3 49.4 28.9 31.1 435.3
## 1985 450.8 278.4 440.9 140.4 166.9 79.6 19.6 30.2 28.5 21.5 24.5 201.0
## 1986 529.9 316.0 305.5 580.2 91.4 8.3 8.7 30.7 34.6 82.5 69.8 226.7
## 1987 490.2 496.8 739.0 411.2 421.2 24.0 40.1 56.6 78.5 50.4 35.5 149.1
## 1988 612.3 531.8 164.2 486.3 373.1 52.3 30.2 19.5 34.3 27.1 49.1 121.1
## 1989 69.7 443.3 735.1 499.3 124.6 82.2 35.9 18.4 35.6 80.4 30.0 243.1
## 1990 189.2 0.0 287.9 483.5 84.4 83.5 68.1 4.4 9.0 47.0 19.6 161.9
## 1991 321.9 638.6 322.0 321.2 201.2 81.4 19.6 36.2 12.3 45.2 41.2 133.5
## 1992 457.5 858.3 524.9 791.6 541.8 240.6 159.2 34.1 18.2 32.6 41.0 146.7
## 1993 455.3 0.0 733.1 0.0 91.4 48.5 69.0 36.0 99.0 32.3 28.8 206.4
## 1994 583.8 540.8 346.0 423.4 315.8 97.2 4.0 9.9 16.7 29.4 70.2 391.0
## 1995 432.3 327.3 264.4 455.2 262.4 130.1 64.0 68.7 19.2 48.8 34.4 53.1
## 1996 325.7 608.4 641.9 423.7 145.7 16.1 23.6 38.9 24.0 19.2 23.0 137.8
## 1997 596.8 458.0 565.2 500.2 269.6 367.0 233.3 138.8 704.4 466.6 801.3 960.2
## 1998 894.2 639.1 826.3 772.5 463.3 291.7 216.1 66.9 64.4 27.7 25.7 52.8
## 1999 211.7 487.6 561.2 614.3 286.7 53.3 23.4 14.8 82.3 57.6 52.7 305.0
## 2000 252.1 621.5 689.0 522.3 325.6 48.0 6.5 24.2 48.1 29.4 20.5 144.7
## 2001 510.2 242.3 499.9 658.3 193.8 12.4 22.4 4.6 21.2 15.1 32.9 116.6
## 2002 334.6 563.3 775.0 599.8 341.3 134.9 23.9 6.4 83.4 61.9 95.1 273.2
## 2003 441.6 523.3 241.5 590.6 318.6 62.7 27.2 21.1 9.5 96.7 30.7 145.2
## 2004 263.1 390.3 252.3 474.0 296.9 38.8 25.0 16.7 107.5 69.9 34.8 64.2
## 2005 370.6 402.8 630.6 635.5 33.4 13.1 7.3 2.7 32.8 35.6 67.6 124.3
## 2006 175.9 720.7 700.5 508.0 72.2 129.1 24.1 72.7 60.2 23.8 144.5 62.9
## 2007 222.4 311.6 528.6 577.8 193.0 93.1 55.1 22.4 47.2 20.6 51.7 129.9
## 2008 646.0 513.4 484.6 358.4 185.6 60.1 65.9 125.0 61.0 42.2 34.1 43.1
## 2009 652.2 480.6 547.1 157.9 129.9 19.0 8.9 24.1 11.2 12.6 15.6 293.2
## 2010 365.7 528.9 361.6 743.4 179.1 64.0 176.0 33.4 49.8 28.5 83.7 296.5
## 2011 498.9 314.5 260.5 715.7 53.3 69.3 133.4 9.4 30.3 55.4 17.5 213.4
## 2012 660.3 707.3 649.5 591.0 252.1 272.0 44.7 17.0 11.7 21.1 41.0 70.2
## 2013 439.2 442.6 743.7 618.5 129.1 78.4 23.8 118.2 41.5 44.4 9.9 200.6
## 2014 429.6 450.0 354.3 572.7 408.7 95.7 17.4 35.9 38.7 98.1 23.9 83.0
## 2015 511.5 518.4 261.8 351.2 474.9 159.2 173.3 14.9 44.2 180.3 98.5 382.7
## 2016 771.5 475.6 727.6 521.5 216.7 71.2 100.3 9.7 83.8 24.7 24.0 55.6
## 2017 532.2 411.3 870.5 528.9 229.6 320.1 19.4 38.5 19.2 76.0 25.3 137.8
## 2018 148.6 686.6 724.8 386.2 506.1 49.8 31.6 14.5 26.1 8.2 89.1 311.0
## 2019 403.7 404.8 344.8 414.4 293.4 86.6 64.7 20.7 26.2 62.7 12.5 160.9
## 2020 396.9 795.3 536.6 483.4 225.8 67.5 17.4 71.8 84.4 104.9 45.7 205.4
## 2021 534.3 541.2 728.6 481.6 245.5 125.3 41.8 20.5 25.6 17.2 12.4 0.0
summary(serie_ts)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.00 34.25 126.90 229.95 409.32 960.20
Este gráfico permite observar la evolución completa de la variable en el tiempo, incluyendo valores altos, valores bajos, cambios de régimen y posibles eventos extremos.
p_serie_general <- ggplot(serie_df, aes(x = fecha, y = valor)) +
geom_line(color = color_principal, linewidth = 0.55) +
geom_smooth(method = "loess", se = FALSE, color = color_alerta, linewidth = 0.9) +
labs(
title = "Serie mensual completa",
subtitle = "Línea azul: datos mensuales; línea roja: tendencia suavizada",
x = "Fecha",
y = "Valor observado"
) +
scale_x_date(date_breaks = "3 years", date_labels = "%Y") +
theme_minimal(base_size = 14) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
p_serie_general
guardar_grafico(p_serie_general, "01_serie_mensual_completa.png")
Este bloque resume la serie por año y calcula la tendencia anual. Es útil cuando se desea reducir la variabilidad mensual y observar cambios de mediano o largo plazo.
serie_anual <- serie_df %>%
dplyr::group_by(anio) %>%
dplyr::summarise(
promedio_anual = mean(valor, na.rm = TRUE),
acumulado_anual = sum(valor, na.rm = TRUE),
maximo_anual = max(valor, na.rm = TRUE),
minimo_anual = min(valor, na.rm = TRUE),
.groups = "drop"
)
p_tendencia_anual <- ggplot(serie_anual, aes(x = anio, y = promedio_anual)) +
geom_line(color = color_gris, linewidth = 0.6) +
geom_point(color = color_principal, size = 2) +
geom_smooth(method = "lm", se = TRUE, color = color_alerta, fill = "#FADBD8", linewidth = 1) +
labs(
title = "Tendencia anual del promedio mensual",
subtitle = "La línea roja representa la tendencia lineal estimada",
x = "Año",
y = "Promedio anual"
) +
theme_minimal(base_size = 14)
p_tendencia_anual
guardar_grafico(p_tendencia_anual, "02_tendencia_anual_promedio.png")
modelo_tendencia_anual <- lm(promedio_anual ~ anio, data = serie_anual)
summary(modelo_tendencia_anual)
##
## Call:
## lm(formula = promedio_anual ~ anio, data = serie_anual)
##
## Residuals:
## Min 1Q Median 3Q Max
## -162.66 -38.48 -12.35 16.83 277.10
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -872.5938 1976.6742 -0.441 0.661
## anio 0.5511 0.9881 0.558 0.580
##
## Residual standard error: 77.62 on 40 degrees of freedom
## Multiple R-squared: 0.007718, Adjusted R-squared: -0.01709
## F-statistic: 0.3111 on 1 and 40 DF, p-value: 0.5801
Este gráfico muestra el comportamiento promedio de cada mes considerando todos los años disponibles. Es útil para identificar la estacionalidad intraanual.
promedio_mensual <- serie_df %>%
dplyr::group_by(mes, mes_nombre) %>%
dplyr::summarise(
promedio = mean(valor, na.rm = TRUE),
mediana = median(valor, na.rm = TRUE),
desviacion = sd(valor, na.rm = TRUE),
.groups = "drop"
)
p_promedio_mensual <- ggplot(promedio_mensual, aes(x = mes_nombre, y = promedio, group = 1)) +
geom_col(fill = color_principal, alpha = 0.82) +
geom_line(color = color_secundario, linewidth = 1.2) +
geom_point(color = color_alerta, size = 3) +
labs(
title = "Promedio mensual histórico",
subtitle = "Promedio de cada mes calculado para todo el periodo disponible",
x = "Mes",
y = "Promedio mensual"
) +
theme_minimal(base_size = 14) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
p_promedio_mensual
guardar_grafico(p_promedio_mensual, "03_promedio_mensual_historico.png")
knitr::kable(promedio_mensual, digits = 2, caption = "Estadísticos mensuales históricos")
| mes | mes_nombre | promedio | mediana | desviacion |
|---|---|---|---|---|
| 1 | Enero | 431.57 | 440.40 | 197.50 |
| 2 | Febrero | 457.23 | 478.10 | 201.34 |
| 3 | Marzo | 500.88 | 526.75 | 218.66 |
| 4 | Abril | 492.65 | 498.50 | 158.81 |
| 5 | Mayo | 245.70 | 227.70 | 136.25 |
| 6 | Junio | 103.26 | 74.80 | 97.58 |
| 7 | Julio | 61.29 | 26.55 | 78.33 |
| 8 | Agosto | 41.33 | 24.15 | 55.17 |
| 9 | Septiembre | 64.46 | 35.10 | 115.66 |
| 10 | Octubre | 75.85 | 43.30 | 117.49 |
| 11 | Noviembre | 76.05 | 34.60 | 147.18 |
| 12 | Diciembre | 209.07 | 147.90 | 184.21 |
El boxplot permite comparar la variabilidad de cada mes. Es especialmente útil para detectar meses con mayor dispersión o valores extremos.
p_boxplot_mensual <- ggplot(serie_df, aes(x = mes_nombre, y = valor, fill = mes_nombre)) +
geom_boxplot(alpha = 0.85, outlier.color = color_alerta, outlier.size = 2) +
labs(
title = "Distribución de valores por mes",
subtitle = "Comparación de variabilidad mensual en todo el periodo analizado",
x = "Mes",
y = "Valor observado"
) +
theme_minimal(base_size = 14) +
theme(
axis.text.x = element_text(angle = 45, hjust = 1),
legend.position = "none"
)
p_boxplot_mensual
guardar_grafico(p_boxplot_mensual, "04_boxplot_mensual.png")
El mapa de calor permite observar patrones temporales combinando años y meses. Es útil para identificar periodos húmedos, secos, anomalías o valores extremos.
p_heatmap <- ggplot(serie_df, aes(x = mes_nombre, y = factor(anio), fill = valor)) +
geom_tile(color = "white", linewidth = 0.15) +
scale_fill_gradientn(
colours = c("#F7FBFF", "#6BAED6", "#2171B5", "#08306B"),
name = "Valor"
) +
labs(
title = "Mapa de calor mensual por año",
subtitle = "Cada celda representa el valor mensual observado",
x = "Mes",
y = "Año"
) +
theme_minimal(base_size = 13) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
p_heatmap
guardar_grafico(p_heatmap, "05_mapa_calor_anio_mes.png", ancho = 12, alto = 10)
En este bloque se separa la serie en estación lluviosa y estación seca. Para la estación lluviosa se considera diciembre a mayo; por ello, diciembre se asigna al año climático siguiente.
resumen_estacional <- serie_df %>%
dplyr::group_by(estacion, anio_estacion) %>%
dplyr::summarise(
promedio_estacional = mean(valor, na.rm = TRUE),
acumulado_estacional = sum(valor, na.rm = TRUE),
maximo_estacional = max(valor, na.rm = TRUE),
minimo_estacional = min(valor, na.rm = TRUE),
n_meses = dplyr::n(),
.groups = "drop"
) %>%
dplyr::filter(n_meses == 6)
knitr::kable(head(resumen_estacional, 12), digits = 2, caption = "Resumen por estación climática")
| estacion | anio_estacion | promedio_estacional | acumulado_estacional | maximo_estacional | minimo_estacional | n_meses |
|---|---|---|---|---|---|---|
| Lluviosa (dic-may) | 1981 | 87.77 | 526.6 | 445.2 | 0.0 | 6 |
| Lluviosa (dic-may) | 1982 | 339.65 | 2037.9 | 683.4 | 131.1 | 6 |
| Lluviosa (dic-may) | 1983 | 633.70 | 3802.2 | 804.5 | 452.6 | 6 |
| Lluviosa (dic-may) | 1984 | 365.58 | 2193.5 | 630.3 | 125.5 | 6 |
| Lluviosa (dic-may) | 1985 | 318.78 | 1912.7 | 450.8 | 140.4 | 6 |
| Lluviosa (dic-may) | 1986 | 337.33 | 2024.0 | 580.2 | 91.4 | 6 |
| Lluviosa (dic-may) | 1987 | 464.18 | 2785.1 | 739.0 | 226.7 | 6 |
| Lluviosa (dic-may) | 1988 | 386.13 | 2316.8 | 612.3 | 149.1 | 6 |
| Lluviosa (dic-may) | 1989 | 332.18 | 1993.1 | 735.1 | 69.7 | 6 |
| Lluviosa (dic-may) | 1990 | 214.68 | 1288.1 | 483.5 | 0.0 | 6 |
| Lluviosa (dic-may) | 1991 | 327.80 | 1966.8 | 638.6 | 161.9 | 6 |
| Lluviosa (dic-may) | 1992 | 551.27 | 3307.6 | 858.3 | 133.5 | 6 |
Este gráfico presenta la tendencia de la estación lluviosa, definida de diciembre a mayo.
lluviosa <- resumen_estacional %>%
dplyr::filter(estacion == "Lluviosa (dic-may)")
p_lluviosa <- ggplot(lluviosa, aes(x = anio_estacion, y = promedio_estacional)) +
geom_line(color = color_gris, linewidth = 0.6) +
geom_point(color = color_principal, size = 2.2) +
geom_smooth(method = "lm", se = TRUE, color = color_alerta, fill = "#FADBD8", linewidth = 1) +
labs(
title = "Tendencia de la estación lluviosa",
subtitle = "Estación lluviosa definida como diciembre a mayo",
x = "Año climático",
y = "Promedio estacional"
) +
theme_minimal(base_size = 14)
p_lluviosa
guardar_grafico(p_lluviosa, "06_tendencia_estacion_lluviosa.png")
modelo_lluviosa <- lm(promedio_estacional ~ anio_estacion, data = lluviosa)
summary(modelo_lluviosa)
##
## Call:
## lm(formula = promedio_estacional ~ anio_estacion, data = lluviosa)
##
## Residuals:
## Min 1Q Median 3Q Max
## -267.35 -44.80 -17.37 22.74 371.66
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -3430.883 2813.904 -1.219 0.230
## anio_estacion 1.911 1.406 1.359 0.182
##
## Residual standard error: 106.5 on 39 degrees of freedom
## Multiple R-squared: 0.04522, Adjusted R-squared: 0.02074
## F-statistic: 1.847 on 1 and 39 DF, p-value: 0.1819
Este gráfico presenta la tendencia de la estación seca, definida de junio a noviembre.
seca <- resumen_estacional %>%
dplyr::filter(estacion == "Seca (jun-nov)")
p_seca <- ggplot(seca, aes(x = anio_estacion, y = promedio_estacional)) +
geom_line(color = color_gris, linewidth = 0.6) +
geom_point(color = color_terciario, size = 2.2) +
geom_smooth(method = "lm", se = TRUE, color = color_alerta, fill = "#FADBD8", linewidth = 1) +
labs(
title = "Tendencia de la estación seca",
subtitle = "Estación seca definida como junio a noviembre",
x = "Año",
y = "Promedio estacional"
) +
theme_minimal(base_size = 14)
p_seca
guardar_grafico(p_seca, "07_tendencia_estacion_seca.png")
modelo_seca <- lm(promedio_estacional ~ anio_estacion, data = seca)
summary(modelo_seca)
##
## Call:
## lm(formula = promedio_estacional ~ anio_estacion, data = seca)
##
## Residuals:
## Min 1Q Median 3Q Max
## -72.29 -40.09 -16.26 6.95 378.13
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 2014.2261 2006.5636 1.004 0.322
## anio_estacion -0.9717 1.0030 -0.969 0.338
##
## Residual standard error: 78.79 on 40 degrees of freedom
## Multiple R-squared: 0.02292, Adjusted R-squared: -0.001502
## F-statistic: 0.9385 on 1 and 40 DF, p-value: 0.3385
Este gráfico permite comparar ambas estaciones climáticas en una misma figura.
p_comparacion_estaciones <- ggplot(resumen_estacional, aes(x = anio_estacion, y = promedio_estacional, color = estacion)) +
geom_line(linewidth = 0.7) +
geom_point(size = 2) +
geom_smooth(method = "lm", se = FALSE, linewidth = 0.9) +
scale_color_manual(values = c("Lluviosa (dic-may)" = color_principal, "Seca (jun-nov)" = color_terciario)) +
labs(
title = "Comparación de tendencias por estación climática",
subtitle = "Promedio estacional anual para estación lluviosa y estación seca",
x = "Año climático",
y = "Promedio estacional",
color = "Estación"
) +
theme_minimal(base_size = 14)
p_comparacion_estaciones
guardar_grafico(p_comparacion_estaciones, "08_comparacion_tendencias_estacionales.png")
Si la variable analizada corresponde a precipitación, el acumulado estacional suele ser más informativo que el promedio, porque representa el volumen total registrado durante la estación lluviosa o seca. Este bloque genera una comparación de acumulados por estación.
p_acumulado_estacional <- ggplot(resumen_estacional, aes(x = anio_estacion, y = acumulado_estacional, color = estacion)) +
geom_line(linewidth = 0.7) +
geom_point(size = 2) +
geom_smooth(method = "lm", se = FALSE, linewidth = 0.9) +
scale_color_manual(values = c("Lluviosa (dic-may)" = color_principal, "Seca (jun-nov)" = color_terciario)) +
labs(
title = "Tendencia del acumulado por estación climática",
subtitle = "Recomendado cuando la variable corresponde a precipitación u otra magnitud acumulable",
x = "Año climático",
y = "Acumulado estacional",
color = "Estación"
) +
theme_minimal(base_size = 14)
p_acumulado_estacional
guardar_grafico(p_acumulado_estacional, "08b_tendencia_acumulado_estacional.png")
modelo_acumulado_lluviosa <- lm(acumulado_estacional ~ anio_estacion, data = lluviosa)
modelo_acumulado_seca <- lm(acumulado_estacional ~ anio_estacion, data = seca)
summary(modelo_acumulado_lluviosa)
##
## Call:
## lm(formula = acumulado_estacional ~ anio_estacion, data = lluviosa)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1604.1 -268.8 -104.2 136.4 2230.0
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -20585.296 16883.422 -1.219 0.230
## anio_estacion 11.467 8.437 1.359 0.182
##
## Residual standard error: 639.2 on 39 degrees of freedom
## Multiple R-squared: 0.04522, Adjusted R-squared: 0.02074
## F-statistic: 1.847 on 1 and 39 DF, p-value: 0.1819
summary(modelo_acumulado_seca)
##
## Call:
## lm(formula = acumulado_estacional ~ anio_estacion, data = seca)
##
## Residuals:
## Min 1Q Median 3Q Max
## -433.72 -240.52 -97.57 41.68 2268.76
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 12085.357 12039.381 1.004 0.322
## anio_estacion -5.830 6.018 -0.969 0.338
##
## Residual standard error: 472.7 on 40 degrees of freedom
## Multiple R-squared: 0.02292, Adjusted R-squared: -0.001502
## F-statistic: 0.9385 on 1 and 40 DF, p-value: 0.3385
Este bloque analiza cada mes por separado. Por ejemplo, enero se compara únicamente contra los eneros de todos los años; febrero contra los febreros de todos los años, y así sucesivamente.
p_tendencia_meses_facetas <- ggplot(serie_df, aes(x = anio, y = valor)) +
geom_line(color = color_gris, linewidth = 0.45) +
geom_point(color = color_principal, size = 1.5) +
geom_smooth(method = "lm", se = TRUE, color = color_alerta, fill = "#FADBD8", linewidth = 0.75) +
facet_wrap(~ mes_nombre, scales = "free_y", ncol = 3) +
labs(
title = "Tendencia independiente por mes",
subtitle = "Cada panel compara el mismo mes a través de todos los años",
x = "Año",
y = "Valor observado"
) +
theme_minimal(base_size = 13)
p_tendencia_meses_facetas
guardar_grafico(p_tendencia_meses_facetas, "09_tendencia_independiente_por_mes_facetas.png", ancho = 14, alto = 10)
Este bloque imprime y guarda un gráfico separado para cada mes. Los
archivos se guardan en la carpeta
graficos_series_tiempo.
for (m in levels(serie_df$mes_nombre)) {
datos_mes <- serie_df %>% dplyr::filter(mes_nombre == m)
p_mes <- ggplot(datos_mes, aes(x = anio, y = valor)) +
geom_line(color = color_gris, linewidth = 0.55) +
geom_point(color = color_principal, size = 2) +
geom_smooth(method = "lm", se = TRUE, color = color_alerta, fill = "#FADBD8", linewidth = 0.95) +
labs(
title = paste("Tendencia histórica del mes de", m),
subtitle = paste("Comparación de", m, "en todos los años disponibles"),
x = "Año",
y = "Valor observado"
) +
theme_minimal(base_size = 14)
print(p_mes)
nombre_archivo <- paste0("10_tendencia_mes_", stringr::str_to_lower(stringr::str_replace_all(m, " ", "_")), ".png")
guardar_grafico(p_mes, nombre_archivo, ancho = 10, alto = 5.5)
}
season plotEste gráfico muestra la forma anual de la serie, comparando los meses dentro de cada año. Es útil para observar si la estacionalidad se mantiene estable o cambia en el tiempo.
p_season <- forecast::ggseasonplot(serie_ts, year.labels = FALSE, continuous = TRUE) +
labs(
title = "Gráfico estacional mensual",
subtitle = "Comparación de la forma intraanual de la serie",
x = "Mes",
y = "Valor observado"
) +
theme_minimal(base_size = 14)
p_season
guardar_grafico(p_season, "11_grafico_estacional_seasonplot.png")
El gráfico de subseries permite observar, para cada mes, la media mensual y la variabilidad interna a lo largo del tiempo.
p_subseries <- forecast::ggsubseriesplot(serie_ts) +
labs(
title = "Gráfico de subseries mensuales",
subtitle = "Evolución de cada mes y comparación con su media histórica",
x = "Mes",
y = "Valor observado"
) +
theme_minimal(base_size = 14)
p_subseries
guardar_grafico(p_subseries, "12_grafico_subseries_mensuales.png")
La descomposición separa la serie en tres componentes: tendencia, estacionalidad y residuo. Se aplica una descomposición aditiva, adecuada cuando la amplitud estacional no depende fuertemente del nivel de la serie.
desc_aditiva <- stats::decompose(serie_ts, type = "additive")
p_desc_aditiva <- forecast::autoplot(desc_aditiva) +
labs(title = "Descomposición clásica aditiva de la serie") +
theme_minimal(base_size = 14)
p_desc_aditiva
guardar_grafico(p_desc_aditiva, "13_descomposicion_clasica_aditiva.png", ancho = 12, alto = 8)
La descomposición STL es flexible y suele ser más robusta para series ambientales mensuales porque permite una estimación suavizada de la tendencia y de la estacionalidad.
desc_stl <- stats::stl(serie_ts, s.window = "periodic")
p_desc_stl <- forecast::autoplot(desc_stl) +
labs(title = "Descomposición STL de la serie mensual") +
theme_minimal(base_size = 14)
p_desc_stl
guardar_grafico(p_desc_stl, "14_descomposicion_stl.png", ancho = 12, alto = 8)
Las medias móviles permiten suavizar la serie y visualizar mejor los cambios de mediano plazo. La media móvil de 12 meses reduce el efecto de la estacionalidad anual.
serie_df <- serie_df %>%
dplyr::mutate(
media_movil_12 = zoo::rollmean(valor, k = 12, fill = NA, align = "center"),
media_movil_24 = zoo::rollmean(valor, k = 24, fill = NA, align = "center")
)
p_medias_moviles <- ggplot(serie_df, aes(x = fecha)) +
geom_line(aes(y = valor), color = "#B0B0B0", linewidth = 0.35) +
geom_line(aes(y = media_movil_12), color = color_principal, linewidth = 0.9) +
geom_line(aes(y = media_movil_24), color = color_alerta, linewidth = 1) +
labs(
title = "Serie mensual con medias móviles",
subtitle = "Gris: serie original; azul: media móvil 12 meses; rojo: media móvil 24 meses",
x = "Fecha",
y = "Valor observado"
) +
scale_x_date(date_breaks = "3 years", date_labels = "%Y") +
theme_minimal(base_size = 14) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
p_medias_moviles
guardar_grafico(p_medias_moviles, "15_medias_moviles_12_24_meses.png")
La anomalía se calcula como la diferencia entre el valor observado y el promedio histórico de su respectivo mes. Este análisis permite identificar meses por encima o por debajo del comportamiento esperado.
climatologia_mensual <- serie_df %>%
dplyr::group_by(mes) %>%
dplyr::summarise(promedio_mes = mean(valor, na.rm = TRUE), .groups = "drop")
serie_anomalias <- serie_df %>%
dplyr::left_join(climatologia_mensual, by = "mes") %>%
dplyr::mutate(
anomalia = valor - promedio_mes,
tipo_anomalia = dplyr::if_else(anomalia >= 0, "Positiva", "Negativa")
)
p_anomalias <- ggplot(serie_anomalias, aes(x = fecha, y = anomalia, fill = tipo_anomalia)) +
geom_col(width = 25, alpha = 0.9) +
geom_hline(yintercept = 0, color = "black", linewidth = 0.4) +
scale_fill_manual(values = c("Positiva" = color_principal, "Negativa" = color_alerta)) +
labs(
title = "Anomalías mensuales",
subtitle = "Diferencia entre el valor observado y el promedio histórico del mismo mes",
x = "Fecha",
y = "Anomalía",
fill = "Tipo"
) +
scale_x_date(date_breaks = "3 years", date_labels = "%Y") +
theme_minimal(base_size = 14) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
p_anomalias
guardar_grafico(p_anomalias, "16_anomalias_mensuales.png", ancho = 13, alto = 6.5)
La autocorrelación permite evaluar si los valores actuales dependen de valores pasados. En series mensuales ambientales es común observar autocorrelación en rezagos de 12 meses.
p_acf <- forecast::ggAcf(serie_ts, lag.max = 60) +
labs(
title = "Función de autocorrelación ACF",
subtitle = "Rezagos mensuales hasta 60 meses"
) +
theme_minimal(base_size = 14)
p_pacf <- forecast::ggPacf(serie_ts, lag.max = 60) +
labs(
title = "Función de autocorrelación parcial PACF",
subtitle = "Rezagos mensuales hasta 60 meses"
) +
theme_minimal(base_size = 14)
p_acf
guardar_grafico(p_acf, "17_acf_serie_mensual.png")
p_pacf
guardar_grafico(p_pacf, "18_pacf_serie_mensual.png")
Esta tabla sintetiza la serie mensual y permite reportar los valores principales del periodo analizado.
resumen_general <- serie_df %>%
dplyr::summarise(
fecha_inicio = min(fecha),
fecha_fin = max(fecha),
n_meses = dplyr::n(),
promedio = mean(valor, na.rm = TRUE),
mediana = median(valor, na.rm = TRUE),
desviacion_estandar = sd(valor, na.rm = TRUE),
minimo = min(valor, na.rm = TRUE),
maximo = max(valor, na.rm = TRUE),
coef_variacion = sd(valor, na.rm = TRUE) / mean(valor, na.rm = TRUE) * 100
)
knitr::kable(resumen_general, digits = 2, caption = "Resumen estadístico general de la serie mensual")
| fecha_inicio | fecha_fin | n_meses | promedio | mediana | desviacion_estandar | minimo | maximo | coef_variacion |
|---|---|---|---|---|---|---|---|---|
| 1980-01-01 | 2021-12-01 | 504 | 229.95 | 126.9 | 234.12 | 0 | 960.2 | 101.81 |
Este bloque guarda las bases procesadas en formato CSV para que puedan ser usadas posteriormente en otros análisis.
write.csv(serie_df, file = "serie_mensual_procesada.csv", row.names = FALSE)
write.csv(serie_anual, file = "serie_anual_resumen.csv", row.names = FALSE)
write.csv(resumen_estacional, file = "serie_estacional_resumen.csv", row.names = FALSE)
write.csv(promedio_mensual, file = "promedio_mensual_historico.csv", row.names = FALSE)
write.csv(serie_anomalias, file = "serie_anomalias_mensuales.csv", row.names = FALSE)
Para una interpretación rigurosa, no conviene concluir que existe una tendencia climática solo por observar una pendiente visual. La tendencia debe evaluarse con el modelo lineal, los intervalos de confianza y, si corresponde, con pruebas no paramétricas adicionales. También debe considerarse la calidad de la serie, presencia de datos faltantes, cambios de estación meteorológica, cambios de instrumentos o cambios en el entorno de medición.