Personalización, correlación y covarianza

Módulo: Análisis Exploratorio de Datos
Docente: Sebastian Moreno Rodríguez

En esta entrega integro dos fuentes de la Gran Encuesta Integrada de Hogares GEIH 2025 (DANE): Características Generales (CG) y Ocupados (OC). Objetivo: unir, preparar y explorar variables clave (edad, sexo, ingreso, horas trabajadas y meses trabajados), reportando descriptivos, covarianzas/correlaciones y gráficos con un análisis breve por visualización.


1. Instalación paquetes, librerias y organización datos

#====================== 1. INSTALO PAQUETES CARGO LIBRERIAS  ===========

install.packages("dplyr")       # Pauete de manipulacion de datos 
install.packages("ggplot2")    # Liberia para usar gráficos 
install.packages("e1071")      # skewness y kurtosis (Fisher-Pearson)
install.packages("DescTools")  # Skew(), Kurt(), BowleySkew()
install.packages("readr")     #importar/exportar CSV

library(dplyr)
library(ggplot2)
library(e1071)
library(DescTools)
library(readr)
library(patchwork) # USAR FRAMES PARA GRAFICOS
# *****************ARCHIVOS DANE*************
#>Gran Encuesta Integrada de Hogares - GEIH - 2025 - 
#>1. archivo de datos: Caracteristicas generales, seguridad social en salud y educacion
#>2. archivo de datos: 

CG <- read_delim("CG.CSV",
                 delim = ";", locale = locale(encoding = "Latin1"))

OC <- read_delim("OC.CSV",
                 delim = ";", locale = locale(encoding = "Latin1"))

# Revisar estructura
#glimpse(CG)
#glimpse(OC)

# ************************** ORGANIZAR DF***********


# De Características Generales
CG <- CG %>%
  select(DIRECTORIO, SECUENCIA_P, ORDEN,
         sexo = P3271,     # Sexo al nacer (1=Hombre, 2=Mujer)
         edad = P6040)     # Edad en años cumplidos

# De Ocupados
OC <- OC %>%
  select(DIRECTORIO, SECUENCIA_P, ORDEN,
         ingreso = P6500,          # Ingreso mensual antes de descuentos
         HoraHabitual = P6800, # Horas semanales habituales
         HoraSemana = P6850,     # Horas efectivas semana pasada
         HoraExtra = P6640S1,    # Horas extra semana pasada
         MesesTrab = P6790,       # Meses trabajados últimos 12 meses
         peso = FEX_C18)           # Factor de expansión 
#> Este paso es clave aca uno los dos archivos, pero solo quedan los datos que están en ambas bases Por eso base queda con 
#> 30155 registros que son los de Ocupados. 
DF_final <- inner_join(CG, OC, by = c("DIRECTORIO", "SECUENCIA_P", "ORDEN"))

# Diagnóstico del join
cat("Filas CG:", nrow(CG), "| Filas OC:", nrow(OC), "| Filas unidas (DF_final):", nrow(DF_final), "\n")
## Filas CG: 68577 | Filas OC: 30155 | Filas unidas (DF_final): 30155
head(DF_final)
## # A tibble: 6 × 11
##   DIRECTORIO SECUENCIA_P ORDEN  sexo  edad ingreso HoraHabitual HoraSemana
##        <dbl>       <dbl> <dbl> <dbl> <dbl>   <dbl>        <dbl>      <dbl>
## 1    8316823           1     1     1    64      NA           40         40
## 2    8316824           1     1     1    62 1423500           47         47
## 3    8316824           1     2     2    50 1423500           47         47
## 4    8316825           1     1     1    30 1423500           48         50
## 5    8316825           1     3     1    29      NA           40         40
## 6    8316825           1     4     2    55  400000           60         60
## # ℹ 3 more variables: HoraExtra <dbl>, MesesTrab <dbl>, peso <dbl>

2. Estadísticos Descriptivos de la base de datos

#> ====================================== PASO 1. ORGANIZACION DATOS DF ============

DF_final <- DF_final %>%
  mutate(
    edad = parse_number(as.character(edad), locale = locale(decimal_mark = ",", grouping_mark = ".")),
    ingreso = parse_number(as.character(ingreso), locale = locale(decimal_mark = ",", grouping_mark = ".")),
    HoraHabitual= parse_number(as.character(HoraHabitual), locale = locale(decimal_mark = ",", grouping_mark = ".")),
    HoraSemana = parse_number(as.character(HoraSemana), locale = locale(decimal_mark = ",", grouping_mark = ".")),
    HoraExtra = parse_number(as.character(HoraExtra), locale = locale(decimal_mark = ",", grouping_mark = ".")),
    MesesTrab = parse_number(as.character(MesesTrab), locale = locale(decimal_mark = ",", grouping_mark = "."))
  )
