# ============================================================
# CARGA DE LA BASE DE DATOS
# ============================================================
archivo <- "C:/Users/Usuario/Downloads/C02-Rumihurco_Precipitación-Subhorario_validado.csv"
data0 <- read.csv(
archivo,
stringsAsFactors = FALSE
)
# Conversión de fecha
data0$fecha <- as.POSIXct(
data0$fecha,
format = "%Y/%m/%d %H:%M:%S"
)
# Revisar periodo disponible
range(data0$fecha, na.rm = TRUE)
## [1] "2019-01-01 00:00:00 -05" "2025-12-31 23:55:00 -05"
La serie temporal corresponde a una sucesión de observaciones registradas a lo largo del tiempo. Se analiza inicialmente la serie original de precipitación con resolución de 5 minutos.
# ============================================================
# SERIE TEMPORAL ORIGINAL DE 5 MINUTOS
# MEDIA Y TENDENCIA
# ============================================================
serie_temporal <- data0[
!is.na(data0$valor),
]
# Media de toda la serie
media_precipitacion <- mean(
serie_temporal$valor,
na.rm = TRUE
)
plot_5min <- ggplot(
data = serie_temporal,
aes(
x = fecha,
y = valor
)
) +
# Datos observados
geom_line(
color = "grey60",
linewidth = 0.25
) +
# Media
geom_hline(
yintercept = media_precipitacion,
color = "blue",
linetype = "dashed",
linewidth = 0.7
) +
# Tendencia lineal
geom_smooth(
method = "lm",
formula = y ~ x,
se = FALSE,
color = "red",
linewidth = 0.8
) +
labs(
title = "Serie temporal de precipitación - Estación C02 Rumihurco",
subtitle = "Resolución temporal: 5 minutos",
x = "Periodo de tiempo",
y = "Precipitación [mm]",
caption = "Elaborado por: Arelys Quimbita"
) +
theme_bw() +
theme(
plot.title = element_text(
hjust = 0.5,
face = "bold"
),
plot.subtitle = element_text(
hjust = 0.5
),
plot.caption = element_text(
hjust = 1
)
)
plot_5min
Interpretación: La serie temporal presenta una elevada variabilidad asociada con la ocurrencia intermitente de eventos de precipitación. La línea azul representa el valor medio de la serie, mientras que la línea roja permite realizar una evaluación visual preliminar de la tendencia temporal. Los vacíos de información deben diferenciarse de cambios reales en el comportamiento de la precipitación.
Se obtienen series acumuladas para duraciones de 15, 30, 120 y 1440 minutos a partir de la serie original de 5 minutos.
# ============================================================
# AGREGACIÓN TEMPORAL
# ============================================================
# Serie original de 5 minutos
zdata5min <- zoo(
data0$valor,
data0$fecha
)
# ------------------------------------------------------------
# Función que suma únicamente cuando el intervalo está completo
# ------------------------------------------------------------
suma_completa <- function(x, n_esperado) {
if (
length(x) == n_esperado &&
!any(is.na(x))
) {
sum(x)
} else {
NA_real_
}
}
# 15 minutos = 3 datos de 5 minutos
zdata15min <- aggregate(
zdata5min,
cut(index(zdata5min), "15 min"),
function(x) suma_completa(x, 3)
)
# 30 minutos = 6 datos de 5 minutos
zdata30min <- aggregate(
zdata5min,
cut(index(zdata5min), "30 min"),
function(x) suma_completa(x, 6)
)
# 120 minutos = 24 datos de 5 minutos
zdata120min <- aggregate(
zdata5min,
cut(index(zdata5min), "2 hours"),
function(x) suma_completa(x, 24)
)
# 1440 minutos = 1 día = 288 datos de 5 minutos
zdata1440min <- aggregate(
zdata5min,
as.Date(index(zdata5min)),
function(x) suma_completa(x, 288)
)
# ============================================================
# CONVERTIR LAS SERIES ZOO A DATA.FRAME
# ============================================================
data5 <- data.frame(
fecha = as.POSIXct(index(zdata5min)),
precipitacion = as.numeric(zdata5min),
duracion = "5 min"
)
data15 <- data.frame(
fecha = as.POSIXct(index(zdata15min)),
precipitacion = as.numeric(zdata15min),
duracion = "15 min"
)
data30 <- data.frame(
fecha = as.POSIXct(index(zdata30min)),
precipitacion = as.numeric(zdata30min),
duracion = "30 min"
)
data120 <- data.frame(
fecha = as.POSIXct(index(zdata120min)),
precipitacion = as.numeric(zdata120min),
duracion = "120 min"
)
data1440 <- data.frame(
fecha = as.POSIXct(index(zdata1440min)),
precipitacion = as.numeric(zdata1440min),
duracion = "1440 min"
)
# Unir todas las duraciones
series_total <- rbind(
data5,
data15,
data30,
data120,
data1440
)
# Orden lógico de las duraciones
series_total$duracion <- factor(
series_total$duracion,
levels = c(
"5 min",
"15 min",
"30 min",
"120 min",
"1440 min"
)
)
# ============================================================
# SERIES PARA TODAS LAS DURACIONES
# ============================================================
plot_series <- ggplot(
data = series_total,
aes(
x = fecha,
y = precipitacion
)
) +
geom_line(
linewidth = 0.25,
na.rm = TRUE
) +
facet_wrap(
~ duracion,
ncol = 1,
scales = "free_y"
) +
labs(
title = "Series de precipitación - Estación C02 Rumihurco",
x = "Fecha",
y = "Precipitación [mm]",
caption = "Elaborado por: Arelys Quimbita"
) +
theme_bw() +
theme(
plot.title = element_text(
hjust = 0.5,
face = "bold"
),
strip.text = element_text(
face = "bold"
),
plot.caption = element_text(
hjust = 1
)
)
plot_series
Esta sección sigue la comparación de escalas temporales utilizada en clase.
# ============================================================
# ESCALAS TEMPORALES
# SERIE HORARIA Y SERIE MENSUAL
# ============================================================
# ------------------------------------------------------------
# SERIE HORARIA
# 1 hora = 12 intervalos de 5 minutos
# ------------------------------------------------------------
zdata_horaria <- aggregate(
zdata5min,
cut(index(zdata5min), "1 hour"),
function(x) suma_completa(x, 12)
)
data_horaria <- data.frame(
fecha = as.POSIXct(index(zdata_horaria)),
precipitacion = as.numeric(zdata_horaria)
)
# ------------------------------------------------------------
# SERIE MENSUAL
# ------------------------------------------------------------
data0$mes <- format(
data0$fecha,
"%Y-%m"
)
data_mensual <- aggregate(
valor ~ mes,
data = data0,
FUN = function(x) {
if (all(is.na(x))) {
NA_real_
} else {
sum(x, na.rm = TRUE)
}
}
)
data_mensual$fecha <- as.Date(
paste0(
data_mensual$mes,
"-01"
)
)
# ------------------------------------------------------------
# GRÁFICO HORARIO
# ------------------------------------------------------------
plot_horaria <- ggplot(
data = data_horaria,
aes(
x = fecha,
y = precipitacion
)
) +
geom_line(
linewidth = 0.25
) +
labs(
title = "Serie temporal horaria",
x = "Fecha",
y = "Precipitación [mm]"
) +
theme_bw() +
theme(
plot.title = element_text(
hjust = 0.5,
face = "bold"
)
)
# ------------------------------------------------------------
# GRÁFICO MENSUAL
# ------------------------------------------------------------
plot_mensual <- ggplot(
data = data_mensual,
aes(
x = fecha,
y = valor
)
) +
geom_line(
linewidth = 0.6
) +
labs(
title = "Serie temporal mensual",
x = "Fecha",
y = "Precipitación acumulada [mm]"
) +
theme_bw() +
theme(
plot.title = element_text(
hjust = 0.5,
face = "bold"
)
)
# ------------------------------------------------------------
# FIGURA COMBINADA
# ------------------------------------------------------------
figura_escalas <- (
plot_horaria /
plot_mensual
) +
plot_annotation(
title = "Escalas temporales de precipitación - Estación C02 Rumihurco",
caption = "Elaborado por: Arelys Quimbita"
)
figura_escalas
Interpretación: La serie horaria conserva con mayor detalle los eventos de corta duración y los picos de precipitación. La agregación mensual suaviza la variabilidad de corto plazo y permite observar el comportamiento acumulado de la precipitación a una escala temporal mayor.
De acuerdo con las expresiones utilizadas en clase se calculan parámetros de localización, dispersión y forma.
# ============================================================
# FUNCIÓN DE ESTADÍSTICA DESCRIPTIVA
# ============================================================
estadistica_serie <- function(x) {
# Eliminar NA
x <- x[!is.na(x)]
# Número de observaciones
n <- length(x)
# Parámetros de localización
promedio <- mean(x)
mediana <- median(x)
# Parámetros de dispersión
minimo <- min(x)
maximo <- max(x)
rango <- maximo - minimo
varianza <- var(x)
desviacion <- sd(x)
# Coeficiente de variación:
# Cv = Sx / media
coef_variacion <- desviacion / promedio
# Coeficiente de asimetría:
# gx = [1/n sum(xi - media)^3] / Sx^3
coef_asimetria <- (
sum((x - promedio)^3) / n
) / desviacion^3
# Exceso de curtosis:
# wx = [1/n sum(xi - media)^4] / Sx^4 - 3
curtosis <- (
sum((x - promedio)^4) / n
) / desviacion^4 - 3
return(
c(
Promedio = promedio,
Mediana = mediana,
Rango = rango,
Varianza = varianza,
Desviacion_estandar = desviacion,
Coef_variacion = coef_variacion,
Coef_asimetria = coef_asimetria,
Curtosis = curtosis
)
)
}
# ============================================================
# ESTADÍSTICA PARA CADA DURACIÓN
# ============================================================
est5 <- estadistica_serie(
as.numeric(zdata5min)
)
est15 <- estadistica_serie(
as.numeric(zdata15min)
)
est30 <- estadistica_serie(
as.numeric(zdata30min)
)
est120 <- estadistica_serie(
as.numeric(zdata120min)
)
est1440 <- estadistica_serie(
as.numeric(zdata1440min)
)
# ============================================================
# TABLA DE RESULTADOS
# ============================================================
tabla_estadistica <- data.frame(
Duracion = c(
"5 min",
"15 min",
"30 min",
"120 min",
"1440 min"
),
rbind(
est5,
est15,
est30,
est120,
est1440
)
)
# Redondear
tabla_estadistica[, -1] <- round(
tabla_estadistica[, -1],
3
)
tabla_estadistica
## Duracion Promedio Mediana Rango Varianza Desviacion_estandar
## est5 5 min 0.011 0.0 6.4 0.008 0.090
## est15 15 min 0.033 0.0 12.4 0.059 0.242
## est30 30 min 0.066 0.0 19.5 0.187 0.432
## est120 120 min 0.267 0.0 25.5 1.539 1.241
## est1440 1440 min 3.278 0.1 49.2 45.012 6.709
## Coef_variacion Coef_asimetria Curtosis
## est5 8.194 18.988 568.695
## est15 7.304 16.180 402.019
## est30 6.511 13.349 271.677
## est120 4.651 7.953 84.189
## est1440 2.047 3.056 10.892
# ============================================================
# BOXPLOT DE PRECIPITACIÓN POR DURACIÓN
# ============================================================
datos_box <- series_total[
!is.na(series_total$precipitacion),
]
plot_box <- ggplot(
data = datos_box,
aes(
x = duracion,
y = precipitacion
)
) +
geom_boxplot(
outlier.size = 0.4
) +
labs(
title = "Distribución de la precipitación por duración",
x = "Duración",
y = "Precipitación [mm]",
caption = "Elaborado por: Arelys Quimbita"
) +
theme_bw() +
theme(
plot.title = element_text(
hjust = 0.5,
face = "bold"
),
plot.caption = element_text(
hjust = 1
)
)
plot_box
El boxplot permite observar la mediana, los percentiles 25 y 75, los bigotes y los posibles valores atípicos, siguiendo el esquema de estadística descriptiva mostrado en clase.
# ============================================================
# HISTOGRAMAS DE PRECIPITACIÓN
# ============================================================
hist_data <- series_total[
!is.na(series_total$precipitacion),
]
plot_hist <- ggplot(
data = hist_data,
aes(
x = precipitacion
)
) +
geom_histogram(
bins = 20,
color = "black",
fill = "grey75"
) +
facet_wrap(
~ duracion,
scales = "free",
ncol = 2
) +
labs(
title = "Histogramas de precipitación - Estación C02 Rumihurco",
x = "Precipitación [mm]",
y = "Frecuencia",
caption = "Elaborado por: Arelys Quimbita"
) +
theme_bw() +
theme(
plot.title = element_text(
hjust = 0.5,
face = "bold"
),
strip.text = element_text(
face = "bold"
),
plot.caption = element_text(
hjust = 1
)
)
plot_hist
Esta figura reproduce la lógica utilizada en clase, mostrando frecuencia absoluta y densidad.
# ============================================================
# HISTOGRAMAS HORARIO Y MENSUAL
# FRECUENCIA Y DENSIDAD
# ============================================================
# Eliminar NA
horaria_hist <- data_horaria[
!is.na(data_horaria$precipitacion),
]
mensual_hist <- data_mensual[
!is.na(data_mensual$valor),
]
# ------------------------------------------------------------
# Frecuencia horaria
# ------------------------------------------------------------
hist_h_frec <- ggplot(
horaria_hist,
aes(x = precipitacion)
) +
geom_histogram(
bins = 20,
color = "black",
fill = "grey75"
) +
labs(
title = "Histograma de precipitación horaria",
x = "Precipitación [mm]",
y = "Frecuencia"
) +
theme_bw()
# ------------------------------------------------------------
# Densidad horaria
# ------------------------------------------------------------
hist_h_dens <- ggplot(
horaria_hist,
aes(
x = precipitacion,
y = after_stat(density)
)
) +
geom_histogram(
bins = 20,
color = "black",
fill = "grey75"
) +
labs(
title = "Histograma de precipitación horaria",
x = "Precipitación [mm]",
y = "Densidad"
) +
theme_bw()
# ------------------------------------------------------------
# Frecuencia mensual
# ------------------------------------------------------------
hist_m_frec <- ggplot(
mensual_hist,
aes(x = valor)
) +
geom_histogram(
bins = 13,
color = "black",
fill = "grey75"
) +
labs(
title = "Histograma de precipitación mensual",
x = "Precipitación [mm]",
y = "Frecuencia"
) +
theme_bw()
# ------------------------------------------------------------
# Densidad mensual
# ------------------------------------------------------------
hist_m_dens <- ggplot(
mensual_hist,
aes(
x = valor,
y = after_stat(density)
)
) +
geom_histogram(
bins = 13,
color = "black",
fill = "grey75"
) +
labs(
title = "Histograma de precipitación mensual",
x = "Precipitación [mm]",
y = "Densidad"
) +
theme_bw()
# ------------------------------------------------------------
# Figura 2 x 2
# ------------------------------------------------------------
figura_frecuencia <- (
hist_h_frec | hist_m_frec
) /
(
hist_h_dens | hist_m_dens
) +
plot_annotation(
title = "Análisis de frecuencia - Estación C02 Rumihurco",
caption = "Elaborado por: Arelys Quimbita"
)
figura_frecuencia
Esto se parece mucho más a la diapositiva de “Frecuencia – H44” de tu ingeniero, donde presenta arriba la frecuencia y abajo la densidad para las escalas horaria y mensual.
# ============================================================
# INTENSIDAD DE PRECIPITACIÓN
# I = P / t
# ============================================================
i5 <- zdata5min / (5 / 60)
i15 <- zdata15min / (15 / 60)
i30 <- zdata30min / (30 / 60)
i120 <- zdata120min / (120 / 60)
i1440 <- zdata1440min / (1440 / 60)
# ============================================================
# FUNCIÓN PARA OBTENER EL MÁXIMO DE CADA AÑO
# ============================================================
max_anual <- function(serie) {
fechas <- index(serie)
# Obtener año
anio <- format(
as.Date(fechas),
"%Y"
)
resultado <- aggregate(
as.numeric(serie),
by = list(
Anio = anio
),
FUN = function(x) {
if (all(is.na(x))) {
NA_real_
} else {
max(
x,
na.rm = TRUE
)
}
}
)
return(resultado)
}
# Máximos para cada duración
max5 <- max_anual(i5)
max15 <- max_anual(i15)
max30 <- max_anual(i30)
max120 <- max_anual(i120)
max1440 <- max_anual(i1440)
# ============================================================
# PREPARAR TABLAS
# ============================================================
m5 <- max5
names(m5) <- c(
"Anio",
"X5min"
)
m15 <- max15
names(m15) <- c(
"Anio",
"X15min"
)
m30 <- max30
names(m30) <- c(
"Anio",
"X30min"
)
m120 <- max120
names(m120) <- c(
"Anio",
"X120min"
)
m1440 <- max1440
names(m1440) <- c(
"Anio",
"X1440min"
)
# Periodo de análisis
m5 <- m5[
m5$Anio >= "2019" &
m5$Anio <= "2025",
]
m15 <- m15[
m15$Anio >= "2019" &
m15$Anio <= "2025",
]
m30 <- m30[
m30$Anio >= "2019" &
m30$Anio <= "2025",
]
m120 <- m120[
m120$Anio >= "2019" &
m120$Anio <= "2025",
]
m1440 <- m1440[
m1440$Anio >= "2019" &
m1440$Anio <= "2025",
]
# Unir por año
tabla_maximos <- merge(
m5,
m15,
by = "Anio"
)
tabla_maximos <- merge(
tabla_maximos,
m30,
by = "Anio"
)
tabla_maximos <- merge(
tabla_maximos,
m120,
by = "Anio"
)
tabla_maximos <- merge(
tabla_maximos,
m1440,
by = "Anio"
)
tabla_maximos
## Anio X5min X15min X30min X120min X1440min
## 1 2019 68.4 44.0 39.0 9.80 1.6250000
## 2 2020 57.6 28.0 21.8 9.85 0.5541667
## 3 2021 58.8 49.6 33.6 12.70 2.0500000
## 4 2022 49.2 43.6 30.4 8.55 1.3750000
## 5 2023 62.4 45.6 27.6 11.00 1.6750000
## 6 2024 43.2 29.6 22.8 8.00 1.9041667
## 7 2025 76.8 46.4 31.8 12.75 1.8166667
# ============================================================
# PASAR TABLA A FORMATO LARGO
# ============================================================
maximos_largos <- melt(
tabla_maximos,
id.vars = "Anio",
variable.name = "Duracion",
value.name = "Intensidad"
)
# Etiquetas
maximos_largos$Duracion <- factor(
maximos_largos$Duracion,
levels = c(
"X5min",
"X15min",
"X30min",
"X120min",
"X1440min"
),
labels = c(
"5 min",
"15 min",
"30 min",
"120 min",
"1440 min"
)
)
# Año numérico
maximos_largos$Anio <- as.numeric(
as.character(
maximos_largos$Anio
)
)
# ============================================================
# GRÁFICO
# ============================================================
plot_maximos <- ggplot(
data = maximos_largos,
aes(
x = Anio,
y = Intensidad
)
) +
geom_point(
size = 2
) +
geom_smooth(
method = "lm",
formula = y ~ x,
se = FALSE,
linetype = "dashed",
linewidth = 0.8
) +
facet_wrap(
~ Duracion,
scales = "free_y",
ncol = 2
) +
scale_x_continuous(
breaks = 2019:2025
) +
labs(
title = "Máximas anuales de intensidad - Estación C02 Rumihurco",
subtitle = "Análisis de tendencia por duración",
x = "Año",
y = "Intensidad máxima anual [mm/h]",
caption = "Elaborado por: Arelys Quimbita"
) +
theme_bw() +
theme(
plot.title = element_text(
hjust = 0.5,
face = "bold"
),
plot.subtitle = element_text(
hjust = 0.5
),
strip.text = element_text(
face = "bold"
),
plot.caption = element_text(
hjust = 1
)
)
plot_maximos
Interpretación de tendencia: Las series de máximas anuales presentan comportamientos diferentes según la duración. Debido a que el periodo disponible comprende únicamente siete años, las líneas ajustadas deben considerarse como una evaluación visual preliminar y no como evidencia estadística concluyente de estacionaridad o no estacionaridad.
# ============================================================
# HISTOGRAMAS DE MÁXIMAS ANUALES
# ============================================================
plot_hist_max <- ggplot(
data = maximos_largos,
aes(
x = Intensidad
)
) +
geom_histogram(
bins = 4,
color = "black",
fill = "grey75"
) +
facet_wrap(
~ Duracion,
scales = "free_x",
ncol = 2
) +
labs(
title = "Histogramas de máximas anuales de intensidad",
subtitle = "Estación C02 - Rumihurco",
x = "Intensidad máxima anual [mm/h]",
y = "Frecuencia",
caption = "Elaborado por: Arelys Quimbita"
) +
theme_bw() +
theme(
plot.title = element_text(
hjust = 0.5,
face = "bold"
),
plot.subtitle = element_text(
hjust = 0.5
),
strip.text = element_text(
face = "bold"
),
plot.caption = element_text(
hjust = 1
)
)
plot_hist_max
Las series muestran que la agregación temporal influye directamente en la magnitud y variabilidad de la precipitación. Las duraciones cortas presentan una alta concentración de valores bajos y eventos intensos aislados, produciendo distribuciones fuertemente asimétricas.
Al aumentar la duración, aumenta la precipitación acumulada y la dispersión absoluta. Sin embargo, disminuyen la asimetría y el coeficiente de variación, indicando una reducción de la variabilidad relativa.
Las intensidades máximas anuales presentan un comportamiento inverso: las mayores intensidades corresponden a las duraciones cortas, mientras que la intensidad disminuye conforme aumenta la duración del intervalo de acumulación.
El análisis de tendencias de máximos anuales permite identificar posibles cambios temporales; sin embargo, debido a la longitud limitada del registro entre 2019 y 2025, estas tendencias deben interpretarse con cautela.