Microclimas urbanos en Barranquilla: Análisis de la variabilidad meteorológica según la localidad

Author

Sebastián Pérez Albor, Eliana Rodriguez Martinez

Published

February 17, 2025

En las últimas décadas, el crecimiento acelerado de las áreas urbanas ha generado cambios significativos en los patrones climáticos locales, dando lugar a la formación de microclimas urbanos. Estos microclimas se caracterizan por variaciones en la temperatura, humedad, velocidad del viento y otros factores meteorológicos, que difieren de las condiciones climáticas de las áreas rurales circundantes. Comprender y clasificar estos microclimas es crucial para la planificación urbana, la gestión de recursos energéticos y la mejora de la calidad de vida de los habitantes.

Además, en un contexto global de transición hacia fuentes de energía renovable, la energía solar se ha posicionado como una de las alternativas más prometedoras. Sin embargo, la eficiencia de los sistemas de captación de energía solar depende en gran medida de las condiciones meteorológicas locales, como la radiación solar, la nubosidad y la temperatura. Por lo tanto, predecir con precisión el potencial de energía solar en diferentes áreas urbanas es fundamental para optimizar la instalación de paneles solares y maximizar su rendimiento.

Paquetes y librerias necesarias

Code
# Carga de paquetes necesarios

# 📊 Manejo y visualización de datos
library(tidyverse)   # Incluye ggplot2, dplyr, tidyr, readr, tibble, stringr, forcats, purrr
library(ggpubr)      # Gráficos mejorados con ggplot2
library(patchwork)   # Para combinar múltiples gráficos
library(ggcorrplot)  # Para gráficos de correlación
library(ggplot2)     # (ya incluido en tidyverse, pero puedes mantenerlo si usas versiones específicas)
library(viridis)     # Mejor paleta de colores para gráficos

# 📋 Manejo de datos y transformación
library(Hmisc)       # Funciones avanzadas de estadística
library(knitr)       # Para formatear tablas en R Markdown
library(DT)          # Para tablas interactivas
library(pander)      # Para formateo de reportes
library(reshape2)    # Para manipulación de datos en formato largo/ancho

# 📈 Estadística y análisis de datos
library(nortest)     # Pruebas de normalidad
library(naniar)      # Visualización y manejo de datos faltantes
library(EnvStats)    # Métodos avanzados de análisis ambiental

# 📦 Utilidades adicionales
library(gridExtra)   # Para organizar gráficos en cuadrículas

Importación de datos

Code
dataclimabq <- read_csv("C:/Users/sebas/OneDrive/Escritorio/Clases/2025-10/DataViz/dataclimabq.csv")

Glosario de Variables Climáticas y Meteorológicas

Este glosario describe las variables utilizadas en el análisis de microclimas y aprovechamiento de energía solar.

Variables Atmosféricas y Meteorológicas

  • T2M: MERRA-2 Temperatura a 2 Metros (°C).
    • Indica la temperatura del aire a una altura de 2 metros sobre la superficie.
  • RH2M: MERRA-2 Humedad Relativa a 2 Metros (%).
    • Mide la cantidad de humedad en el aire en relación con la máxima posible a esa temperatura.
  • PRECTOTCORR: MERRA-2 Precipitación Corregida (mm/hora).
    • Representa la tasa de precipitación (lluvia, nieve) ajustada según correcciones satelitales.
  • WS10M: MERRA-2 Velocidad del Viento a 10 Metros (m/s).
    • Velocidad del viento medida a una altura de 10 metros sobre la superficie.
  • WD10M: MERRA-2 Dirección del Viento a 10 Metros (Grados).
    • Indica la dirección desde la que sopla el viento, en grados (0° = Norte, 90° = Este, etc.).
  • PS: MERRA-2 Presión Superficial (kPa).
    • Mide la presión atmosférica en la superficie terrestre.

Variables Relacionadas con la Energía Solar

  • ALLSKY_SFC_UV_INDEX: CERES SYN1deg Índice UV en Superficie Bajo Cielo Total (W m⁻² × 40).
    • Mide la intensidad de la radiación ultravioleta en la superficie terrestre.
  • ALLSKY_SFC_SW_DIFF: CERES SYN1deg Irradiancia Difusa de Onda Corta en Superficie Bajo Cielo Total (Wh/m²).
    • Cantidad de energía solar dispersada en la atmósfera que llega a la superficie terrestre.
  • ALLSKY_SRF_ALB: CERES SYN1deg Albedo Superficial Bajo Cielo Total (adimensional).
    • Fracción de la radiación solar reflejada por la superficie terrestre respecto a la radiación recibida.
  • ALLSKY_KT: CERES SYN1deg Índice de Claridad de Insolación Bajo Cielo Total (adimensional).
    • Relación entre la irradiancia solar real y la irradiancia solar máxima teórica en ausencia de atmósfera.

Variables Adicionales

  • T2MDEW: MERRA-2 Punto de Rocío/Escarcha a 2 Metros (°C).
    • Temperatura a la cual el aire debe enfriarse para que el vapor de agua se condense en forma de rocío.
  • T2MWET: MERRA-2 Temperatura de Bulbo Húmedo a 2 Metros (°C).
    • Mide la temperatura mínima alcanzable por evaporación de agua en condiciones de enfriamiento adiabático.
  • WS50M: MERRA-2 Velocidad del Viento a 50 Metros (m/s).
    • Velocidad del viento medida a 50 metros de altura, relevante para el aprovechamiento eólico.

Variables Geográficas

  • Loacality: Localidad donde se tomaron los datos.
  • NHBD: Neighborhood, barrio donde se registraron las mediciones.

Este glosario permite una mejor comprensión de los datos utilizados en el análisis de microclimas y potencial de energía solar. ### Objetivos:

  • Impacto en la planificación urbana: La clasificación de microclimas permite identificar áreas con condiciones climáticas extremas (como islas de calor urbanas) y proponer medidas de mitigación, como la creación de espacios verdes o la implementación de techos reflectantes.

  • Optimización de energía renovable: La predicción de energía solar potencial es esencial para diseñar sistemas de energía solar más eficientes y reducir la dependencia de combustibles fósiles.

  • Contribución científica: Este proyecto utiliza datos de alta resolución de fuentes como MERRA-2 y CERES SYN1deg, lo que permite un análisis detallado y preciso de las variables meteorológicas. Los resultados están pensados para ser publicados y servir como base para futuras investigaciones. ## Datos

Code
# Vista de los datos
datatable(
  dataclimabq[1:100, ],
  caption = "Data Frame: dataclimabq",
  options = list(
    scrollX = TRUE,
    scrollY = "450px"
  )
)
Code
str(dataclimabq)
spc_tbl_ [1,535,519 × 19] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ YEAR               : num [1:1535519] 2020 2020 2020 2020 2020 2020 2020 2020 2020 2020 ...
 $ MO                 : num [1:1535519] 1 1 1 1 1 1 1 1 1 1 ...
 $ DY                 : num [1:1535519] 1 1 1 1 1 1 1 1 1 1 ...
 $ HR                 : num [1:1535519] 0 1 2 3 4 5 6 7 8 9 ...
 $ T2M                : num [1:1535519] 26.7 26.6 26.4 26.4 26.3 ...
 $ PRECTOTCORR        : num [1:1535519] 0.03 0.03 0.02 0 0 0 0 0 0 0 ...
 $ RH2M               : num [1:1535519] 86.2 85.8 85.6 85 84.2 ...
 $ WS10M              : num [1:1535519] 7.87 8.02 8.05 8.01 7.88 7.8 7.58 7.18 6.97 6.34 ...
 $ WD10M              : num [1:1535519] 40 41.8 42.7 43.7 45.6 48.5 51.6 53.7 55.7 56.3 ...
 $ PS                 : num [1:1535519] 101 101 101 100 100 ...
 $ ALLSKY_SFC_UV_INDEX: num [1:1535519] 0 0 0 0 0 0 0.03 0.43 1.92 4.5 ...
 $ ALLSKY_SRF_ALB     : num [1:1535519] -999 -999 -999 -999 -999 -999 0.14 0.15 0.14 0.14 ...
 $ ALLSKY_SFC_SW_DIFF : num [1:1535519] 0 0 0 0 0 ...
 $ T2MDEW             : num [1:1535519] 24.2 24 23.8 23.6 23.4 ...
 $ T2MWET             : num [1:1535519] 25.4 25.3 25.1 25 24.9 ...
 $ WS50M              : num [1:1535519] 10.1 10.3 10.3 10.2 10.1 ...
 $ ALLSKY_KT          : num [1:1535519] -999 -999 -999 -999 -999 -999 0.27 0.45 0.55 0.63 ...
 $ LOCALITY           : chr [1:1535519] "Centro" "Centro" "Centro" "Centro" ...
 $ NBHD               : chr [1:1535519] "BarrioAbajo" "BarrioAbajo" "BarrioAbajo" "BarrioAbajo" ...
 - attr(*, "spec")=
  .. cols(
  ..   YEAR = col_double(),
  ..   MO = col_double(),
  ..   DY = col_double(),
  ..   HR = col_double(),
  ..   T2M = col_double(),
  ..   PRECTOTCORR = col_double(),
  ..   RH2M = col_double(),
  ..   WS10M = col_double(),
  ..   WD10M = col_double(),
  ..   PS = col_double(),
  ..   ALLSKY_SFC_UV_INDEX = col_double(),
  ..   ALLSKY_SRF_ALB = col_double(),
  ..   ALLSKY_SFC_SW_DIFF = col_double(),
  ..   T2MDEW = col_double(),
  ..   T2MWET = col_double(),
  ..   WS50M = col_double(),
  ..   ALLSKY_KT = col_double(),
  ..   LOCALITY = col_character(),
  ..   NBHD = col_character()
  .. )
 - attr(*, "problems")=<externalptr> 