# Visualizo
names(DF_final)
##  [1] "DIRECTORIO"   "SECUENCIA_P"  "ORDEN"        "sexo"         "edad"        
##  [6] "ingreso"      "HoraHabitual" "HoraSemana"   "HoraExtra"    "MesesTrab"   
## [11] "peso"
dplyr::glimpse(DF_final)
## Rows: 30,155
## Columns: 11
## $ DIRECTORIO   <dbl> 8316823, 8316824, 8316824, 8316825, 8316825, 8316825, 831…
## $ SECUENCIA_P  <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
## $ ORDEN        <dbl> 1, 1, 2, 1, 3, 4, 1, 1, 1, 2, 1, 2, 1, 1, 2, 1, 2, 2, 1, …
## $ sexo         <dbl> 1, 1, 2, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 1, 1, 2, 1, 1, 2, …
## $ edad         <dbl> 64, 62, 50, 30, 29, 55, 44, 48, 64, 63, 32, 31, 44, 50, 3…
## $ ingreso      <dbl> NA, 1423500, 1423500, 1423500, NA, 400000, NA, 2500000, N…
## $ HoraHabitual <dbl> 40, 47, 47, 48, 40, 60, 60, 60, 24, 40, 16, 56, 47, 36, 4…
## $ HoraSemana   <dbl> 40, 47, 47, 50, 40, 60, 60, 60, 24, 40, 16, 56, 47, 36, 4…
## $ HoraExtra    <dbl> NA, NA, NA, 2, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
## $ MesesTrab    <dbl> 12, 12, 12, 12, 2, 12, 12, 12, 12, 12, 7, 12, 12, 12, 12,…
## $ peso         <dbl> 21.33878, 21.31727, 21.31727, 39.31131, 39.31131, 39.3113…
# CAMBIAR ETIQUETA HOMBR EY MUUJER 

DF_final <- DF_final %>%
  mutate(
    sexo = case_when(
      sexo == 1 ~ "Hombre",
      sexo == 2 ~ "Mujer",
      TRUE ~ NA_character_
    ),
    sexo = factor(sexo, levels = c("Hombre", "Mujer"))
  )




# *****************************************. DESCRIPTIVOS ******************
#Guardo en vars_num la selección de las variables que les voy a sacar descriptivos 
vars_num <- DF_final %>% select(edad, ingreso, HoraHabitual, HoraSemana, HoraExtra, MesesTrab)


# ********************* Estadisticos a todas las variables 

# Función simple: centralidad, dispersión y forma
 analizar <- function(x, nom) {
  x <- as.numeric(x)
  x <- x[!is.na(x)]
  data.frame(
    
    # LOCALIZACIÓN 
    Variable   = nom,
    n          = length(x),
    Media      = mean(x),
    Mediana    = median(x),
    Moda       = as.numeric(names(which.max(table(x)))),   # moda simple y rápida
    
    
    #CUARTILES 
    
    Q1         = quantile(x, 0.25, na.rm=TRUE),
    Q2         = quantile(x, 0.50, na.rm=TRUE),
    Q3         = quantile(x, 0.75, na.rm=TRUE),
    
    #DISPERSIÓN
    Varianza   = var(x),
    Desv_Est   = sd(x),
    Rango      = max(x) - min(x),
    IQR        = IQR(x),
    CV         = ifelse(mean(x) == 0, NA_real_, sd(x) / mean(x) * 100),
    
    #FORMA 
    Asimetria  = e1071::skewness(x, type = 2),             # Fisher–Pearson
    Curtosis   = e1071::kurtosis(x, type = 2),             # exceso de curtosis (normal = 0)
    row.names  = NULL
  )
}
# **************************** Muestro la tabla 
TablaEstadisticos <- Map(analizar, vars_num, names(vars_num))
EstadisticosDF <- bind_rows(TablaEstadisticos)
EstadisticosDFinal <- EstadisticosDF %>%     mutate(across(-c(Variable, n), ~ sprintf("%.3f", as.numeric(.))))
#EstadisticosDFinal 

