3  Transformación y limpieza de datos

NoteResultados de aprendizaje
  • R2. Importar y manipular bases de datos en R.

En la práctica, los datos casi nunca llegan listos para analizar. Este capítulo cubre las funciones del tidyverse para crear variables, clasificar, unir tablas y reordenarlas. Usaremos datos del Censo 2024 agregados a nivel de manzana (INE), que combinan población, hogares y viviendas.

library(tidyverse)
manzanas_pob <- read_csv("data/manzanas_pob_biobio.csv")
manzanas_hog <- read_csv("data/manzanas_hog_biobio.csv")
manzanas_viv <- read_csv("data/manzanas_viv_biobio.csv")

glimpse(manzanas_pob)
Rows: 18,647
Columns: 108
$ CONTENEDOR_COMUNAL                  <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
$ COD_REGION                          <dbl> 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8…
$ REGION                              <chr> "BIOBÍO", "BIOBÍO", "BIOBÍO", "BIO…
$ PROVINCIA                           <chr> "CONCEPCIÓN", "CONCEPCIÓN", "CONCE…
$ CUT                                 <dbl> 8101, 8101, 8101, 8101, 8101, 8101…
$ COMUNA                              <chr> "CONCEPCIÓN", "CONCEPCIÓN", "CONCE…
$ AREA_C                              <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
$ MANZENT                             <dbl> 8.101011e+12, 8.101011e+12, 8.1010…
$ DISTRITO                            <chr> "INTENDENCIA", "INTENDENCIA", "INT…
$ COD_DISTRITO                        <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
$ COD_LOCALIDAD                       <dbl> 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7…
$ COD_ZONA                            <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
$ LOCALIDAD                           <chr> "GRAN CONCEPCIÓN", "GRAN CONCEPCIÓ…
$ COD_ENTIDAD                         <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
$ COD_MANZANA                         <dbl> 1, 2, 3, 4, 5, 6, 7, 10, 11, 13, 1…
$ ENTIDAD                             <chr> "CONCEPCIÓN", "CONCEPCIÓN", "CONCE…
$ TIPO_MZ                             <chr> "URBANO", "URBANO", "URBANO", "URB…
$ COD_CATEGORIA                       <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
$ CATEGORIA                           <chr> "Ciudad", "Ciudad", "Ciudad", "Ciu…
$ ID_ENTIDAD                          <dbl> 8.10101e+16, 8.10101e+16, 8.10101e…
$ ID_LOCALIDAD                        <dbl> 81011007, 81011007, 81011007, 8101…
$ ID_DISTRITO                         <dbl> 810101, 810101, 810101, 810101, 81…
$ ID_ZONA                             <dbl> 810101001, 810101001, 810101001, 8…
$ n_per                               <dbl> 19, 71, 203, 43, 133, 68, 103, 118…
$ n_hombres                           <dbl> 15, 24, 91, 19, 60, 29, 51, 49, 22…
$ n_mujeres                           <dbl> 4, 47, 112, 24, 73, 39, 52, 69, 38…
$ n_edad_0_5                          <dbl> 1, 4, 4, 5, 3, 0, 2, 3, 1, 1, 0, 0…
$ n_edad_6_13                         <dbl> 0, 2, 12, 1, 4, 2, 6, 6, 3, 12, 0,…
$ n_edad_14_17                        <dbl> 0, 0, 8, 1, 5, 4, 5, 3, 1, 4, 0, 0…
$ n_edad_18_24                        <dbl> 1, 8, 23, 4, 21, 9, 13, 26, 9, 12,…
$ n_edad_25_44                        <dbl> 6, 34, 66, 17, 39, 28, 40, 38, 27,…
$ n_edad_45_59                        <dbl> 6, 5, 32, 11, 24, 12, 15, 17, 5, 1…
$ n_edad_60_mas                       <dbl> 5, 18, 58, 4, 37, 13, 22, 25, 14, …
$ prom_edad                           <dbl> 488, 409, 441, 358, 433, 408, 403,…
$ n_inmigrantes                       <dbl> 4, 5, 22, 5, 8, 17, 20, 7, 14, 12,…
$ n_nacionalidad                      <dbl> 4, NA, 17, 13, 8, 16, 19, 7, 14, 1…
$ n_pueblos_orig                      <dbl> NA, 4, 13, NA, 15, 5, 4, 4, NA, 9,…
$ n_afrodescendencia                  <dbl> 0, 0, 0, 0, 0, 0, NA, 0, 0, NA, 0,…
$ n_lengua_indigena                   <dbl> NA, NA, 4, NA, NA, NA, NA, 4, 0, N…
$ n_religion                          <dbl> 10, 29, 110, 28, 86, 34, 59, 64, 3…
$ n_dificultad_ver                    <dbl> 0, 0, 13, 0, 8, 6, 4, 3, 3, 3, 0, …
$ n_dificultad_oir                    <dbl> 0, 0, 7, 0, 2, 1, 3, 3, 4, 1, 1, 1…
$ n_dificultad_mover                  <dbl> 2, 0, 9, 1, 4, 5, 2, 3, 2, 2, 2, 0…
$ n_dificultad_cogni                  <dbl> 2, 0, 7, 1, 3, 4, 2, 6, 2, 8, 1, 0…
$ n_dificultad_cuidado                <dbl> 2, 1, 5, 0, 3, 0, 0, 3, 0, 2, 1, 0…
$ n_dificultad_comunic                <dbl> 3, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0…
$ n_discapacidad                      <dbl> 3, 1, 29, 2, 12, 11, 11, 11, 9, 11…
$ n_estcivcon_casado                  <dbl> 6, 8, 33, 11, 23, 8, 23, 24, 9, 9,…
$ n_estcivcon_conviviente             <dbl> 4, 14, 42, 4, 28, 6, 12, 14, 12, 1…
$ n_estcivcon_conv_civil              <dbl> 0, 2, 2, 4, 0, 0, 0, 0, 0, 0, 0, 0…
$ n_estcivcon_anul_sep_div            <dbl> 1, 4, 15, 1, 14, 8, 12, 9, 4, 14, …
$ n_estcivcon_viudo                   <dbl> 0, 7, 12, 2, 4, 1, 5, 5, 1, 4, 0, …
$ n_estcivcon_soltero                 <dbl> 7, 30, 82, 15, 56, 40, 42, 57, 30,…
$ prom_escolaridad18                  <dbl> 144, 144, 152, 131, 151, 144, 146,…
$ n_asistencia_parv                   <dbl> 0, 4, 3, 4, 1, 0, 2, 1, 0, 1, 0, 0…
$ n_asistencia_basica                 <dbl> 0, 2, 12, 0, 3, 2, 5, 5, 3, 11, 0,…
$ n_asistencia_media                  <dbl> 0, 0, 8, 1, 4, 3, 3, 3, 1, 4, 0, 0…
$ n_asistencia_superior               <dbl> 0, 3, 17, 3, 19, 8, 9, 20, 6, 9, 2…
$ n_cine_nunca_curso_primera_infancia <dbl> 1, 4, 5, 5, 3, 1, 2, 3, 1, 2, 0, 0…
$ n_cine_primaria                     <dbl> 2, 14, 35, 7, 13, 12, 24, 18, 7, 1…
$ n_cine_secundaria                   <dbl> 9, 10, 60, 21, 59, 24, 35, 46, 23,…
$ n_cine_terciaria_maestria_doctorado <dbl> 7, 43, 103, 10, 57, 31, 42, 51, 29…
$ n_cine_especial_diferencial         <dbl> 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0…
$ n_analfabet                         <dbl> 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0…
$ n_ocupado                           <dbl> 13, 44, 110, 23, 69, 35, 53, 53, 3…
$ n_desocupado                        <dbl> 0, 7, 5, 6, 4, 6, 6, 7, 2, 4, 0, 0…
$ n_fuera_fuerza_trabajo              <dbl> 5, 14, 71, 8, 52, 22, 35, 49, 19, …
$ n_cise_rec_independientes           <dbl> 4, 42, 36, 23, 35, 17, 25, 43, 10,…
$ n_cise_rec_dependientes             <dbl> 8, 1, 74, 0, 34, 17, 27, 10, 25, 3…
$ n_cise_rec_trabajador_no_remunerado <dbl> 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0…
$ n_ciuo_1                            <dbl> 2, 5, 12, 4, 8, 4, 3, 7, 4, 7, 0, …
$ n_ciuo_2                            <dbl> 3, 21, 51, 3, 24, 10, 16, 19, 8, 2…
$ n_ciuo_3                            <dbl> 1, 6, 11, 0, 10, 3, 4, 6, 5, 8, 0,…
$ n_ciuo_4                            <dbl> 0, 1, 4, 0, 1, 4, 6, 3, 2, 3, 0, 0…
$ n_ciuo_5                            <dbl> 5, 7, 16, 11, 12, 11, 18, 12, 12, …
$ n_ciuo_6                            <dbl> 0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0…
$ n_ciuo_7                            <dbl> 0, 0, 3, 0, 6, 2, 2, 1, 3, 2, 0, 3…
$ n_ciuo_8                            <dbl> 0, 1, 1, 0, 3, 0, 1, 0, 0, 2, 0, 0…
$ n_ciuo_9                            <dbl> 2, 0, 3, 0, 4, 0, 3, 1, 0, 2, 0, 0…
$ n_ciuo_0                            <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
$ n_caenes_A                          <dbl> 0, 0, 1, 0, 1, 1, 0, 3, 0, 0, 0, 0…
$ n_caenes_B                          <dbl> 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0…
$ n_caenes_C                          <dbl> 0, 0, 3, 0, 7, 2, 6, 0, 2, 1, 0, 0…
$ n_caenes_D                          <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
$ n_caenes_E                          <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
$ n_caenes_F                          <dbl> 1, 0, 1, 0, 3, 1, 0, 0, 0, 1, 0, 0…
$ n_caenes_G                          <dbl> 5, 7, 14, 13, 8, 9, 14, 7, 12, 6, …
$ n_caenes_H                          <dbl> 0, 1, 5, 0, 1, 2, 2, 0, 0, 3, 0, 0…
$ n_caenes_I                          <dbl> 4, 2, 5, 0, 4, 5, 3, 4, 6, 11, 0, …
$ n_caenes_J                          <dbl> 0, 0, 6, 0, 2, 1, 1, 2, 1, 1, 0, 0…
$ n_caenes_K                          <dbl> 0, 1, 1, 0, 0, 0, 0, 0, 2, 1, 0, 0…
$ n_caenes_L                          <dbl> 0, 0, 3, 0, 1, 0, 1, 1, 2, 2, 0, 0…
$ n_caenes_M                          <dbl> 0, 5, 12, 2, 9, 1, 9, 7, 1, 12, 0,…
$ n_caenes_N                          <dbl> 0, 1, 3, 0, 1, 0, 0, 2, 1, 0, 0, 0…
$ n_caenes_O                          <dbl> 0, 2, 5, 0, 0, 1, 1, 5, 1, 2, 0, 0…
$ n_caenes_P                          <dbl> 3, 9, 19, 1, 10, 2, 4, 3, 2, 10, 2…
$ n_caenes_Q                          <dbl> 0, 5, 11, 0, 9, 5, 3, 4, 3, 6, 0, …
$ n_caenes_R                          <dbl> 0, 1, 3, 0, 0, 1, 1, 0, 0, 2, 0, 0…
$ n_caenes_S                          <dbl> 0, 0, 3, 0, 3, 3, 1, 2, 1, 2, 0, 1…
$ n_caenes_T                          <dbl> 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 0…
$ n_caenes_U                          <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
$ n_transporte_auto                   <dbl> 3, 0, 16, 0, 5, 1, 7, 5, 3, 9, 0, …
$ n_transporte_publico                <dbl> 2, 1, 22, 1, 25, 16, 6, 12, 5, 11,…
$ n_transporte_camina                 <dbl> 1, 0, 39, 0, 30, 9, 29, 31, 22, 31…
$ n_transporte_bicicleta              <dbl> 0, 0, 8, 0, 3, 2, 2, 0, 0, 4, 0, 1…
$ n_transporte_motocicleta            <dbl> 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0…
$ n_transporte_cab_lan_bote           <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
$ n_transporte_otros                  <dbl> 0, 0, 4, 0, 0, 1, 1, 1, 0, 0, 1, 0…
Note