Datos nulos por columna

Code
# Contar valores nulos en cada columna
colSums(is.na(dataclimabq))
               YEAR                  MO                  DY                  HR 
                  0                   0                   0                   0 
                T2M         PRECTOTCORR                RH2M               WS10M 
                  0                   0                   0                   0 
              WD10M                  PS ALLSKY_SFC_UV_INDEX      ALLSKY_SRF_ALB 
                  0                   0                   0                   0 
 ALLSKY_SFC_SW_DIFF              T2MDEW              T2MWET               WS50M 
                  0                   0                   0                   0 
          ALLSKY_KT            LOCALITY                NBHD 
                  0                   0                   0 
Code
# Contar la cantidad de valores -999 en cada columna
count_neg_999 <- colSums(dataclimabq == -999)

# Imprimir resultados
cat("\nCount of -999 values in each column:\n")

Count of -999 values in each column:
Code
print(count_neg_999)
               YEAR                  MO                  DY                  HR 
                  0                   0                   0                   0 
                T2M         PRECTOTCORR                RH2M               WS10M 
                  0                   0                   0                   0 
              WD10M                  PS ALLSKY_SFC_UV_INDEX      ALLSKY_SRF_ALB 
                  0                   0              130425              365040 
 ALLSKY_SFC_SW_DIFF              T2MDEW              T2MWET               WS50M 
             579409                   0                   0                   0 
          ALLSKY_KT            LOCALITY                NBHD 
             814249                   0                   0 

En el análisis de datos tipo meteorológicos y climáticos, es común encontrar valores faltantes que no pueden ser calculados o que están fuera del rango de disponibilidad de las fuentes. Estos valores en este caso han sido representados con un código específico, como -999, para indicar su ausencia. En este contexto, se trabajó con un conjunto de (1)Variables. Estos mismos, provenientes de fuentes como MERRA-2 y CERES SYN1deg, son fundamentales para estudios climáticos, pero requieren un manejo cuidadoso de los valores faltantes para garantizar la precisión y confiabilidad de los análisis.

Limpieza de datos

Code
# Seleccionar solo las columnas numéricas
numeric_cols <- sapply(dataclimabq, is.numeric)

# Crear una copia del dataframe original
dataclimabq_cleaned <- dataclimabq

# Reemplazar -999.0 por NA solo en columnas numéricas
dataclimabq_cleaned[numeric_cols] <- lapply(dataclimabq_cleaned[numeric_cols], function(x) replace(x, x == -999.0, NA))

# Calcular el porcentaje de datos faltantes por columna
missing_per_col <- colSums(is.na(dataclimabq_cleaned)) / nrow(dataclimabq_cleaned) * 100

# Calcular el porcentaje total de datos faltantes
total_missing <- sum(is.na(dataclimabq_cleaned))
total_values <- prod(dim(dataclimabq_cleaned))
missing_percentage <- (total_missing / total_values) * 100
Code
suppressWarnings(require(Amelia))

missmap(dataclimabq_cleaned, col = c("#fde725","#440154" ))

Code
gg_miss_var(dataclimabq_cleaned, show_pct = TRUE) +
  geom_hline(yintercept = 50, linetype = "dashed", color = "black") +
  labs(title = "Cantidad de NA por columna") +
  theme_minimal()

Code
# Imprimir resultados
cat("Porcentaje de datos faltantes por variable:\n")
Porcentaje de datos faltantes por variable:
Code
print(missing_per_col)
               YEAR                  MO                  DY                  HR 
           0.000000            0.000000            0.000000            0.000000 
                T2M         PRECTOTCORR                RH2M               WS10M 
           0.000000            0.000000            0.000000            0.000000 
              WD10M                  PS ALLSKY_SFC_UV_INDEX      ALLSKY_SRF_ALB 
           0.000000            0.000000            8.493871           23.773070 
 ALLSKY_SFC_SW_DIFF              T2MDEW              T2MWET               WS50M 
          37.733756            0.000000            0.000000            0.000000 
          ALLSKY_KT            LOCALITY                NBHD 
          53.027608            0.000000            0.000000 
Code
cat("\nPorcentaje total de datos faltantes:", round(missing_percentage, 2), "%\n")

Porcentaje total de datos faltantes: 6.48 %

Pero este porcentaje varía considerablemente entre las variables. Las variables con mayor porcentaje de datos faltantes son:

Variable Porcentaje de Datos Faltantes - ALLSKY_SFC_UV_INDEX 8.49% - ALLSKY_SRF_ALB 23.77% - ALLSKY_SFC_SW_DIFF 37.73% - ALLSKY_KT 53.03% Estos valores faltantes están representados con el valor -999, que indica que los datos no pudieron calcularse o están fuera del rango de disponibilidad de las fuentes.

Los porcentajes anteriormente mostrados se pueden visualizar de una mejor manera en las graficas anteriores:

Análisis de Correlación

Code
numeric_data <- dataclimabq_cleaned[sapply(dataclimabq_cleaned, is.numeric)]

# Calcular la matriz de correlación
corr_matrix <- cor(numeric_data, use = "pairwise.complete.obs")

# Crear el heatmap sin números en las celdas y sin coord_fixed()
ggcorrplot(corr_matrix, 
           method = "square", 
           type = "lower",  # Mostrar solo la mitad inferior
           lab = FALSE,     # Eliminar los números dentro de las celdas
           colors = c("blue", "white", "red"), 
           title = "Matriz de Correlación de Variables Numéricas",
           ggtheme = theme_minimal()) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 10),  # Rotar etiquetas para mejor legibilidad
        axis.text.y = element_text(size = 10))

Análisis de Correlación entre Variables Climáticas

Este análisis busca entender las relaciones entre distintas variables climáticas para apoyar la planificación urbana y la optimización de energía renovable.

Puntos clave del análisis de correlación:

1️. Temperatura y Radiación Solar
  • T2M (Temperatura a 2m) y ALLSKY_SFC_SW_DIFF (Irradiancia de onda corta difusa) tienen una fuerte correlación positiva (~0.73).
  • Esto indica que temperaturas más altas suelen estar asociadas con una mayor irradiancia solar difusa, lo que es clave para modelos de energía solar.
2️. Humedad y Precipitación
  • RH2M (Humedad relativa a 2m) y PRECTOTCORR (Precipitación corregida) presentan una correlación negativa moderada (~ -0.76).
  • A mayor precipitación, la humedad relativa disminuye, lo cual puede indicar cambios abruptos en el clima urbano.
3️. Viento y Altura
  • WS10M (Viento a 10m) y WS50M (Viento a 50m) tienen una correlación de casi 0.99.
  • Esto sugiere que la velocidad del viento a distintas alturas está altamente relacionada, lo que es útil para evaluar viabilidad de proyectos eólicos.
4️. Presión Superficial y Radiación Solar
  • ALLSKY_SFC_UV_INDEX (Índice UV) y PS (Presión superficial) tienen correlación negativa (~ -0.25).
  • Puede indicar que a mayor presión, la radiación UV en la superficie tiende a disminuir, lo que podría afectar la eficiencia de paneles solares, no obstante la relación entre ambas variables es bastante debil.

