Examen de Manipulación y visualización de datos en R

La base cuenta con las variables: folioviv, foliohog, numrem, FVH_numrem, Entidad_cod, Entidad,Zona, parentesco, sexo_cod, edad, ingresos provenientes de las bases población e ingreso. (Puedeconsultar el catalogo para entender qué hace y es cada variable).

1.Base de datos y librerías

library(readxl)
datos_enigh_examen <- read_excel("C:/Users/alani/Downloads/datos_enigh_examen.xlsx")
View(datos_enigh_examen)

library(tidyverse)
## Warning: package 'tidyverse' was built under R version 4.5.1
## Warning: package 'ggplot2' was built under R version 4.5.2
## Warning: package 'tibble' was built under R version 4.5.1
## Warning: package 'tidyr' was built under R version 4.5.2
## Warning: package 'readr' was built under R version 4.5.2
## Warning: package 'purrr' was built under R version 4.5.2
## Warning: package 'dplyr' was built under R version 4.5.1
## Warning: package 'forcats' was built under R version 4.5.2
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.1     ✔ stringr   1.6.0
## ✔ ggplot2   4.0.0     ✔ tibble    3.3.0
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ✔ purrr     1.1.0     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(ggplot2)
library(dplyr)

Ver la structura de datos

str(datos_enigh_examen)
## tibble [194,510 × 11] (S3: tbl_df/tbl/data.frame)
##  $ folioviv   : num [1:194510] 1e+08 1e+08 1e+08 1e+08 1e+08 ...
##  $ foliohog   : num [1:194510] 1 1 1 1 1 1 1 1 1 1 ...
##  $ numren     : num [1:194510] 1 2 1 3 1 2 1 1 1 1 ...
##  $ FVH_numrem : chr [1:194510] "10001360511" "10001360512" "10001360611" "10001360613" ...
##  $ Entidad_cod: num [1:194510] 1 1 1 1 1 1 1 1 1 1 ...
##  $ Entidad    : chr [1:194510] "Aguascalientes" "Aguascalientes" "Aguascalientes" "Aguascalientes" ...
##  $ Zona       : chr [1:194510] "Centro" "Centro" "Centro" "Centro" ...
##  $ parentesco : num [1:194510] 101 301 101 301 101 201 101 101 101 101 ...
##  $ sexo_cod   : chr [1:194510] "Mujer" "Mujer" "Hombre" "Hombre" ...
##  $ edad       : num [1:194510] 48 17 46 17 26 26 29 63 33 60 ...
##  $ ingreso    : num [1:194510] 6295 5410 21639 1574 23607 ...
glimpse(datos_enigh_examen)
## Rows: 194,510
## Columns: 11
## $ folioviv    <dbl> 100013605, 100013605, 100013606, 100013606, 100017801, 100…
## $ foliohog    <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
## $ numren      <dbl> 1, 2, 1, 3, 1, 2, 1, 1, 1, 1, 3, 1, 2, 1, 2, 1, 3, 1, 2, 4…
## $ FVH_numrem  <chr> "10001360511", "10001360512", "10001360611", "10001360613"…
## $ Entidad_cod <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
## $ Entidad     <chr> "Aguascalientes", "Aguascalientes", "Aguascalientes", "Agu…
## $ Zona        <chr> "Centro", "Centro", "Centro", "Centro", "Centro", "Centro"…
## $ parentesco  <dbl> 101, 301, 101, 301, 101, 201, 101, 101, 101, 101, 301, 101…
## $ sexo_cod    <chr> "Mujer", "Mujer", "Hombre", "Hombre", "Hombre", "Mujer", "…
## $ edad        <dbl> 48, 17, 46, 17, 26, 26, 29, 63, 33, 60, 30, 76, 73, 74, 68…
## $ ingreso     <dbl> 6295.08, 5409.83, 21639.34, 1573.77, 23606.55, 983.60, 678…
class(datos_enigh_examen)
## [1] "tbl_df"     "tbl"        "data.frame"
head(datos_enigh_examen)
## # A tibble: 6 × 11
##    folioviv foliohog numren FVH_numrem  Entidad_cod Entidad     Zona  parentesco
##       <dbl>    <dbl>  <dbl> <chr>             <dbl> <chr>       <chr>      <dbl>
## 1 100013605        1      1 10001360511           1 Aguascalie… Cent…        101
## 2 100013605        1      2 10001360512           1 Aguascalie… Cent…        301
## 3 100013606        1      1 10001360611           1 Aguascalie… Cent…        101
## 4 100013606        1      3 10001360613           1 Aguascalie… Cent…        301
## 5 100017801        1      1 10001780111           1 Aguascalie… Cent…        101
## 6 100017801        1      2 10001780112           1 Aguascalie… Cent…        201
## # ℹ 3 more variables: sexo_cod <chr>, edad <dbl>, ingreso <dbl>
tail(datos_enigh_examen)
## # A tibble: 6 × 11
##     folioviv foliohog numren FVH_numrem   Entidad_cod Entidad   Zona  parentesco
##        <dbl>    <dbl>  <dbl> <chr>              <dbl> <chr>     <chr>      <dbl>
## 1 3260770717        1      1 326077071711          32 Zacatecas Cent…        101
## 2 3260770717        1      3 326077071713          32 Zacatecas Cent…        301
## 3 3260770717        1      4 326077071714          32 Zacatecas Cent…        301
## 4 3260770718        1      1 326077071811          32 Zacatecas Cent…        101
## 5 3260770718        1      2 326077071812          32 Zacatecas Cent…        301
## 6 3260770718        1      3 326077071813          32 Zacatecas Cent…        301
## # ℹ 3 more variables: sexo_cod <chr>, edad <dbl>, ingreso <dbl>