#View(EstadisticosDFinal) 
knitr::kable (EstadisticosDFinal )
Variable n Media Mediana Moda Q1 Q2 Q3 Varianza Desv_Est Rango IQR CV Asimetria Curtosis
edad 30155 41.831 40.000 30.000 30.000 40.000 52.000 201.624 14.199 80.000 22.000 33.945 0.370 -0.617
ingreso 15893 2788119.883 1423500.000 1423500.000 1400000.000 1423500.000 2000000.000 161015309324169.781 12689180.798 470000000.000 600000.000 455.116 17.428 370.787
HoraHabitual 30155 43.279 44.000 48.000 40.000 44.000 48.000 167.925 12.959 129.000 8.000 29.942 -0.083 2.421
HoraSemana 30155 42.149 44.000 48.000 40.000 44.000 48.000 200.181 14.149 130.000 8.000 33.568 -0.325 2.049
HoraExtra 414 10.118 8.000 4.000 4.000 8.000 12.000 95.867 9.791 98.000 8.000 96.767 4.628 34.048
MesesTrab 30155 11.213 12.000 12.000 12.000 12.000 12.000 5.049 2.247 11.000 0.000 20.039 -3.120 9.055

3. Correlación y covarianza

#> ======================================= PASO 2.  CORRELACIONES Y COVARIANZAS ==============

# **************** COVARIANZAS ****************

VarCovCo <- DF_final %>%
  select(edad, ingreso, HoraHabitual, HoraSemana, HoraExtra, MesesTrab)
#>"complete.obs" --> Solo usa las filas donde todas las variables tienen datos validos

covarianza <- cov(VarCovCo, use = "complete.obs") 
round(covarianza, 3) 
##                      edad       ingreso HoraHabitual  HoraSemana   HoraExtra
## edad              144.110  1.275715e+07       -4.021       4.164      18.632
## ingreso      12757148.552  3.538006e+13 -4047976.655 -384738.771 2786141.178
## HoraHabitual       -4.021 -4.047977e+06       68.458      62.502      21.850
## HoraSemana          4.164 -3.847388e+05       62.502     104.046      42.829
## HoraExtra          18.632  2.786141e+06       21.850      42.829      96.089
## MesesTrab           1.741  2.287089e+05        0.554       0.501      -0.165
##               MesesTrab
## edad              1.741
## ingreso      228708.945
## HoraHabitual      0.554
## HoraSemana        0.501
## HoraExtra        -0.165
## MesesTrab         2.083
# ******************* CORELLACIONES **************

cor_pearson  <- cor(vars_num, method = "pearson",  use = "complete.obs")
cor_spearman <- cor(vars_num, method = "spearman", use = "complete.obs")

round(cor_pearson, 3)
##                edad ingreso HoraHabitual HoraSemana HoraExtra MesesTrab
## edad          1.000   0.179       -0.040      0.034     0.158     0.100
## ingreso       0.179   1.000       -0.082     -0.006     0.048     0.027
## HoraHabitual -0.040  -0.082        1.000      0.741     0.269     0.046
## HoraSemana    0.034  -0.006        0.741      1.000     0.428     0.034
## HoraExtra     0.158   0.048        0.269      0.428     1.000    -0.012
## MesesTrab     0.100   0.027        0.046      0.034    -0.012     1.000
round(cor_spearman, 3)
##                edad ingreso HoraHabitual HoraSemana HoraExtra MesesTrab
## edad          1.000   0.257       -0.044      0.001     0.130     0.163
## ingreso       0.257   1.000       -0.126     -0.027     0.052     0.122
## HoraHabitual -0.044  -0.126        1.000      0.599     0.257     0.054
## HoraSemana    0.001  -0.027        0.599      1.000     0.573     0.073
## HoraExtra     0.130   0.052        0.257      0.573     1.000     0.044
## MesesTrab     0.163   0.122        0.054      0.073     0.044     1.000

4. Gráficos

4.1 Mapa Correlaciones

El mapa de correlaciones (coeficiente de Pearson) permite observar la intensidad y dirección de las relaciones lineales entre variables numéricas. Los tonos verdes indican relaciones positivas, mientras que los rojos indican negativas. En la matriz se aprecia una alta correlación positiva entre horas habituales y horas efectivas trabajadas, lo cual es esperable porque representan medidas relacionadas de la jornada laboral. Por el contrario, la relación entre edad y horas extras tiende a ser baja o incluso negativa, lo que sugiere que las personas mayores podrían realizar menos horas adicionales. Este gráfico es útil para identificar patrones de asociación y posibles redundancias entre variables.