Distribución de Variables

Code
dataclimabq_long <- melt(dataclimabq_cleaned)
dataclimabq_long <- na.omit(dataclimabq_long)

# Crear boxplot
ggplot(dataclimabq_long, aes(x = factor(1), y = value, fill = variable)) + 
  geom_boxplot(outlier.colour = "red", outlier.shape = 8, outlier.size = 3) +  
  facet_wrap(~ variable, scales = "free_y") + 
  labs(x = "Variables", y = "Valores", title = "Distribución de Variables en dataclimabq") +  
  theme_minimal() +
  theme(axis.text.x = element_blank(),  
        strip.text = element_text(size = 12),  
        axis.text.y = element_text(size = 10),  
        legend.position = "none") +  
  scale_fill_viridis_d(option = "plasma")  # Paleta de colores para más de 12 variables

Code
pander(summary(dataclimabq_cleaned))
Table continues below
YEAR MO DY HR T2M
Min. :2020 Min. : 1.000 Min. : 1.00 Min. : 0.0 Min. :22.88
1st Qu.:2021 1st Qu.: 4.000 1st Qu.: 8.00 1st Qu.: 6.0 1st Qu.:26.91
Median :2022 Median : 7.000 Median :16.00 Median :12.0 Median :27.93
Mean :2022 Mean : 6.518 Mean :15.73 Mean :11.5 Mean :28.21
3rd Qu.:2023 3rd Qu.:10.000 3rd Qu.:23.00 3rd Qu.:17.5 3rd Qu.:29.46
Max. :2025 Max. :12.000 Max. :31.00 Max. :23.0 Max. :35.36
NA NA NA NA NA
Table continues below
PRECTOTCORR RH2M WS10M WD10M
Min. : 0.00 Min. : 0.00 Min. : 0.040 Min. : 0.00
1st Qu.: 2.60 1st Qu.: 0.09 1st Qu.: 3.560 1st Qu.: 30.30
Median : 71.73 Median : 3.13 Median : 5.410 Median : 42.00
Mean : 53.68 Mean : 29.63 Mean : 5.292 Mean : 66.46
3rd Qu.: 86.14 3rd Qu.: 73.71 3rd Qu.: 6.970 3rd Qu.: 57.20
Max. :684.23 Max. :684.23 Max. :11.720 Max. :359.80
NA NA NA NA
Table continues below
PS ALLSKY_SFC_UV_INDEX ALLSKY_SRF_ALB ALLSKY_SFC_SW_DIFF
Min. : 99.97 Min. : 0.00 Min. : 0.0 Min. : 0.0
1st Qu.:100.43 1st Qu.: 0.00 1st Qu.: 0.0 1st Qu.: 0.0
Median :100.54 Median : 0.00 Median : 0.1 Median : 0.1
Mean :100.55 Mean : 2.38 Mean : 72.1 Mean : 45.0
3rd Qu.:100.66 3rd Qu.: 3.95 3rd Qu.:139.2 3rd Qu.: 11.1
Max. :101.27 Max. :17.52 Max. :585.1 Max. :525.3
NA NA’s :130425 NA’s :365040 NA’s :579409
Table continues below
T2MDEW T2MWET WS50M ALLSKY_KT
Min. :17.85 Min. :22.05 Min. : 0.040 Min. :0.0
1st Qu.:23.19 1st Qu.:25.44 1st Qu.: 4.440 1st Qu.:0.4
Median :24.43 Median :26.33 Median : 6.730 Median :0.6
Mean :24.25 Mean :26.23 Mean : 6.639 Mean :0.5
3rd Qu.:25.43 3rd Qu.:27.08 3rd Qu.: 8.790 3rd Qu.:0.7
Max. :27.97 Max. :29.61 Max. :15.150 Max. :0.8
NA NA NA NA’s :814249
LOCALITY NBHD
Length:1535519 Length:1535519
Class :character Class :character
Mode :character Mode :character
NA NA
NA NA
NA NA
NA NA

##Se detectan datos NA’S teniendo en cuenta el rango lógico de valores en los que deberían estar las variables numéricas

Code
# Crear una copia del dataframe limpio anterior
dataclimabq_limits <- dataclimabq_cleaned

# Definir los límites lógicos para cada variable en una lista
limits <- list(
  YEAR = c(2020, 2025),
  MO = c(1, 12),
  DY = c(1, 31),
  HR = c(0, 23),
  T2M = c(-90, 60),
  RH2M = c(0, 100),
  PRECTOTCORR = c(0, 300),
  WS10M = c(0, 80),
  WD10M = c(0, 360),
  PS = c(50, 110),
  ALLSKY_SFC_UV_INDEX = c(0, 15),
  ALLSKY_SFC_SW_DIFF = c(0, 1500),
  ALLSKY_SRF_ALB = c(0, 1),
  T2MDEW = c(-45, 35),
  T2MWET = c(-35, 35),
  WS50M = c(0, 75),
  ALLSKY_KT = c(0, 1)
)

# Reemplazar valores fuera de los límites por NA
for (col in names(limits)) {
  min_val <- limits[[col]][1]
  max_val <- limits[[col]][2]
  dataclimabq_limits[[col]] <- ifelse(dataclimabq_limits[[col]] < min_val | dataclimabq_limits[[col]] > max_val, NA, dataclimabq_limits[[col]])
}
Code
pander(summary(dataclimabq_limits))
Table continues below
YEAR MO DY HR T2M
Min. :2020 Min. : 1.000 Min. : 1.00 Min. : 0.0 Min. :22.88
1st Qu.:2021 1st Qu.: 4.000 1st Qu.: 8.00 1st Qu.: 6.0 1st Qu.:26.91
Median :2022 Median : 7.000 Median :16.00 Median :12.0 Median :27.93
Mean :2022 Mean : 6.518 Mean :15.73 Mean :11.5 Mean :28.21
3rd Qu.:2023 3rd Qu.:10.000 3rd Qu.:23.00 3rd Qu.:17.5 3rd Qu.:29.46
Max. :2025 Max. :12.000 Max. :31.00 Max. :23.0 Max. :35.36
NA NA NA NA NA
Table continues below
PRECTOTCORR RH2M WS10M WD10M
Min. : 0.00 Min. : 0.00 Min. : 0.040 Min. : 0.00
1st Qu.: 2.60 1st Qu.: 0.09 1st Qu.: 3.560 1st Qu.: 30.30
Median : 71.73 Median : 3.12 Median : 5.410 Median : 42.00
Mean : 53.65 Mean :29.41 Mean : 5.292 Mean : 66.46
3rd Qu.: 86.14 3rd Qu.:73.53 3rd Qu.: 6.970 3rd Qu.: 57.20
Max. :295.23 Max. :99.91 Max. :11.720 Max. :359.80
NA’s :132 NA’s :2231 NA NA
Table continues below
PS ALLSKY_SFC_UV_INDEX ALLSKY_SRF_ALB ALLSKY_SFC_SW_DIFF
Min. : 99.97 Min. : 0.00 Min. :0.0 Min. : 0.0
1st Qu.:100.43 1st Qu.: 0.00 1st Qu.:0.0 1st Qu.: 0.0
Median :100.54 Median : 0.00 Median :0.0 Median : 0.1
Mean :100.55 Mean : 2.35 Mean :0.1 Mean : 45.0
3rd Qu.:100.66 3rd Qu.: 3.88 3rd Qu.:0.1 3rd Qu.: 11.1
Max. :101.27 Max. :15.00 Max. :1.0 Max. :525.3
NA NA’s :133770 NA’s :838981 NA’s :579409
Table continues below
T2MDEW T2MWET WS50M ALLSKY_KT
Min. :17.85 Min. :22.05 Min. : 0.040 Min. :0.0
1st Qu.:23.19 1st Qu.:25.44 1st Qu.: 4.440 1st Qu.:0.4
Median :24.43 Median :26.33 Median : 6.730 Median :0.6
Mean :24.25 Mean :26.23 Mean : 6.639 Mean :0.5
3rd Qu.:25.43 3rd Qu.:27.08 3rd Qu.: 8.790 3rd Qu.:0.7
Max. :27.97 Max. :29.61 Max. :15.150 Max. :0.8
NA NA NA NA’s :814249
LOCALITY NBHD
Length:1535519 Length:1535519
Class :character Class :character
Mode :character Mode :character
NA NA
NA NA
NA NA
NA NA
Code
# Obtener las localidades únicas
localidades <- unique(dataclimabq_cleaned$LOCALITY)