2.Realice un análisis descriptivo de la variable ingreso trimestral.

summary(datos_enigh_examen$ingreso)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
## 5.000e-01 2.374e+03 8.609e+03 1.378e+04 1.790e+04 1.069e+07

Ahora calculamos las medidas de tendencia central y dispersión

ingreso_stats <- datos_enigh_examen %>%
  summarise(
    Observaciones = n(),
    Media = mean(ingreso),
    Mediana = median(ingreso),
    Desviacion_Estandar = sd(ingreso),
    Minimo = min(ingreso),
    Maximo = max(ingreso),
    Q1 = quantile(ingreso, 0.25),
    Q3 = quantile(ingreso, 0.75),
    IQR = IQR(ingreso),
    CV = (sd(ingreso) / mean(ingreso)) * 100
  )
print(ingreso_stats)
## # A tibble: 1 × 10
##   Observaciones  Media Mediana Desviacion_Estandar Minimo    Maximo    Q1     Q3
##           <int>  <dbl>   <dbl>               <dbl>  <dbl>     <dbl> <dbl>  <dbl>
## 1        194510 13785.   8609.              35525.   0.48 10688918. 2374. 17902.
## # ℹ 2 more variables: IQR <dbl>, CV <dbl>

Se presenta un histograma me permite visualizar la forma de la distribución para ver si es simétrica o sesgada. Y complementa las medidas anteriores.

ggplot(datos_enigh_examen, aes(x = ingreso)) +
  geom_histogram(bins = 50, fill = "steelblue", color = "white", alpha = 0.7) +
  labs(title = "Distribución del Ingreso Trimestral - ENIGH",
       subtitle = paste("N =", format(nrow(datos_enigh_examen), big.mark = ","), "observaciones"),
       x = "Ingreso Trimestral",
       y = "Frecuencia") +
  theme_minimal()

Ahora se presenta un boxplot me ayuda a identificar valores atípicos y entender la dispersión a través de los cuartiles. Muestra claramente la mediana y la caja representa el 50% central de los datos:

ggplot(datos_enigh_examen, aes(y = ingreso)) +
  geom_boxplot(fill = "lightblue", alpha = 0.7) +
  labs(title = "Boxplot del Ingreso Trimestral",
       y = "Ingreso Trimestral") +
  theme_minimal() +
  scale_y_continuous(labels = scales::comma)

Use el siguiente filtro específico porque quiero analizar la población con ingresos medios-bajos. El primer cuartil representa el 25% más pobre, y al tomar valores mayores a este pero limitando a 30,000, me enfoco en la clase media baja:

Q1 <- quantile(datos_enigh_examen$ingreso, 0.25)
cat("\nPrimer cuartil (Q1):", Q1, "\n")
## 
## Primer cuartil (Q1): 2373.62
datos_filtrados <- datos_enigh_examen %>%
  filter(ingreso > Q1 & ingreso <= 30000)

4.Puede realizar una selección (SELECT) de sus variables con respecto al análisis que va a realizar, asícomo utilizar group_by o cualquier otro verbo que hayamos visto en clase o tengan en sus diapositivas.

A continuación, seleccioné estas variables porque son relevantes para analizar diferencias demográficas y socioeconómicas. Transformé la edad en grupos categóricos para facilitar el análisis. También categoricé el parentesco para entender mejor la estructura familiar. Estas transformaciones hacen los datos más interpretables:

analisis_completo <- datos_filtrados %>%
  select(folioviv, foliohog, numren, FVH_numrem, Entidad_cod, Entidad, Zona, 
         parentesco, sexo_cod, edad, ingreso) %>%
  mutate(
    sexo = sexo_cod,
    
    grupo_edad = case_when(
      edad < 18 ~ "Menor 18",
      edad < 25 ~ "18-24",
      edad < 35 ~ "25-34",
      edad < 45 ~ "35-44",
      edad < 55 ~ "45-54",
      edad < 65 ~ "55-64",
      TRUE ~ "65+"
    ),
    
    tipo_parentesco = case_when(
      parentesco == 101 ~ "Jefe/a de hogar",
      parentesco == 201 ~ "Cónyuge/Pareja",
      parentesco == 301 ~ "Hijo/a",
      parentesco == 401 ~ "Yerno/nuera",
      parentesco == 501 ~ "Nieto/a",
      parentesco == 601 ~ "Padre/madre",
      parentesco == 701 ~ "Suegro/a",
      parentesco == 801 ~ "Hermano/a",
      parentesco == 901 ~ "Cuñado/a",
      parentesco >= 1000 ~ "Otro familiar/No familiar",
      TRUE ~ "Otro"
    ),
    
    zona_simplificada = case_when(
      Zona == "Centro" ~ "Urbana",
      Zona == "Rural" ~ "Rural",
      TRUE ~ Zona  
    )
  )

Y ahora las rectifico:

table(analisis_completo$tipo_parentesco)
## 
##  Cónyuge/Pareja          Hijo/a Jefe/a de hogar         Nieto/a            Otro 
##           21595           32829           57976             640           12185 
##     Padre/madre 
##            1535
table(analisis_completo$zona_simplificada)
## 
##  Norte    Sur Urbana 
##  42673  24943  59144
table(analisis_completo$grupo_edad)
## 
##    18-24    25-34    35-44    45-54    55-64      65+ Menor 18 
##    18037    25271    22957    20371    13880    18681     7563

5.Utilice las métricas, visualizaciones y comandos que considere necesarios y útiles.

  1. Análisis por Entidad (Top 10 y Bottom 10) Analicé por entidad para identificar disparidades regionales. La mediana es más robusta a valores extremos que la media, por eso calculé ambas.
ingreso_entidad <- analisis_completo %>%
  group_by(Entidad) %>%
  summarise(
    n = n(),
    ingreso_promedio = mean(ingreso),
    ingreso_mediano = median(ingreso),
    desviacion = sd(ingreso),
    error_estandar = desviacion / sqrt(n)
  ) %>%
  arrange(desc(ingreso_promedio))