library(ggcorrplot)
library(GGally)
library(ggplot2)
library(scales)

# PREPARACION COR MATRIX_ 

CorMatriz<- cor(vars_num, method = "pearson", use = "complete.obs")

#> 1. Mapa Correlaciones
ggcorrplot(CorMatriz,
           hc.order = TRUE,           # agrupa variables similares
           type = "lower",            # muestra mitad inferior
           lab = TRUE,                # muestra valores numéricos
           lab_size = 3,
           colors = c("red", "white", "green"),
           title = "Mapa de correlaciones (Pearson) - GEIH 2025",
           ggtheme = ggplot2::theme_minimal())

4.1.a Edad - ingreso

El gráfico de dispersión con línea de tendencia OLS (mínimos cuadrados ordinarios) muestra una relación positiva moderada entre edad e ingreso. Esto sugiere que, en promedio, los ingresos aumentan con la edad, probablemente debido a la experiencia laboral acumulada o la antigüedad en los puestos. Sin embargo, la nube de puntos es amplia, lo que refleja una alta variabilidad: existen individuos jóvenes con ingresos altos y adultos con ingresos bajos.

#> 2. Relación  edad–ingreso


ggplot(DF_final, aes(x = edad, y = ingreso)) +
  geom_point(alpha = 0.3, color = "#1F78B4") +
  geom_smooth(method = "lm", color = "#E31A1C", se = FALSE) +
  scale_y_continuous(labels = comma) +
  labs(title = "Relación entre edad e ingreso - GEIH 2025",
       subtitle = "Tendencia lineal y dispersión",
       x = "Edad (años cumplidos)",
       y = "Ingreso mensual (COP)") +
  theme_minimal()

## 4.1.b Horas Habituales y semana

El gráfico de hexágonos (“heatmap”) evidencia una concentración diagonal, lo que significa que las horas efectivas trabajadas son cercanas a las habituales para la mayoría. Los hexágonos más oscuros indican la zona de mayor densidad, donde el número de horas planeadas coincide con las realmente trabajadas. La dispersión hacia arriba o abajo de esa diagonal indica variaciones o incumplimiento de jornadas, posiblemente por informalidad o flexibilidad laboral.

#> 3. Horas Habituales y semana

ggplot(DF_final, aes(x = HoraHabitual, y = HoraSemana)) +
  geom_hex(bins = 25) +
  scale_fill_viridis_c() +
  labs(title = "Concentración de horas trabajadas - GEIH 2025",
       subtitle = "Distribución conjunta entre horas habituales y efectivas",
       x = "Horas habituales por semana",
       y = "Horas efectivas la semana pasada",
       fill = "Frecuencia") +
  theme_minimal()

4.2.a Densidad univariada ingreso

La densidad del ingreso revela una distribución altamente asimétrica a la derecha, donde la mayoría de los valores se concentran en los tramos bajos, mientras que un pequeño grupo presenta ingresos muy altos (cola larga). Este comportamiento es típico de variables económicas con desigualdad en la distribución de riqueza.

library(scales)
# 3.1 SIN FILTRO
ggplot(DF_final, aes(x = ingreso)) +
  geom_density(fill = "pink", alpha = 0.5) +
  scale_x_continuous(labels = scales::comma)+ 
  labs(title = "Distribución del ingreso mensual - GEIH 2025",
       subtitle = "Alta asimetría y presencia de valores extremos",
       x = "Ingreso mensual (COP)",
       y = "Densidad") +
  theme_minimal()

4.2.b Ingreso filtrado (entre $100.000 y $20.000.000 COP)

Al aplicar el filtro, la forma de la curva se hace más legible, eliminando los extremos. Se distingue un modo principal alrededor de los ingresos medios-bajos, que representan la mayor parte de la población ocupada. Este gráfico permite analizar el comportamiento central sin distorsión por valores extremos.

# FIltrado 
DF_filtrado <- DF_final %>%
  filter(ingreso >= 100000 & ingreso <= 20000000)
ggplot(DF_filtrado, aes(x = ingreso)) +
  geom_density(fill = "orange", alpha = 0.5) +
  scale_x_continuous(labels = scales::comma) +
  labs(title = "Distribución del ingreso mensual (filtrado) - GEIH 2025",
       subtitle = "Ingresos entre $100.000 y $20.000.000 COP",
       x = "Ingreso mensual (COP)",
       y = "Densidad") +
  theme_minimal()