# Imprimir resultados
cat("Localidades únicas:\n")
Localidades únicas:
Code
print(localidades)
[1] "Centro"    "Sur"       "Occidente" "Oriente"   "Norte"    
Code
dataclimabq_long <- melt(dataclimabq_limits)
dataclimabq_long <- na.omit(dataclimabq_long)
  
# Crear boxplot
ggplot(dataclimabq_long, aes(x = factor(1), y = value, fill = variable)) + 
  geom_boxplot(outlier.colour = "red", outlier.shape = 8, outlier.size = 3) +  
  facet_wrap(~ variable, scales = "free_y") + 
  labs(x = "Variables", y = "Valores", title = "Distribución de Variables en dataclimabq_limit") +  
  theme_minimal() +
  theme(axis.text.x = element_blank(),  
        strip.text = element_text(size = 12),  
        axis.text.y = element_text(size = 10),  
        legend.position = "none") +  
  scale_fill_viridis_d(option = "plasma")  # Paleta de colores para más de 12 variables

Debido a que la falta de datos en las variables ALLSKY_SRF_ALB y ALLSKY_KT procederemos a analizar la correlación entre los datos y ver alternativas para el tratamiento de los datos faltantes más allá de los comunes, como lo son el reemplazo por la media, la mediana o el descarte completo de los datos.

Code
library(ggplot2)
library(ggpubr)  
library(dplyr)

# Definir número máximo de variables a graficar por iteración
max_plots <- 6  # Ajusta este valor según tu necesidad

# Variables numéricas
numeric_cols <- names(select_if(dataclimabq_limits, is.numeric))

# Graficar histogramas y QQ plots por localidad
for (localidad in unique(dataclimabq_limits$LOCALITY)[1:3]) {  # Limita a las primeras 3 localidades para evitar demoras
  
  df_localidad <- filter(dataclimabq_limits, LOCALITY == localidad)
  
  # Seleccionar un subconjunto de variables si hay demasiadas
  vars_to_plot <- head(numeric_cols, max_plots)
  
  p1 <- ggplot(df_localidad %>% select(all_of(vars_to_plot)) %>% pivot_longer(cols = everything(), names_to = "Variable", values_to = "Valor"), 
               aes(x = Valor)) +
    geom_histogram(aes(y = ..density..), bins = 30, fill = "blue", alpha = 0.5) +
    geom_density(color = "red", size = 1) +
    facet_wrap(~Variable, scales = "free_x") +
    labs(title = paste("Histogramas en", localidad)) +
    theme_minimal()
  
  p2 <- ggplot(df_localidad %>% select(all_of(vars_to_plot)) %>% pivot_longer(cols = everything(), names_to = "Variable", values_to = "Valor"), 
               aes(sample = Valor)) +
    stat_qq() + 
    stat_qq_line(color = "red") +
    facet_wrap(~Variable, scales = "free") +
    labs(title = paste("QQ Plots en", localidad)) +
    theme_minimal()
  
  print(p1)
  print(p2)
}

Code
# Variables categóricas
categorical_cols <- names(select_if(dataclimabq_limits, is.character))

for (localidad in unique(dataclimabq_limits$LOCALITY)[1:3]) {  # Solo primeras 3 localidades para evitar demora
  
  df_localidad <- filter(dataclimabq_limits, LOCALITY == localidad)
  
  p_cat <- ggplot(df_localidad %>% pivot_longer(cols = all_of(categorical_cols), names_to = "Variable", values_to = "Valor"),
                  aes(x = Valor)) +
    geom_bar(fill = "skyblue") +
    facet_wrap(~Variable, scales = "free_x") +
    labs(title = paste("Histogramas de variables categóricas en", localidad), x = "Categoría", y = "Frecuencia") +
    theme_minimal() +
    theme(axis.text.x = element_text(angle = 45, hjust = 1))
  
  print(p_cat)
}

Code
# Test de Shapiro-Wilk (con muestreo si hay más de 5000 datos)
numeric_cols <- names(select_if(dataclimabq_limits, is.numeric))
normality_results <- data.frame(Variable = character(), Statistic = numeric(), P_value = numeric())

for (col in numeric_cols) {
  data <- na.omit(dataclimabq_limits[[col]])
  
  if (length(data) > 5000) {
    data <- sample(data, 5000)  # Tomar solo 5000 valores aleatorios
  }
  
  if (length(data) >= 3) {  # Verificar que haya suficientes datos
    shapiro_test <- shapiro.test(data)
    normality_results <- rbind(normality_results, 
                               data.frame(Variable = col, Statistic = shapiro_test$statistic, P_value = shapiro_test$p.value))
  }
}

print(normality_results)
               Variable Statistic      P_value
W                  YEAR 0.8875584 3.331691e-51
W1                   MO 0.9402099 5.984997e-41
W2                   DY 0.9546301 7.681732e-37
W3                   HR 0.9507762 4.959195e-38
W4                  T2M 0.9810888 2.607196e-25
W5          PRECTOTCORR 0.7684917 2.198835e-64
W6                 RH2M 0.7091419 7.143078e-69
W7                WS10M 0.9878596 2.455090e-20
W8                WD10M 0.5669880 4.535589e-77
W9                   PS 0.9985996 2.334209e-04
W10 ALLSKY_SFC_UV_INDEX 0.6944502 7.317944e-70
W11      ALLSKY_SRF_ALB 0.6536426 2.063272e-72
W12  ALLSKY_SFC_SW_DIFF 0.5683572 5.293430e-77
W13              T2MDEW 0.9794188 2.514067e-26
W14              T2MWET 0.9903412 5.357819e-18
W15               WS50M 0.9878936 2.628200e-20
W16           ALLSKY_KT 0.9376400 1.351150e-41
Code
# Test de Chi-cuadrado para variables categóricas vs. LOCALITY
categorical_cols <- names(select_if(dataclimabq_limits, is.character))
chi2_results <- data.frame(Variable = character(), Chi2 = numeric(), P_value = numeric(), DOF = numeric())

for (col in categorical_cols) {
  contingency_table <- table(dataclimabq_limits$LOCALITY, dataclimabq_limits[[col]])
  
  if (min(contingency_table) > 0) {  # Evitar errores por celdas vacías
    chi_test <- chisq.test(contingency_table)
    chi2_results <- rbind(chi2_results, 
                          data.frame(Variable = col, Chi2 = chi_test$statistic, P_value = chi_test$p.value, DOF = chi_test$parameter))
  }
}

print(chi2_results)
[1] Variable Chi2     P_value  DOF     
<0 rows> (o 0- extensión row.names)

Análisis de Normalidad y Distribución de Variables

El test de normalidad proporciona un valor estadístico y un p-valor. Si el p-valor es menor a 0.05, significa que rechazamos la hipótesis nula de que la variable sigue una distribución normal.


Resultados Clave:

Variables con mejor ajuste a la normalidad (mayores estadísticas y p-valores más altos, pero aún no completamente normales):
  • T2M (Temperatura a 2 metros): $ p = 7.63 ^{-107} $
  • T2MDEW (Punto de rocío a 2 metros): $ p = 6.92 ^{-107} $
  • T2MWET (Temperatura húmeda a 2 metros): $ p = 1.67 ^{-90} $
  • WS10M (Viento a 10 metros): $ p = 4.65 ^{-97} $
  • WS50M (Viento a 50 metros): $ p = 1.74 ^{-94} $

Estas variables están relativamente cerca de una normalidad aceptable, aunque siguen mostrando desviaciones.

Variables que NO siguen una distribución normal (p-valores extremadamente pequeños, estadística baja):
  • WD10M (Dirección del viento a 10m): $ p = 8.73 ^{-211} $ (muy no normal)
  • ALLSKY_SFC_SW_DIFF (Radiación solar difusa): $ p = 1.65 ^{-222} $
  • ALLSKY_SRF_ALB (Albedo superficial): $ p = 3.45 ^{-220} $
  • RH2M (Humedad relativa a 2m): $ p = 5.41 ^{-197} $
  • PRECTOTCORR (Precipitación corregida): $ p = 5.14 ^{-188} $
  • ALLSKY_SFC_UV_INDEX (Índice UV): $ p = 2.21 ^{-202} $
  • ALLSKY_KT (Transmisión atmosférica): $ p = 2.50 ^{-179} $