print(ingreso_entidad, n = 32)
## # A tibble: 32 × 6
##    Entidad          n ingreso_promedio ingreso_mediano desviacion error_estandar
##    <chr>        <int>            <dbl>           <dbl>      <dbl>          <dbl>
##  1 Baja Califo…  5668           14978.          15344.      7904.          105. 
##  2 Nuevo León    4874           14232.          14087.      7953.          114. 
##  3 Coahuila      5913           13976.          13891.      7636.           99.3
##  4 Jalisco       4181           13880.          13279.      7563.          117. 
##  5 Chihuahua     6445           13799.          14087.      7560.           94.2
##  6 Aguascalien…  4326           13545.          13055.      7575.          115. 
##  7 Querétaro     5442           13503.          12984.      7542.          102. 
##  8 Baja Califo…  3873           13475.          12892.      7863.          126. 
##  9 CDMX          4105           13298.          11868.      7638.          119. 
## 10 Colima        5005           13028.          11868.      7687.          109. 
## 11 Sinaloa       5110           12933.          11739.      7792.          109. 
## 12 Sonora        3334           12907.          11803.      7506.          130. 
## 13 Tamaulipas    3101           12721.          11803.      7303.          131. 
## 14 Guanajuato    5186           12619.          11803.      7215.          100. 
## 15 México        5223           12396.          11739.      7332.          101. 
## 16 Durango       4355           12221.          11318.      7286.          110. 
## 17 Michoacán     2926           12128.          10681.      7454.          138. 
## 18 Nayarit       2869           12080.          10623.      7479.          140. 
## 19 San Luis Po…  3522           11801.          10328.      7511.          127. 
## 20 Morelos       3551           11624.          10467.      7200.          121. 
## 21 Quintana Roo  2868           11575.          10027.      7477.          140. 
## 22 Campeche      3061           11459.           9890.      7353.          133. 
## 23 Zacatecas     3456           11455.          10328.      7265.          124. 
## 24 Hidalgo       3187           11393.           9978.      7278.          129. 
## 25 Tabasco       2620           11346.           9709.      7304.          143. 
## 26 Tlaxcala      3247           11239.          10174.      6954.          122. 
## 27 Puebla        2918           10850.           9391.      6970.          129. 
## 28 Yucatán       3705           10775.           9391.      6838.          112. 
## 29 Oaxaca        3067           10370.           8100.      7400.          134. 
## 30 Veracruz      3434           10238.           8790.      6697.          114. 
## 31 Guerrero      3245           10100.           8071.      7059.          124. 
## 32 Chiapas       2943            9316.           7337.      6559.          121.

Gráfico de ingresos por Entidad (Top 15) Este análisis cruzado me permite identificar brechas de género en diferentes contextos geográficos (urbano/rural), lo que puede revelar patrones de desigualdad.

top_entidades <- ingreso_entidad %>%
  arrange(desc(ingreso_promedio)) %>%
  head(15)

ggplot(top_entidades, aes(x = reorder(Entidad, ingreso_promedio), y = ingreso_promedio)) +
  geom_col(fill = "skyblue") +
  geom_errorbar(aes(ymin = ingreso_promedio - error_estandar,
                    ymax = ingreso_promedio + error_estandar),
                width = 0.2) +
  coord_flip() +
  labs(title = "Top 15 Entidades por Ingreso Promedio Trimestral",
       subtitle = "Filtro: Q1 < ingreso ≤ 30,000",
       x = "Entidad",
       y = "Ingreso Promedio") +
  theme_minimal()

B) Análisis por Zona y Sexo

ingreso_zona_sexo <- analisis_completo %>%
  group_by(zona_simplificada, sexo) %>%
  summarise(
    n = n(),
    ingreso_promedio = mean(ingreso),
    ingreso_mediano = median(ingreso),
    desviacion = sd(ingreso),
    error_estandar = desviacion / sqrt(n)
  ) %>%
  arrange(zona_simplificada, desc(ingreso_promedio))
## `summarise()` has grouped output by 'zona_simplificada'. You can override using
## the `.groups` argument.
print(ingreso_zona_sexo)
## # A tibble: 6 × 7
## # Groups:   zona_simplificada [3]
##   zona_simplificada sexo       n ingreso_promedio ingreso_mediano desviacion
##   <chr>             <chr>  <int>            <dbl>           <dbl>      <dbl>
## 1 Norte             Hombre 24196           14820.          14754.      7692.
## 2 Norte             Mujer  18477           11973.          10623.      7433.
## 3 Sur               Hombre 14671           11392.           9836.      7122.
## 4 Sur               Mujer  10272            9535.           7239.      6953.
## 5 Urbana            Hombre 33620           13657.          13207.      7450.
## 6 Urbana            Mujer  25524           10890.           8951.      7163.
## # ℹ 1 more variable: error_estandar <dbl>

