Práctico 2: Manipulación de Datos con dplyr: Respondiendo Preguntas con datos sociales

Trabajando con la Encuesta CASEN 2024

Autor/a

Gabriel Sotomayor

Fecha de publicación

21 septiembre 2026

0. Objetivos del Práctico

El objetivo de este práctico es aprender a manipular y transformar datos en R utilizando la “gramática” del paquete dplyr. Al finalizar, serás capaz de:

  • Seleccionar y filtrar datos usando tanto R base como dplyr, comprendiendo sus diferencias.
  • Entender y aplicar el operador pipe (%>%) para crear flujos de trabajo legibles.
  • Utilizar los cinco verbos principales de dplyr (select, filter, arrange, mutate, summarise).
  • Responder preguntas de investigación simples mediante la combinación de group_by() y summarise().

1. Preparación del Entorno

1.1 Proyecto de RStudio y estructura de carpetas

Siempre trabaja dentro de un Proyecto de RStudio (.Rproj). Un proyecto establece automáticamente el directorio de trabajo, lo que permite usar rutas relativas ("datos/archivo.sav") en lugar de rutas absolutas ("C:/Users/tu_nombre/Desktop/..."). El código funciona en cualquier computador sin modificaciones.

Si aún no tienes un proyecto creado para este curso:

  1. En RStudio, ve a File → New Project → New Directory → New Project
  2. Asígnale un nombre descriptivo (por ejemplo, practicos-dyv) y elige una ubicación
  3. Dentro del proyecto, crea una carpeta llamada datos donde guardarás las bases de datos

1.2 Cargar Paquetes

Los paquetes se instalan una vez con install.packages(), pero se cargan en cada sesión con library().

library(haven)
library(tidyverse)

1.3 Cargar la Base de Datos

Descarga la base de datos CASEN 2024 desde el sitio del Observatorio Social del Ministerio de Desarrollo Social y Familia y guárdala en tu carpeta datos. Luego cárgala con una ruta relativa:

casen <- haven::read_sav("datos/Base de datos Casen 2024 SPSS.sav")

Crearemos una submuestra con las variables que usaremos hoy:

casen_sub <- casen[, c("folio", "pco1", "region", "sexo", "edad", "esc", "ytotcorh")]

2. R Base vs. dplyr: Un Contraste

Antes de sumergirnos en dplyr, comparemos cómo se realizan dos tareas básicas en R base para apreciar las ventajas de la nueva gramática.

2.1 Seleccionar Variables

Objetivo: Crear un data.frame solo con edad, sexo y esc.

Con R Base:

casen_base_select <- casen_sub[, c("edad", "sexo", "esc")]
head(casen_base_select)
## # A tibble: 6 × 3
##    edad sexo            esc
##   <dbl> <dbl+lbl>     <dbl>
## 1    72 2 [2. Mujer]      1
## 2    67 1 [1. Hombre]     4
## 3    40 2 [2. Mujer]     15
## 4    56 1 [1. Hombre]    NA
## 5    25 2 [2. Mujer]     12
## 6     2 1 [1. Hombre]    NA

Usamos la notación de corchetes [filas, columnas]. Dejamos el espacio de las filas en blanco para indicar que queremos todas las filas, y le pasamos un vector de nombres de columnas c("edad", "sexo", "esc") para seleccionar solo esas variables.

Con dplyr:

casen_dplyr_select <- select(casen_sub, edad, sexo, esc)
head(casen_dplyr_select)
## # A tibble: 6 × 3
##    edad sexo            esc
##   <dbl> <dbl+lbl>     <dbl>
## 1    72 2 [2. Mujer]      1
## 2    67 1 [1. Hombre]     4
## 3    40 2 [2. Mujer]     15
## 4    56 1 [1. Hombre]    NA
## 5    25 2 [2. Mujer]     12
## 6     2 1 [1. Hombre]    NA

La función select() es más explícita. El primer argumento es la base de datos, y los siguientes son los nombres de las variables que queremos mantener, sin comillas.

2.2 Filtrar Casos

Objetivo: Quedarnos solo con las personas mayores de 65 años.

Con R Base:

casen_base_filter <- casen_sub[casen_sub$edad > 65, ]
dim(casen_base_filter)
## [1] 30809     7

Aquí, la condición lógica casen_sub$edad > 65 se aplica en el espacio de las filas. R evalúa esta condición para cada fila y se queda solo con aquellas donde el resultado es TRUE.

Con dplyr:

casen_dplyr_filter <- filter(casen_sub, edad > 65)
dim(casen_dplyr_filter)
## [1] 30809     7

El verbo filter() hace la misma operación, pero de una forma más legible: “Filtra la base casen_sub donde la edad sea mayor a 65”.

3. La Gramática de dplyr y el Poder del Pipe %>%

3.1 La Lógica de las Funciones de dplyr

El paquete dplyr es tan poderoso porque sus funciones (“verbos”) siguen una gramática simple y consistente:

  1. El primer argumento es siempre un data.frame.
  2. Los argumentos siguientes describen qué hacer, usando los nombres de las variables sin comillas.
  3. El resultado es siempre un nuevo data.frame. dplyr nunca modifica tus datos originales.