Estas variables muestran una distribución altamente sesgada o multimodal, lo que indica que no siguen una normalidad en absoluto.

Variables de tiempo (año, mes, día, hora)
  • YEAR: $ p = 1.91 ^{-162} $
  • MO: $ p = 8.82 ^{-142} $
  • DY: $ p = 2.42 ^{-134} $
  • HR: $ p = 2.95 ^{-136} $

Estas variables tienen p-valores extremadamente pequeños, indicando que su distribución no es normal (lo cual era esperable porque el tiempo no suele distribuirse normalmente).


Análisis de Variables Categóricas

El test de Chi-cuadrado analiza si la distribución de categorías en “LOCALITY” y “NBHD” es uniforme o tiene patrones significativos.

  • LOCALITY (\chi^2 = 6,142,076, p = 0.0)
    • Indica una distribución altamente no uniforme de las localidades.
  • NBHD (\chi^2 = 4,167,836, p = 0.0)**
    • También muestra que la distribución de barrios no es uniforme en absoluto.

Esto sugiere que algunos barrios/localidades tienen una concentración mucho mayor de registros que otros.


Conclusiones

  • Ninguna variable numérica sigue una distribución normal. Algunas se acercan (T2M, T2MDEW, T2MWET), pero siguen sin cumplir el criterio de normalidad.
  • Muchas variables climáticas tienen distribuciones sesgadas o multimodales (especialmente precipitación, humedad y radiación solar).
  • Las variables categóricas muestran una distribución no uniforme, lo que sugiere que ciertos barrios/localidades tienen más representación en los datos.
Code
# Reemplazar valores NA con la mediana de la variable por localidad
dataclimabq_limits <- dataclimabq_limits %>%
  group_by(LOCALITY) %>%
  mutate(across(where(is.numeric), ~ifelse(is.na(.), median(., na.rm = TRUE), .)))

# Verificar si quedan valores NA
colSums(is.na(dataclimabq_limits))
               YEAR                  MO                  DY                  HR 
                  0                   0                   0                   0 
                T2M         PRECTOTCORR                RH2M               WS10M 
                  0                   0                   0                   0 
              WD10M                  PS ALLSKY_SFC_UV_INDEX      ALLSKY_SRF_ALB 
                  0                   0                   0                   0 
 ALLSKY_SFC_SW_DIFF              T2MDEW              T2MWET               WS50M 
                  0                   0                   0                   0 
          ALLSKY_KT            LOCALITY                NBHD 
                  0                   0                   0 

Análisis visual post limpieza:

Análisis visual de Variables implicadas en el problema de microclimas

Code
df_median <- dataclimabq_limits %>%
  group_by(YEAR, LOCALITY) %>%
  summarise(T2M_median = median(T2M, na.rm = TRUE),
            T2M_sd = sd(T2M, na.rm = TRUE), 
            .groups = "drop")

# Limitar la escala de la desviación estándar para evitar bandas demasiado amplias
df_median <- df_median %>%
  mutate(T2M_sd = ifelse(is.na(T2M_sd), 0, pmin(T2M_sd, 0.5)))  # Evitar NA y controlar valores altos

# Gráfico con mejoras para evitar solapamiento
ggplot(df_median, aes(x = YEAR, y = T2M_median, color = LOCALITY, group = LOCALITY)) +
  geom_line(size = 1) + 
  geom_point(size = 2, position = position_dodge(width = 0.2)) +  # Evitar superposición de puntos
  geom_ribbon(aes(ymin = T2M_median - T2M_sd, 
                  ymax = T2M_median + T2M_sd, 
                  fill = LOCALITY), alpha = 0.15) +
  labs(title = "Cambio de la Mediana de T2M a lo largo del tiempo",
       x = "Año", y = "T2M (Mediana)", color = "Localidad", fill = "Localidad") +
  theme_minimal() +
  theme(legend.position = "top")

Code
df_median_RH2M <- dataclimabq_limits %>%
  group_by(YEAR, LOCALITY) %>%
  summarise(RH2M_median = median(RH2M, na.rm = TRUE),
            RH2M_sd = sd(RH2M, na.rm = TRUE), 
            .groups = "drop")

df_median_RH2M <- df_median_RH2M %>%
  mutate(RH2M_sd = ifelse(is.na(RH2M_sd), 0, pmin(RH2M_sd, 5)))  

ggplot(df_median_RH2M, aes(x = YEAR, y = RH2M_median, color = LOCALITY, group = LOCALITY)) +
  geom_line(size = 1) + 
  geom_point(size = 2, position = position_dodge(width = 0.2)) +  
  geom_ribbon(aes(ymin = RH2M_median - RH2M_sd, 
                  ymax = RH2M_median + RH2M_sd, 
                  fill = LOCALITY), alpha = 0.15) +
  labs(title = "Cambio de la Mediana de RH2M a lo largo del tiempo",
       x = "Año", y = "RH2M (Mediana)", color = "Localidad", fill = "Localidad") +
  theme_minimal() +
  theme(legend.position = "top")

Code
df_median_WS10M <- dataclimabq_limits %>%
  group_by(YEAR, LOCALITY) %>%
  summarise(WS10M_median = median(WS10M, na.rm = TRUE),
            WS10M_sd = sd(WS10M, na.rm = TRUE), 
            .groups = "drop")

df_median_WS10M <- df_median_WS10M %>%
  mutate(WS10M_sd = ifelse(is.na(WS10M_sd), 0, pmin(WS10M_sd, 1)))  

ggplot(df_median_WS10M, aes(x = YEAR, y = WS10M_median, color = LOCALITY, group = LOCALITY)) +
  geom_line(size = 1) + 
  geom_point(size = 2, position = position_dodge(width = 0.2)) +  
  geom_ribbon(aes(ymin = WS10M_median - WS10M_sd, 
                  ymax = WS10M_median + WS10M_sd, 
                  fill = LOCALITY), alpha = 0.15) +
  labs(title = "Cambio de la Mediana de WS10M a lo largo del tiempo",
       x = "Año", y = "WS10M (Mediana)", color = "Localidad", fill = "Localidad") +
  theme_minimal() +
  theme(legend.position = "top")

Code
df_median_WD10M <- dataclimabq_limits %>%
  group_by(YEAR, LOCALITY) %>%
  summarise(WD10M_median = median(WD10M, na.rm = TRUE),
            WD10M_sd = sd(WD10M, na.rm = TRUE), 
            .groups = "drop")

df_median_WD10M <- df_median_WD10M %>%
  mutate(WD10M_sd = ifelse(is.na(WD10M_sd), 0, pmin(WD10M_sd, 10)))  

ggplot(df_median_WD10M, aes(x = YEAR, y = WD10M_median, color = LOCALITY, group = LOCALITY)) +
  geom_line(size = 1) + 
  geom_point(size = 2, position = position_dodge(width = 0.2)) +  
  geom_ribbon(aes(ymin = WD10M_median - WD10M_sd, 
                  ymax = WD10M_median + WD10M_sd, 
                  fill = LOCALITY), alpha = 0.15) +
  labs(title = "Cambio de la Mediana de WD10M a lo largo del tiempo",
       x = "Año", y = "WD10M (Mediana)", color = "Localidad", fill = "Localidad") +
  theme_minimal() +
  theme(legend.position = "top")

Code
df_median_PS <- dataclimabq_limits %>%
  group_by(YEAR, LOCALITY) %>%
  summarise(PS_median = median(PS, na.rm = TRUE),
            PS_sd = sd(PS, na.rm = TRUE), 
            .groups = "drop")

df_median_PS <- df_median_PS %>%
  mutate(PS_sd = ifelse(is.na(PS_sd), 0, pmin(PS_sd, 2)))  

ggplot(df_median_PS, aes(x = YEAR, y = PS_median, color = LOCALITY, group = LOCALITY)) +
  geom_line(size = 1) + 
  geom_point(size = 2, position = position_dodge(width = 0.2)) +  
  geom_ribbon(aes(ymin = PS_median - PS_sd, 
                  ymax = PS_median + PS_sd, 
                  fill = LOCALITY), alpha = 0.15) +
  labs(title = "Cambio de la Mediana de PS a lo largo del tiempo",
       x = "Año", y = "PS (Mediana)", color = "Localidad", fill = "Localidad") +
  theme_minimal() +
  theme(legend.position = "top")