4.3 Densidad con escala de logaritmo

El uso de la escala logarítmica en el eje X reduce la asimetría y comprime los valores altos, permitiendo visualizar mejor la concentración de los ingresos bajos. Se observa que la mayoría de la población percibe ingresos en un rango reducido respecto al total. Esta transformación es útil para representar variables con diferencias de magnitud amplias y para comparaciones entre grupos.

# genero otro grafico por que no permite uuna visualizacion adecuada

ggplot(DF_final, aes(x = ingreso)) +
  geom_density(fill = "aquamarine", alpha = 0.5) +
  scale_x_log10(labels = scales::comma) + # cambio la escala
  labs(title = "Distribución del ingreso mensual (escala logarítmica) - GEIH 2025",
       subtitle = "La mayoría de los ingresos se concentran en valores bajos",
       x = "Ingreso mensual (COP, escala log10)",
       y = "Densidad") +
  theme_minimal()

4.4 Ingreso Mensual por genero

El boxplot compara la distribución de ingresos entre hombres y mujeres. Se aprecia que la mediana del ingreso es más alta para los hombres, y la dispersión (IQR) también es mayor, lo cual sugiere mayor heterogeneidad salarial en ese grupo. Además, la presencia de outliers en ambos casos refleja individuos con ingresos muy por encima del promedio, lo que puede asociarse a diferencias sectoriales o jerárquicas en el mercado laboral. Este resultado es coherente con la evidencia de brecha salarial de género observada en encuestas laborales.

# ******************************. POR GENERO ***********************

library(dplyr)
library(ggplot2)
library(scales)

# =====================Boxplot de ingreso por sexo

DF_filtrado <- DF_final %>%
  filter(!is.na(sexo),
         ingreso >= 100000, ingreso <= 10000000)

ggplot(DF_filtrado, aes(x = sexo, y = ingreso, fill = sexo)) +
  geom_boxplot(alpha = 0.6, outlier.colour = "red", outlier.alpha = 0.4) +
  scale_y_continuous(labels = comma) +
  labs(title = "Ingreso mensual por sexo (filtrado)",
       subtitle = "Corte entre $100.000 y $10.000.000 COP para mejorar legibilidad",
       x = "Sexo", y = "Ingreso mensual (COP)") +
  theme_minimal(base_size = 13) +
  theme(legend.position = "none")

4.5 Distribución ingreso por sexo

Las curvas de densidad permiten visualizar la forma de la distribución para cada grupo. Se observa un desplazamiento hacia la derecha en la curva de los hombres, indicando que en general perciben ingresos más altos. El solapamiento parcial sugiere que, aunque existen diferencias, también hay zonas de coincidencia en los ingresos medios. Este análisis permite visualizar la desigualdad de manera más intuitiva que los estadísticos descriptivos

# =========================Densidades de ingreso por sexo

ggplot(DF_filtrado, aes(x = ingreso, fill = sexo)) +
  geom_density(alpha = 0.45) +
  scale_fill_manual(values = c("Hombre" = "cyan", "Mujer" = "pink"))+
  scale_x_continuous(labels = comma) +
  labs(title = "Distribución del ingreso por sexo",
       subtitle = "Se observa desplazamiento de la densidad entre grupos",
       x = "Ingreso mensual (COP)", y = "Densidad", fill = "Sexo") +
  theme_minimal(base_size = 13)

4.6 Dispersión edad–ingreso por sexo

Al separar por sexo, las rectas de tendencia muestran que tanto hombres como mujeres presentan una relación positiva entre edad e ingreso, aunque con pendientes distintas. La curva de los hombres tiende a ubicarse por encima, lo cual refuerza la hipótesis de mayor remuneración promedio. Sin embargo, la dispersión dentro de cada grupo evidencia una gran heterogeneidad: factores como tipo de ocupación, nivel educativo o región podrían explicar las diferencias individuales.

#====================== Dispersión edad–ingreso por sexo + rectas de tendencia
  
  
  ggplot(DF_filtrado, aes(x = edad, y = ingreso, color = sexo)) +
    geom_point(alpha = 0.25, size = 1) +
    geom_smooth(method = "lm", se = FALSE, size = 1) +
    scale_y_continuous(labels = comma) +
    labs(title = "Relación entre edad e ingreso por sexo",
         subtitle = "Rectas OLS separadas por grupo",
         x = "Edad (años)", y = "Ingreso mensual (COP)", color = "Sexo") +
    theme_minimal(base_size = 13)