3.2 ¿Por qué funciona el Pipe?

El operador pipe (%>%) toma lo que está a su izquierda y lo pone como primer argumento de la función a su derecha.

Esta es la razón por la que dplyr y el pipe son la combinación perfecta: como todos los verbos de dplyr esperan un data.frame como primer argumento, podemos encadenarlos fluidamente.

Sin el pipe (funciones anidadas): El código se lee “de adentro hacia afuera”, lo que es poco intuitivo.

casen_anidado <- filter(
  select(casen_sub, region, edad, ytotcorh),
  region == 13 # Usamos el código numérico de la RM
)
head(casen_anidado)
## # A tibble: 6 × 3
##   region                                 edad ytotcorh
##   <dbl+lbl>                             <dbl>    <dbl>
## 1 13 [Región Metropolitana de Santiago]    54  2945500
## 2 13 [Región Metropolitana de Santiago]    28  2945500
## 3 13 [Región Metropolitana de Santiago]    27  2945500
## 4 13 [Región Metropolitana de Santiago]    19  2945500
## 5 13 [Región Metropolitana de Santiago]    56  2945500
## 6 13 [Región Metropolitana de Santiago]    81   963997

Con el pipe (%>%): El código se lee de izquierda a derecha. ¡Mucho más intuitivo!

# Atajo para el pipe: Ctrl + Shift + M
casen_pipe <- casen_sub %>%
  select(region, edad, ytotcorh) %>%
  filter(region == 13)

head(casen_pipe)
## # A tibble: 6 × 3
##   region                                 edad ytotcorh
##   <dbl+lbl>                             <dbl>    <dbl>
## 1 13 [Región Metropolitana de Santiago]    54  2945500
## 2 13 [Región Metropolitana de Santiago]    28  2945500
## 3 13 [Región Metropolitana de Santiago]    27  2945500
## 4 13 [Región Metropolitana de Santiago]    19  2945500
## 5 13 [Región Metropolitana de Santiago]    56  2945500
## 6 13 [Región Metropolitana de Santiago]    81   963997

A partir de ahora, usaremos el pipe para todas nuestras operaciones.

4. Aplicando los Verbos de dplyr

Ahora, usemos esta nueva gramática para responder preguntas de investigación.

4.1 arrange(): Ordenando los datos

Pregunta: ¿Cuáles son los 5 hogares con mayor ingreso (ytotcorh) en nuestra submuestra?

# Usamos arrange() con desc() para ordenar de mayor a menor
casen_sub %>%
  filter(pco1 == 1) %>% # 1. Filtramos solo jefes de hogar
  arrange(desc(ytotcorh)) %>%
  head(5) # Usamos head() para ver solo los primeros 5 resultados
## # A tibble: 5 × 7
##       folio pco1                     region         sexo     edad   esc ytotcorh
##       <dbl> <dbl+lbl>                <dbl+lbl>      <dbl+l> <dbl> <dbl>    <dbl>
## 1 295720201 1 [1. Jefatura de Hogar] 13 [Región Me… 1 [1. …    44    17 77300000
## 2 246360701 1 [1. Jefatura de Hogar] 14 [Región de… 1 [1. …    72    17 65167000
## 3 391140701 1 [1. Jefatura de Hogar]  3 [Región de… 1 [1. …    45    13 56833333
## 4 306270201 1 [1. Jefatura de Hogar]  4 [Región de… 1 [1. …    54    15 53783334
## 5 320530301 1 [1. Jefatura de Hogar] 14 [Región de… 1 [1. …    44    12 35143333

Interpretación del output: La tabla muestra las 5 filas que corresponden a los jefes de los hogares con los ingresos más altos en toda la muestra.

4.2 mutate(): Creando nuevas variables

mutate() nos permite crear nuevas columnas. La buena práctica es crear nuevas columnas con nombres diferentes (ej. sexo_factor) para no perder la información original (ej. sexo).

Objetivo: Crear una variable con la edad en meses y convertir sexo y region a factores para que sean más legibles.

casen_procesada <- casen_sub %>%
  mutate(
    edad_meses = edad * 12, # Un cálculo aritmético simple
    sexo_factor = as_factor(sexo), # Creamos una nueva variable factor
    region_factor = as_factor(region) # Creamos otra variable factor
  )

# Veamos las nuevas columnas (al final a la derecha)
head(casen_procesada)
## # A tibble: 6 × 10
##      folio pco1     region   sexo     edad   esc ytotcorh edad_meses sexo_factor
##      <dbl> <dbl+lb> <dbl+lb> <dbl+l> <dbl> <dbl>    <dbl>      <dbl> <fct>      
## 1   1.00e8  2 [2. … 16 [Reg… 2 [2. …    72     1  1010894        864 2. Mujer   
## 2   1.00e8  1 [1. … 16 [Reg… 1 [1. …    67     4  1010894        804 1. Hombre  
## 3   1.00e8  4 [4. … 16 [Reg… 2 [2. …    40    15  1010894        480 2. Mujer   
## 4   1.00e8  2 [2. … 16 [Reg… 1 [1. …    56    NA   418192        672 1. Hombre  
## 5   1.00e8  4 [4. … 16 [Reg… 2 [2. …    25    12   418192        300 2. Mujer   
## 6   1.00e8 10 [10.… 16 [Reg… 1 [1. …     2    NA   418192         24 1. Hombre  
## # ℹ 1 more variable: region_factor <fct>