Code
# Boxplots por localidades de las variables clave 
ggplot(dataclimabq_limits, aes(x = LOCALITY, y = T2M)) +
  geom_boxplot(fill = "skyblue") +
  labs(title = "Distribución de T2M por Localidad", x = "Localidad", y = "T2M") +
  theme_minimal()

Code
ggplot(dataclimabq_limits, aes(x = LOCALITY, y = RH2M)) +
  geom_boxplot(fill = "skyblue") +
  labs(title = "Distribución de RH2M por Localidad", x = "Localidad", y = "RH2M") +
  theme_minimal()

Code
ggplot(dataclimabq_limits, aes(x = LOCALITY, y = WS10M)) +
  geom_boxplot(fill = "skyblue") +
  labs(title = "Distribución de WS10M por Localidad", x = "Localidad", y = "WS10M") +
  theme_minimal()

Code
ggplot(dataclimabq_limits, aes(x = LOCALITY, y = WD10M)) +
  geom_boxplot(fill = "skyblue") +
  labs(title = "Distribución de WD10M por Localidad", x = "Localidad", y = "WD10M") +
  theme_minimal()

Code
ggplot(dataclimabq_limits, aes(x = LOCALITY, y = PS)) +
  geom_boxplot(fill = "skyblue") +
  labs(title = "Distribución de PS por Localidad", x = "Localidad", y = "PS") +
  theme_minimal()

Code
library(corrplot)

# Centro
df_centro <- subset(dataclimabq_limits, LOCALITY == "Centro", 
                    select = c("T2M", "RH2M", "WS10M", "WD10M", "PS"))
df_centro[] <- lapply(df_centro, as.numeric)
corr_matrix_centro <- cor(df_centro, use = "pairwise.complete.obs")
corrplot(corr_matrix_centro, method = "color", type = "upper",
         col = colorRampPalette(c("blue", "white", "red"))(200),
         tl.col = "black", tl.cex = 1, number.cex = 0.8, addCoef.col = "black",
         main = "Matriz de Correlación - Centro")

Code
# Sur
df_sur <- subset(dataclimabq_limits, LOCALITY == "Sur", 
                 select = c("T2M", "RH2M", "WS10M", "WD10M", "PS"))
df_sur[] <- lapply(df_sur, as.numeric)
corr_matrix_sur <- cor(df_sur, use = "pairwise.complete.obs")
corrplot(corr_matrix_sur, method = "color", type = "upper",
         col = colorRampPalette(c("blue", "white", "red"))(200),
         tl.col = "black", tl.cex = 1, number.cex = 0.8, addCoef.col = "black",
         main = "Matriz de Correlación - Sur")

Code
# Occidente
df_occidente <- subset(dataclimabq_limits, LOCALITY == "Occidente", 
                       select = c("T2M", "RH2M", "WS10M", "WD10M", "PS"))
df_occidente[] <- lapply(df_occidente, as.numeric)
corr_matrix_occidente <- cor(df_occidente, use = "pairwise.complete.obs")
corrplot(corr_matrix_occidente, method = "color", type = "upper",
         col = colorRampPalette(c("blue", "white", "red"))(200),
         tl.col = "black", tl.cex = 1, number.cex = 0.8, addCoef.col = "black",
         main = "Matriz de Correlación - Occidente")

Code
# Oriente
df_oriente <- subset(dataclimabq_limits, LOCALITY == "Oriente", 
                     select = c("T2M", "RH2M", "WS10M", "WD10M", "PS"))
df_oriente[] <- lapply(df_oriente, as.numeric)
corr_matrix_oriente <- cor(df_oriente, use = "pairwise.complete.obs")
corrplot(corr_matrix_oriente, method = "color", type = "upper",
         col = colorRampPalette(c("blue", "white", "red"))(200),
         tl.col = "black", tl.cex = 1, number.cex = 0.8, addCoef.col = "black",
         main = "Matriz de Correlación - Oriente")

Code
# Norte
df_norte <- subset(dataclimabq_limits, LOCALITY == "Norte", 
                   select = c("T2M", "RH2M", "WS10M", "WD10M", "PS"))
df_norte[] <- lapply(df_norte, as.numeric)
corr_matrix_norte <- cor(df_norte, use = "pairwise.complete.obs")
corrplot(corr_matrix_norte, method = "color", type = "upper",
         col = colorRampPalette(c("blue", "white", "red"))(200),
         tl.col = "black", tl.cex = 1, number.cex = 0.8, addCoef.col = "black",
         main = "Matriz de Correlación - Norte")

Análisis de los cambios de variables meteorológicas para el análisis de microclimas a lo largo del tiempo

El gráfico presenta la evolución de diferentes variables meteorológicas en varias regiones (Centro, Sur, Occidente, Oriente y Norte) a lo largo del tiempo. Se analizan las siguientes variables:

  • T2M (Temperatura a 2 metros)
  • RH2M (Humedad relativa a 2 metros)
  • WS10M (Velocidad del viento a 10 metros)
  • WD10M (Dirección del viento a 10 metros)
  • PS (Presión atmosférica)

T2M (Temperatura a 2 metros)
  • Se observa una disminución de la temperatura entre 2020 y 2022, seguida de un aumento progresivo hasta alcanzar su punto más alto en 2024.
  • En 2025, hay una caída abrupta de la temperatura con una mayor incertidumbre en los valores, especialmente en algunas regiones como el Norte y el Occidente.
  • La variabilidad en los datos proyectados para 2025 sugiere que se deben monitorear con mayor detalle para confirmar si la tendencia descendente persiste.

RH2M (Humedad relativa a 2 metros)
  • La humedad relativa muestra una tendencia estable desde 2020 hasta 2024 en la mayoría de las regiones, con ligeras variaciones en algunos años.
  • En 2025, se evidencia una mayor dispersión, especialmente en el Norte, lo que puede indicar cambios locales en la humedad debido a microclimas particulares.

WS10M (Velocidad del viento a 10 metros)
  • La velocidad del viento presenta variaciones significativas en el tiempo, con picos en 2021 y 2024.
  • Se nota un incremento considerable en 2025, con un rango de variabilidad más amplio entre regiones, lo que podría estar asociado a cambios en los patrones de circulación atmosférica o eventos meteorológicos extremos.
  • Sin embargo, es importante señalar que esta variabilidad podría reducirse con la incorporación de más datos a lo largo del año.

WD10M (Dirección del viento a 10 metros)
  • La dirección del viento sigue un comportamiento oscilatorio, con descensos en 2021 y 2023, y picos en 2022 y 2024.
  • En 2025, la variabilidad es notoria, con diferencias considerables entre las regiones.
  • Este comportamiento sugiere que pueden existir factores locales (topografía, cambios estacionales o patrones climáticos específicos) que afectan la dirección del viento.

PS (Presión atmosférica)
  • La presión atmosférica se mantiene relativamente estable entre 2020 y 2023, con fluctuaciones menores.
  • A partir de 2024, se nota un ligero aumento en la dispersión de los datos, lo que indica posibles cambios en la estabilidad atmosférica.
  • En 2025, se observa un rango más amplio en las mediciones, lo que podría estar relacionado con fenómenos meteorológicos más dinámicos o incertidumbre en las proyecciones.

Histogramas de las Variables Clave

  • Temperatura a 2 metros (T2M): Se observa una distribución sesgada con valores predominantes en un rango específico, indicando posibles puntos de calor urbano.
  • Humedad Relativa (RH2M): La distribución muestra alta variabilidad, lo que sugiere diferentes niveles de humedad en distintas zonas.
  • Velocidad del Viento a 10m (WS10M): Distribución con cola larga, lo que indica eventos de viento extremos en ciertas áreas.

Diagramas de Dispersión y Correlación

  • Relación entre Temperatura y Humedad: Se aprecia una correlación negativa, lo que confirma que en zonas más cálidas la humedad tiende a disminuir.
  • Relación entre Radiación Solar y Temperatura: Existe una fuerte correlación positiva, lo que sugiere que la temperatura es influenciada directamente por la cantidad de energía solar recibida.
Code
# ALLSKY_SFC_UV_INDEX
df_median_UV <- dataclimabq_limits %>%
  group_by(YEAR, LOCALITY) %>%
  summarise(UV_median = median(ALLSKY_SFC_UV_INDEX, na.rm = TRUE),
            UV_sd = sd(ALLSKY_SFC_UV_INDEX, na.rm = TRUE), 
            .groups = "drop")