4.7 Violin + boxplot (Ingreso por sexo)

El gráfico de violín combina la forma de la distribución (ancho del violín) con los estadísticos resumidos (boxplot interno). Se observa que la distribución del ingreso de los hombres tiene una cola más larga hacia valores altos, mientras que la de las mujeres es más compacta y concentrada. En escala logarítmica, esta visualización resalta que los ingresos femeninos tienden a concentrarse en los tramos bajos-medios, mientras que los masculinos se extienden más en los rangos altos. Este tipo de representación es ideal para comparar forma y dispersión de manera simultánea.

# ============================ Violin + boxplot (Ingreso por sexo)
  
  ggplot(DF_final %>% filter(!is.na(sexo), ingreso > 0),
         aes(x = sexo, y = ingreso, fill = sexo)) +
    geom_violin(alpha = 0.35, trim = TRUE) +
    geom_boxplot(width = 0.15, outlier.alpha = 0.25) +
    scale_y_log10(labels = comma) +
    labs(title = "Ingreso por sexo (escala log10)",
         subtitle = "Violin + box: forma y mediana con todos los datos",
         x = "Sexo", y = "Ingreso mensual (COP, log10)") +
    theme_minimal(base_size = 13) +
    theme(legend.position = "none")

4.8 Correlaciones separadas poro Sexo

La tabla de correlaciones separadas por sexo resume cómo se relacionan las variables principales en cada grupo.

En ambos sexos, la relación entre edad e ingreso es positiva, aunque ligeramente más fuerte en los hombres, indicando que el efecto de la edad sobre el salario podría ser mayor en ellos.

La correlación entre horas habituales y efectivas es alta y similar en ambos grupos, lo que sugiere consistencia en las jornadas laborales.

Finalmente, la relación entre horas extras y horas semanales también es positiva, pero más débil, lo cual indica que las horas adicionales no siempre se traducen en mayores horas totales, posiblemente por compensaciones o límites normativos.

# ============================= Correlaciones separadas por sexo
  
  cor_por_sexo <- DF_final %>%
    filter(!is.na(sexo)) %>%
    group_by(sexo) %>%
    summarise(
      r_edad_ingreso = cor(edad, ingreso, use = "complete.obs", method = "pearson"),
      r_horas        = cor(HoraHabitual, HoraSemana, use = "complete.obs", method = "pearson"),
      r_extra_sem    = cor(HoraExtra, HoraSemana, use = "complete.obs", method = "pearson"),
      .groups = "drop"
    )
  
  cor_por_sexo %>%
    dplyr::mutate(dplyr::across(where(is.numeric), ~ round(., 3))) %>%
    knitr::kable(caption = "Correlaciones clave separadas por sexo (Pearson)")
Correlaciones clave separadas por sexo (Pearson)
sexo r_edad_ingreso r_horas r_extra_sem
Hombre 0.064 0.884 0.435
Mujer 0.046 0.910 0.369

5 Referencias (APA)

Referencias

Bruce, P., Bruce, A., & Gedeck, P. (2022). Estadística práctica para ciencia de datos con R y Python (2ª ed.). Marcombo.

DataScience Latinoamérica. (2023, 28 de marzo). Mapa de correlaciones en R – ggcorrplot y GGally [Video]. YouTube. https://www.youtube.com/watch?v=sSn5_wYOJrY

Departamento Administrativo Nacional de Estadística – DANE. (2025). Gran Encuesta Integrada de Hogares (GEIH) – Características generales, seguridad social en salud y educación: Diccionario de datos, Formulario F1. Microdatos DANE. https://microdatos.dane.gov.co/index.php/catalog/853/data-dictionary/F1?file_name=Caracteristicas%20generales,%20seguridad%20social%20en%20salud%20y%20educacion

Estadística con R. (2023, 12 de mayo). Correlación y gráficos en R paso a paso [Video]. YouTube. https://www.youtube.com/watch?v=iZBpLucydh4

R-charts. (s. f.). Gráficos de correlación en R. R-charts. Recuperado el 2 de noviembre de 2025, de https://r-charts.com/es/correlacion/

Wickham, H., Çetinkaya-Rundel, M., & Grolemund, G. (2023). R for data science: Import, tidy, transform, visualize, and model data (2nd ed.). O’Reilly Media. https://r4ds.hadley.nz