Interpretación del output: Nuestra nueva base de datos casen_procesada ahora tiene 10 columnas. Las tres últimas son las que acabamos de crear. sexo_factor y region_factor ahora contienen texto legible en lugar de códigos numéricos.

4.3 group_by() y summarise(): El corazón del análisis descriptivo

Pregunta 1: ¿Cuál es el promedio de años de escolaridad (esc) por sexo?

Ahora podemos usar nuestra nueva variable sexo_factor para que el resultado sea más claro.

casen_procesada %>%
  group_by(sexo_factor) %>% # Agrupamos los datos por la variable factor
  summarise(
    escolaridad_promedio = mean(esc, na.rm = TRUE), # Calculamos la media para cada grupo
    n_casos = n() # n() es una función especial que cuenta los casos por grupo
  )
## # A tibble: 2 × 3
##   sexo_factor escolaridad_promedio n_casos
##   <fct>                      <dbl>   <int>
## 1 1. Hombre                   11.2   95656
## 2 2. Mujer                    11.1  106575

Interpretación del output: La tabla muestra la escolaridad promedio separada por sexo. Los valores entre hombres y mujeres son similares, aunque las diferencias específicas dependen del año de la encuesta. La columna n_casos informa cuántas personas hay en cada grupo.

Pregunta 2 (más compleja): ¿Cuál es el ingreso promedio del hogar (ytotcorh) en cada región, pero considerando solo a los jefes de hogar (pco1 == 1)? Ordena los resultados de mayor a menor.

casen %>% # Volvemos a la base original porque necesitamos pco1
  mutate(region_factor = as_factor(region)) %>% # Creamos el factor de región
  filter(pco1 == 1) %>% # 1. Filtramos solo jefes de hogar
  group_by(region_factor) %>% # 2. Agrupamos por la región (factor)
  summarise(
    ingreso_hog_promedio = mean(ytotcorh, na.rm = TRUE) # 3. Calculamos la media
  ) %>%
  arrange(desc(ingreso_hog_promedio)) # 4. Ordenamos el resultado
## # A tibble: 16 × 2
##    region_factor                                     ingreso_hog_promedio
##    <fct>                                                            <dbl>
##  1 Región Metropolitana de Santiago                              1863656.
##  2 Región de Magallanes y de la Antártica Chilena                1603080.
##  3 Región de Aysén del Gral. Carlos Ibáñez del Campo             1550975.
##  4 Región de Antofagasta                                         1502477.
##  5 Región de Tarapacá                                            1334223.
##  6 Región de Arica y Parinacota                                  1273240.
##  7 Región de Valparaíso                                          1255938.
##  8 Región de Atacama                                             1226856.
##  9 Región del Libertador Gral. Bernardo O'Higgins                1214506.
## 10 Región de Coquimbo                                            1204635.
## 11 Región de Los Ríos                                            1203132.
## 12 Región de Los Lagos                                           1158288.
## 13 Región del Biobío                                             1140827.
## 14 Región del Maule                                              1000071.
## 15 Región de La Araucanía                                         996879.
## 16 Región de Ñuble                                                959050.

Interpretación del output: La tabla muestra un ranking de las regiones según el ingreso promedio de sus hogares, revelando patrones de desigualdad territorial. La región con mayor ingreso promedio y la de menor ingreso quedan visibles directamente en la tabla ordenada.


Actividad 1: Usando la base casen_procesada, calcula la edad promedio y el número de casos por sexo_factor. Luego, filtra el resultado para quedarte solo con las personas menores de 30 años antes de calcular los promedios. ¿Cambia la relación entre los grupos al restringir la muestra a jóvenes?

# Escribe tu código aquí

5. Actividad de Desafío

Ahora te toca a ti. Usando la base casen_procesada y la gramática de dplyr, escribe el código necesario para responder la siguiente pregunta de investigación:

Pregunta: ¿Cuál es la edad promedio y el promedio de años de escolaridad para cada región? Asegúrate de que los resultados estén ordenados de mayor a menor años de escolaridad promedio.

# 

6. Reflexiones Finales

En este práctico, hemos aprendido a usar la poderosa gramática de dplyr para manipular datos de forma eficiente y reproducible. Hemos visto que con unos pocos verbos (select, filter, arrange, mutate, summarise) y el operador %>%, podemos pasar de una base de datos cruda a una tabla de resultados que responde a nuestras preguntas de investigación.

En la próxima clase, profundizaremos en la creación de variables más complejas, como índices, usando mutate() y case_when().