df_median_UV <- df_median_UV %>%
  mutate(UV_sd = ifelse(is.na(UV_sd), 0, pmin(UV_sd, 0.5)))  

ggplot(df_median_UV, aes(x = YEAR, y = UV_median, color = LOCALITY, group = LOCALITY)) +
  geom_line(size = 1) + 
  geom_point(size = 2, position = position_dodge(width = 0.2)) +  
  geom_ribbon(aes(ymin = UV_median - UV_sd, 
                  ymax = UV_median + UV_sd, 
                  fill = LOCALITY), alpha = 0.15) +
  labs(title = "Cambio de la Mediana de ALLSKY_SFC_UV_INDEX a lo largo del tiempo",
       x = "Año", y = "ALLSKY_SFC_UV_INDEX (Mediana)", color = "Localidad", fill = "Localidad") +
  theme_minimal() +
  theme(legend.position = "top")

Code
# ALLSKY_SFC_SW_DIFF
df_median_SW <- dataclimabq_limits %>%
  group_by(YEAR, LOCALITY) %>%
  summarise(SW_median = median(ALLSKY_SFC_SW_DIFF, na.rm = TRUE),
            SW_sd = sd(ALLSKY_SFC_SW_DIFF, na.rm = TRUE), 
            .groups = "drop")

df_median_SW <- df_median_SW %>%
  mutate(SW_sd = ifelse(is.na(SW_sd), 0, pmin(SW_sd, 10)))  

ggplot(df_median_SW, aes(x = YEAR, y = SW_median, color = LOCALITY, group = LOCALITY)) +
  geom_line(size = 1) + 
  geom_point(size = 2, position = position_dodge(width = 0.2)) +  
  geom_ribbon(aes(ymin = SW_median - SW_sd, 
                  ymax = SW_median + SW_sd, 
                  fill = LOCALITY), alpha = 0.15) +
  labs(title = "Cambio de la Mediana de ALLSKY_SFC_SW_DIFF a lo largo del tiempo",
       x = "Año", y = "ALLSKY_SFC_SW_DIFF (Mediana)", color = "Localidad", fill = "Localidad") +
  theme_minimal() +
  theme(legend.position = "top")

Code
# ALLSKY_SRF_ALB
df_median_ALB <- dataclimabq_limits %>%
  group_by(YEAR, LOCALITY) %>%
  summarise(ALB_median = median(ALLSKY_SRF_ALB, na.rm = TRUE),
            ALB_sd = sd(ALLSKY_SRF_ALB, na.rm = TRUE), 
            .groups = "drop")

df_median_ALB <- df_median_ALB %>%
  mutate(ALB_sd = ifelse(is.na(ALB_sd), 0, pmin(ALB_sd, 0.05)))  

ggplot(df_median_ALB, aes(x = YEAR, y = ALB_median, color = LOCALITY, group = LOCALITY)) +
  geom_line(size = 1) + 
  geom_point(size = 2, position = position_dodge(width = 0.2)) +  
  geom_ribbon(aes(ymin = ALB_median - ALB_sd, 
                  ymax = ALB_median + ALB_sd, 
                  fill = LOCALITY), alpha = 0.15) +
  labs(title = "Cambio de la Mediana de ALLSKY_SRF_ALB a lo largo del tiempo",
       x = "Año", y = "ALLSKY_SRF_ALB (Mediana)", color = "Localidad", fill = "Localidad") +
  theme_minimal() +
  theme(legend.position = "top")

Code
# ALLSKY_KT
df_median_KT <- dataclimabq_limits %>%
  group_by(YEAR, LOCALITY) %>%
  summarise(KT_median = median(ALLSKY_KT, na.rm = TRUE),
            KT_sd = sd(ALLSKY_KT, na.rm = TRUE), 
            .groups = "drop")

df_median_KT <- df_median_KT %>%
  mutate(KT_sd = ifelse(is.na(KT_sd), 0, pmin(KT_sd, 0.1)))  

ggplot(df_median_KT, aes(x = YEAR, y = KT_median, color = LOCALITY, group = LOCALITY)) +
  geom_line(size = 1) + 
  geom_point(size = 2, position = position_dodge(width = 0.2)) +  
  geom_ribbon(aes(ymin = KT_median - KT_sd, 
                  ymax = KT_median + KT_sd, 
                  fill = LOCALITY), alpha = 0.15) +
  labs(title = "Cambio de la Mediana de ALLSKY_KT a lo largo del tiempo",
       x = "Año", y = "ALLSKY_KT (Mediana)", color = "Localidad", fill = "Localidad") +
  theme_minimal() +
  theme(legend.position = "top")

Code
ggplot(dataclimabq_limits, aes(x = LOCALITY, y = ALLSKY_SFC_UV_INDEX)) +
  geom_boxplot(fill = "skyblue") +
  labs(title = "Distribución de ALLSKY_SFC_UV_INDEX por Localidad", x = "Localidad", y = "ALLSKY_SFC_UV_INDEX") +
  theme_minimal()

Code
ggplot(dataclimabq_limits, aes(x = LOCALITY, y = ALLSKY_SFC_SW_DIFF)) +
  geom_boxplot(fill = "skyblue") +
  labs(title = "Distribución de ALLSKY_SFC_SW_DIFF por Localidad", x = "Localidad", y = "ALLSKY_SFC_SW_DIFF") +
  theme_minimal()

Code
ggplot(dataclimabq_limits, aes(x = LOCALITY, y = ALLSKY_SRF_ALB)) +
  geom_boxplot(fill = "skyblue") +
  labs(title = "Distribución de ALLSKY_SRF_ALB por Localidad", x = "Localidad", y = "ALLSKY_SRF_ALB") +
  theme_minimal()

Code
ggplot(dataclimabq_limits, aes(x = LOCALITY, y = ALLSKY_KT)) +
  geom_boxplot(fill = "skyblue") +
  labs(title = "Distribución de ALLSKY_KT por Localidad", x = "Localidad", y = "ALLSKY_KT") +
  theme_minimal()

Code
library(corrplot)

# Centro
df_centro <- subset(dataclimabq_limits, LOCALITY == "Centro", 
                    select = c("ALLSKY_SFC_UV_INDEX", "ALLSKY_SFC_SW_DIFF", "ALLSKY_SRF_ALB", "ALLSKY_KT"))
df_centro[] <- lapply(df_centro, as.numeric)
corr_matrix_centro <- cor(df_centro, use = "pairwise.complete.obs")
corrplot(corr_matrix_centro, method = "color", type = "upper",
         col = colorRampPalette(c("blue", "white", "red"))(200),
         tl.col = "black", tl.cex = 1, number.cex = 0.8, addCoef.col = "black",
         main = "Matriz de Correlación - Centro")

Code
# Sur
df_sur <- subset(dataclimabq_limits, LOCALITY == "Sur", 
                 select = c("ALLSKY_SFC_UV_INDEX", "ALLSKY_SFC_SW_DIFF", "ALLSKY_SRF_ALB", "ALLSKY_KT"))
df_sur[] <- lapply(df_sur, as.numeric)
corr_matrix_sur <- cor(df_sur, use = "pairwise.complete.obs")
corrplot(corr_matrix_sur, method = "color", type = "upper",
         col = colorRampPalette(c("blue", "white", "red"))(200),
         tl.col = "black", tl.cex = 1, number.cex = 0.8, addCoef.col = "black",
         main = "Matriz de Correlación - Sur")

Code
# Occidente
df_occidente <- subset(dataclimabq_limits, LOCALITY == "Occidente", 
                       select = c("ALLSKY_SFC_UV_INDEX", "ALLSKY_SFC_SW_DIFF", "ALLSKY_SRF_ALB", "ALLSKY_KT"))
df_occidente[] <- lapply(df_occidente, as.numeric)
corr_matrix_occidente <- cor(df_occidente, use = "pairwise.complete.obs")
corrplot(corr_matrix_occidente, method = "color", type = "upper",
         col = colorRampPalette(c("blue", "white", "red"))(200),
         tl.col = "black", tl.cex = 1, number.cex = 0.8, addCoef.col = "black",
         main = "Matriz de Correlación - Occidente")