Estas tablas corresponden al Censo 2024, agregado a nivel de manzana, para la Región del Biobío. Comparten la llave MANZENT, que identifica cada manzana.

3.1 Crear variables con mutate()

mutate() agrega columnas nuevas sin eliminar las existentes. Puedes crear varias de una vez:

manzanas_pob <- manzanas_pob %>%
  mutate(
    pct_mujeres         = (n_mujeres / n_per) * 100,
    pct_adultos_mayores = (n_edad_60_mas / n_per) * 100,
    pct_edad_activa     = ((n_edad_25_44 + n_edad_45_59) / n_per) * 100,
    pct_inmigrantes     = (n_inmigrantes / n_per) * 100
  )

3.2 Operadores lógicos

Los operadores lógicos devuelven TRUE/FALSE y pueden usarse dentro de mutate():

manzanas_pob <- manzanas_pob %>%
  mutate(
    zona_urbana = AREA_C == 1,
    zona_rural  = AREA_C == 2,
    # %in% comprueba pertenencia a un conjunto de valores
    region_sur  = COD_REGION %in% c(9, 10, 14, 11, 12)
  )

El operador de subconjunto [ ] permite resumir solo una parte de los datos sin filtrar antes:

manzanas_pob %>%
  summarise(
    prom_edad_urbano = mean(prom_edad[AREA_C == 1], na.rm = TRUE),
    prom_edad_rural  = mean(prom_edad[AREA_C == 2], na.rm = TRUE)
  )