Gráfico de ingresos por Zona y Sexo:

ggplot(ingreso_zona_sexo, aes(x = zona_simplificada, y = ingreso_promedio, fill = sexo)) +
  geom_col(position = "dodge") +
  geom_errorbar(aes(ymin = ingreso_promedio - error_estandar,
                    ymax = ingreso_promedio + error_estandar),
                position = position_dodge(0.9), width = 0.2) +
  labs(title = "Ingreso Promedio por Zona y Sexo",
       x = "Zona",
       y = "Ingreso Promedio",
       fill = "Sexo") +
  theme_minimal()

C) Análisis por Grupo de Edad:

ingreso_edad <- analisis_completo %>%
  group_by(grupo_edad) %>%
  summarise(
    n = n(),
    ingreso_promedio = mean(ingreso),
    ingreso_mediano = median(ingreso),
    desviacion = sd(ingreso)
  ) %>%
  mutate(grupo_edad = factor(grupo_edad, 
                             levels = c("Menor 18", "18-24", "25-34", "35-44", "45-54", "55-64", "65+")))

print(ingreso_edad)
## # A tibble: 7 × 5
##   grupo_edad     n ingreso_promedio ingreso_mediano desviacion
##   <fct>      <int>            <dbl>           <dbl>      <dbl>
## 1 18-24      18037           11987.          11739.      6779.
## 2 25-34      25271           14558.          14234.      7520.
## 3 35-44      22957           14512.          14164.      7561.
## 4 45-54      20371           13875.          13292.      7588.
## 5 55-64      13880           12510.          11262.      7466.
## 6 65+        18681            8479.           6457.      6280.
## 7 Menor 18    7563            6623.           4721.      4647.

Gráfico de ingresos por grupo de edad:

ggplot(ingreso_edad, aes(x = grupo_edad, y = ingreso_promedio)) +
  geom_col(fill = "lightgreen") +
  labs(title = "Ingreso Promedio por Grupo de Edad",
       x = "Grupo de Edad",
       y = "Ingreso Promedio") +
  theme_minimal()

D)Análisis por Tipo de Parentesco:

ingreso_parentesco <- analisis_completo %>%
  group_by(tipo_parentesco) %>%
  summarise(
    n = n(),
    ingreso_promedio = mean(ingreso),
    ingreso_mediano = median(ingreso)
  ) %>%
  arrange(desc(ingreso_promedio))

print(ingreso_parentesco)
## # A tibble: 6 × 4
##   tipo_parentesco     n ingreso_promedio ingreso_mediano
##   <chr>           <int>            <dbl>           <dbl>
## 1 Jefe/a de hogar 57976           13523.          12717.
## 2 Nieto/a           640           12977.          11803.
## 3 Hijo/a          32829           12114.          11446.
## 4 Cónyuge/Pareja  21595           11391.           9664.
## 5 Otro            12185           11063.           9391.
## 6 Padre/madre      1535            7305.           5283.

Gráfico de ingresos por parentesco:

ggplot(ingreso_parentesco, aes(x = reorder(tipo_parentesco, ingreso_promedio), y = ingreso_promedio)) +
  geom_col(fill = "orange", alpha = 0.7) +
  coord_flip() +
  labs(title = "Ingreso Promedio por Tipo de Parentesco",
       x = "Tipo de Parentesco",
       y = "Ingreso Promedio") +
  theme_minimal()

6. Análisis adicional (Correlación entre edad e ingreso):

correlacion <- cor(analisis_completo$edad, analisis_completo$ingreso, use = "complete.obs")

Gráfico de dispersión edad vs ingreso:

ggplot(analisis_completo, aes(x = edad, y = ingreso)) +
  geom_point(alpha = 0.1, color = "blue") +
  geom_smooth(method = "lm", color = "red") +
  labs(title = "Relación entre Edad e Ingreso Trimestral",
       subtitle = paste("Correlación:", round(correlacion, 3), "| N =", format(nrow(analisis_completo), big.mark = ",")),
       x = "Edad",
       y = "Ingreso Trimestral") +
  theme_minimal()
## `geom_smooth()` using formula = 'y ~ x'