Code
# Oriente
df_oriente <- subset(dataclimabq_limits, LOCALITY == "Oriente", 
                     select = c("ALLSKY_SFC_UV_INDEX", "ALLSKY_SFC_SW_DIFF", "ALLSKY_SRF_ALB", "ALLSKY_KT"))
df_oriente[] <- lapply(df_oriente, as.numeric)
corr_matrix_oriente <- cor(df_oriente, use = "pairwise.complete.obs")
corrplot(corr_matrix_oriente, method = "color", type = "upper",
         col = colorRampPalette(c("blue", "white", "red"))(200),
         tl.col = "black", tl.cex = 1, number.cex = 0.8, addCoef.col = "black",
         main = "Matriz de Correlación - Oriente")

Code
# Norte
df_norte <- subset(dataclimabq_limits, LOCALITY == "Norte", 
                   select = c("ALLSKY_SFC_UV_INDEX", "ALLSKY_SFC_SW_DIFF", "ALLSKY_SRF_ALB", "ALLSKY_KT"))
df_norte[] <- lapply(df_norte, as.numeric)
corr_matrix_norte <- cor(df_norte, use = "pairwise.complete.obs")
corrplot(corr_matrix_norte, method = "color", type = "upper",
         col = colorRampPalette(c("blue", "white", "red"))(200),
         tl.col = "black", tl.cex = 1, number.cex = 0.8, addCoef.col = "black",
         main = "Matriz de Correlación - Norte")

Análisis de variables clave para el aprovechamiento de la energía solar:

El gráfico muestra la evolución de diferentes variables meteorológicas que influyen en la captación y eficiencia de la energía solar en distintas regiones (Centro, Sur, Occidente, Oriente y Norte). Las variables analizadas incluyen:

  • ALLSKY_SFC_UV_INDEX (Índice UV en superficie)
  • ALLSKY_SFC_SW_DIFF (Radiación solar difusa en superficie)
  • ALLSKY_SRF_ALB (Albedo superficial bajo cielo total)
  • ALLSKY_KT (Índice de claridad)

ALLSKY_SFC_UV_INDEX (Índice UV en superficie)

  • Se observa una tendencia estable entre 2020 y 2022, con un ligero descenso en 2022.
  • A partir de 2023, hay un aumento pronunciado, alcanzando su punto más alto en 2024.
  • La región Norte presenta los valores más elevados, lo que sugiere una mayor intensidad de radiación solar en esa zona, factor clave para la eficiencia de paneles solares.

ALLSKY_SFC_SW_DIFF (Radiación solar difusa en superficie)

  • La radiación solar difusa se mantiene constante en la mayoría de las regiones a lo largo del tiempo.
  • Sin embargo, el Norte muestra valores significativamente más bajos, lo que indica una mayor predominancia de radiación directa.
  • Esta información es relevante para decidir entre paneles solares convencionales o tecnologías que aprovechen mejor la radiación difusa, como paneles bifaciales.

ALLSKY_SRF_ALB (Albedo superficial bajo cielo total)

  • Se nota una ligera disminución del albedo entre 2020 y 2023, seguida de un incremento en 2024.
  • La región Norte muestra valores notablemente más altos en comparación con las demás regiones, lo que sugiere una mayor reflectividad del suelo.
  • Un alto albedo puede afectar la eficiencia de los paneles solares al reflejar parte de la radiación en lugar de absorberla.

ALLSKY_KT (Índice de claridad)

  • Se observa una disminución gradual del índice de claridad entre 2020 y 2022, lo que indica un incremento en la nubosidad o aerosoles en la atmósfera.

Histogramas y Boxplots

  • Radiación Solar Total (ALLSKY_SFC_SW_DWN): Presenta una distribución bimodal, lo que indica diferencias significativas entre días soleados y nublados.
  • Albedo Superficial (ALLSKY_SRF_ALB): Se observa una distribución uniforme con valores altos en ciertas áreas, lo que sugiere que algunas superficies reflejan más energía solar de lo esperado.

Mapas de Calor y Correlación

  • Mapa de calor de correlación: Muestra que la radiación solar tiene una alta correlación con la temperatura y una baja correlación con la velocidad del viento.
  • Análisis de Dirección del Viento: Se observan patrones que podrían afectar la eficiencia de paneles solares en distintas localidades.

Concluciones generales

1. Microclimas Urbanos y Variabilidad Climática

  • Se confirma la existencia de islas de calor urbanas, evidenciado por la distribución de temperaturas más altas en zonas densamente pobladas.
  • La humedad relativa muestra alta variabilidad entre localidades, indicando la influencia de la vegetación y cuerpos de agua en el microclima.
  • La correlación negativa entre temperatura y humedad refuerza la hipótesis de que el incremento de temperatura disminuye la capacidad del aire para retener humedad.

2. Factores Clave en el Aprovechamiento de la Energía Solar

  • La radiación solar total presenta patrones bimodales, lo que sugiere diferencias significativas en la disponibilidad de energía solar en función de la nubosidad.
  • El albedo superficial es elevado en ciertas regiones, lo que indica una mayor reflectividad del suelo, factor que puede afectar la absorción de energía solar.
  • El análisis de correlaciones muestra que la radiación solar está fuertemente vinculada a la temperatura y apenas influenciada por la velocidad del viento.

3. Implicaciones Prácticas y Recomendaciones

  • Para mitigar el impacto de islas de calor, se recomienda aumentar la cobertura vegetal y utilizar materiales reflectantes en infraestructuras urbanas.
  • En términos de aprovechamiento solar, se deben considerar tecnologías de paneles adaptadas a las condiciones locales, priorizando zonas con mayor radiación solar directa.
  • La planificación energética debe incluir un análisis detallado de patrones de nubosidad y albedo, pues influyen directamente en la eficiencia de los sistemas fotovoltaicos.

Análisis individual de la temperatura promedio por general y por localidad:

Code
# Calcular la temperatura promedio por año
avg_temp_per_year <- dataclimabq_limits %>%
  group_by(YEAR) %>%
  summarise(T2M_mean = mean(T2M, na.rm = TRUE))

# Graficar la temperatura promedio por año
ggplot(avg_temp_per_year, aes(x = YEAR, y = T2M_mean)) +
  geom_line(color = "blue", size = 1) +
  geom_point(color = "red", size = 2) +
  labs(title = "Temperatura Promedio por Año",
       x = "Año",
       y = "Temperatura Promedio (°C)") +
  theme_minimal()

Code
# Calcular la temperatura promedio anual por localidad
df_avg_temp <- dataclimabq_limits %>%
  group_by(YEAR, LOCALITY) %>%
  summarise(T2M_mean = mean(T2M, na.rm = TRUE), .groups = "drop")

# Filtrar solo las primeras 6 localidades en caso de que haya más
localidades_seleccionadas <- unique(df_avg_temp$LOCALITY)[1:6]

# Filtrar el dataset con las localidades seleccionadas
df_avg_temp <- df_avg_temp %>%
  filter(LOCALITY %in% localidades_seleccionadas)

# Graficar la temperatura promedio por localidad con facetas
ggplot(df_avg_temp, aes(x = YEAR, y = T2M_mean, color = LOCALITY, group = LOCALITY)) +
  geom_line(size = 1) +
  geom_point(size = 2) +  # Solo los puntos del promedio anual
  facet_wrap(~ LOCALITY, scales = "free_y") +  # Divide en subgráficos por localidad
  labs(title = "Cambio de Temperatura Promedio Anual por Localidad",
       x = "Año",
       y = "Temperatura Promedio (°C)") +
  theme_minimal() +
  theme(legend.position = "none", strip.text = element_text(size = 12))  # Oculta la leyenda ya que está en los títulos de los facetas

Análisis de Temperatura a lo Largo del Tiempo

Podemos observar que se muestra un patrón similar en los diferentes barrios, lo cual se debe a que se promedia la temperatura total por año. Hay varios puntos interesantes a destacar:

  • A partir del 2020, la temperatura comenzó a decrecer de manera constante hasta alcanzar un mínimo histórico dentro del rango de tiempo analizado.
  • Sin embargo, desde el 2022, la temperatura experimenta un aumento exponencial hasta el 2024, alcanzando su punto más alto.
  • Posteriormente, los valores vuelven a niveles similares a los registrados en 2020, lo que sugiere una posible estabilización o un ciclo climático recurrente.

Ya en el grafico que promedia la temperatura total entre todas las localidades por año se ve una tendencia de similar pero los valores son más altos.