Tipna.rm = TRUE

Casi todas las funciones de resumen (mean, sum, max…) devuelven NA si encuentran un solo valor faltante. Con na.rm = TRUE les indicamos que ignoren los NA antes de calcular.

3.3 Clasificar con if_else() y case_when()

if_else() elige entre dos valores; case_when() generaliza a varias condiciones, evaluadas en orden:

manzanas_pob <- manzanas_pob %>%
  mutate(
    tipo_zona = if_else(AREA_C == 1, "Urbano", "Rural"),
    nivel_envejecimiento = case_when(
      pct_adultos_mayores > 20  ~ "Alto",
      pct_adultos_mayores >= 10 ~ "Medio",
      pct_adultos_mayores < 10  ~ "Bajo",
      .default = NA_character_
    )
  )

3.4 Unir tablas con left_join()

Para combinar tablas necesitamos una llave: una columna en común que identifique cada fila de forma única. Antes de unir, conviene verificarla:

# ¿Qué columnas comparten?
intersect(names(manzanas_pob), names(manzanas_hog))
[1] "MANZENT"
# ¿Es MANZENT una llave única? (el resultado debe estar vacío)
manzanas_pob %>% count(MANZENT) %>% filter(n > 1)

left_join() conserva todas las filas de la tabla izquierda y agrega las columnas de la derecha cuando hay coincidencia:

manzanas_completo <- manzanas_pob %>%
  left_join(manzanas_hog, by = "MANZENT") %>%
  left_join(manzanas_viv, by = "MANZENT")

Una vez unidas, group_by() + summarise() permite calcular indicadores por grupo (por ejemplo, por región):

manzanas_completo %>%
  group_by(COD_REGION) %>%
  summarise(
    prom_per_hog  = mean(prom_per_hog, na.rm = TRUE),
    pct_hacinadas = mean(n_viv_hacinadas > 0, na.rm = TRUE) * 100,
    prom_escol    = mean(prom_escolaridad18, na.rm = TRUE)
  ) %>%
  arrange(desc(pct_hacinadas))

3.5 Pivotear: formato ancho y largo

A veces los datos están en formato ancho (una columna por categoría) y necesitamos el formato largo (una fila por categoría), o viceversa.

pivot_longer() va de ancho a largo:

edad_largo <- manzanas_pob %>%
  select(MANZENT, REGION, AREA_C, starts_with("n_edad_")) %>%
  pivot_longer(
    cols      = starts_with("n_edad_"),
    names_to  = "grupo_edad",
    values_to = "n_personas"
  ) %>%
  mutate(grupo_edad = str_remove(grupo_edad, "n_edad_"))

pivot_wider() hace lo inverso:

resumen_edad <- edad_largo %>%
  group_by(REGION, grupo_edad) %>%
  summarise(total = sum(n_personas, na.rm = TRUE), .groups = "drop") %>%
  pivot_wider(names_from = grupo_edad, values_from = total)

Ejercicios

ImportantEjercicio 3.1 — Perfil territorial de una región

En grupos de 2-3 personas, construyan un perfil territorial de la región que elijan:

  1. Filtren las tres bases por una región (COD_REGION).
  2. Únanlas con left_join().
  3. Creen variables de clasificación con mutate() + case_when() (envejecimiento, tipo de zona, % inmigrantes).
  4. Generen una tabla resumen por comuna con group_by() + summarise().
  5. Construyan un índice simple de vulnerabilidad sumando dimensiones críticas (% adultos mayores + % inmigrantes + % viviendas hacinadas).
  6. Usen pivot_wider() para comparar el índice entre zona urbana y rural.