1 Leemos la Casen


En la CASEN 2022, el Ingreso del Trabajo Corregido (ITC) es una variable derivada que busca reflejar de manera más precisa el ingreso laboral efectivo de las personas, corrigiendo distorsiones de la variable de ingreso bruto reportada en la encuesta.

🔹 Qué significa

Ingreso del trabajo: usualmente incluye salarios, remuneraciones por cuenta propia, honorarios, comisiones y otros pagos por empleo o actividad económica principal.

Corregido: la CASEN aplica ajustes para:

Valores atípicos o erróneos reportados (outliers).

Imputaciones cuando el ingreso no fue declarado.

Homogeneizar diferentes fuentes de ingreso (salarios, honorarios, trabajo independiente) a una misma base.

Ajustar por periodicidad (mensualizar o anualizar ingresos reportados semanalmente o quincenalmente).

Objetivo:

Evitar que errores de reporte distorsionen estadísticas como promedio, mediana, desigualdad (Gini, Theil, Atkinson).

Permitir comparaciones más confiables entre grupos, comunas o regiones.

🔹 Uso en análisis

Es la variable recomendada para estudiar ingresos laborales.

Se utiliza en cálculos de desigualdad, correlaciones con pobreza, estratificación socioeconómica y análisis de políticas laborales.

Evita problemas que tendrías usando solo el ingreso bruto reportado, que puede contener valores extremos o inconsistentes.

# 1) Cargar archivo SPSS

casen2022 <- casen2022 |> 
  janitor::clean_names()

# 2) Lista de nombres de todas las variables
vars <- names(casen2022)

# 3) Buscar candidatos a variables de diseño
peso_vars    <- vars[str_detect(vars, regex("factor|expan|peso", ignore_case = TRUE))]
estrato_vars <- vars[str_detect(vars, regex("estrat", ignore_case = TRUE))]
upm_vars     <- vars[str_detect(vars, regex("upm|psu|conglo", ignore_case = TRUE))]

# 4) Buscar candidatos a variables de ingreso
ingreso_vars <- vars[str_detect(vars, regex("ingreso|autonom|trabaj|monet", ignore_case = TRUE))]

# 5) Mostrar resultados
list(
  posibles_pesos   = peso_vars,
  posibles_estrato = estrato_vars,
  posibles_upm     = upm_vars,
  posibles_ingresos = ingreso_vars
)
## $posibles_pesos
## character(0)
## 
## $posibles_estrato
## [1] "estrato"
## 
## $posibles_upm
## [1] "cod_upm"
## 
## $posibles_ingresos
## [1] "ytrabajocor"  "ytrabajocorh"

Voy a trabajar con la CASEN 2022 considerando su estructura de muestreo real: estratificación, unidades primarias de muestreo y pesos. Además, ajustaré automáticamente los errores estándar en estratos con un solo PSU (unidad primaria de muestreo).

1️⃣ Crear el ingreso per cápita por vivienda

Cada vivienda (id_vivienda) tiene un ingreso total ytrabajocorh. Cada persona dentro de esa vivienda está identificada con id_persona. Entonces:

ingreso_per_capita = ytrabajocorh/numero de personas en la vivienda

casen2022a <- casen2022 %>%
  mutate(
    estrato_char = as.character(estrato),
    comuna = ifelse(
      nchar(estrato_char) == 6,
      substr(estrato_char, 1, 4),
      substr(estrato_char, 1, 5)
    )
  )


casen2022b <- casen2022a %>%
  group_by(id_vivienda) %>%
  mutate(
    personas_vivienda = n(),
    ingreso_per_capita = ytrabajocorh / personas_vivienda
  ) %>%
  ungroup()


options(survey.lonely.psu = "adjust")

diseño_casen <- svydesign(
  ids = ~cod_upm,
  strata = ~estrato,
  weights = ~expr,
  data = casen2022b
)

# Media del ingreso per cápita usando el diseño muestral
svymean(~ingreso_per_capita, diseño_casen, na.rm = TRUE)
##                      mean     SE
## ingreso_per_capita 371993 4361.3
library(dplyr)
library(DT)
library(survey)

options(survey.lonely.psu = "adjust")

# Cargar dataset
casen2022 <- readRDS("casen2022_raw.rds")

# Crear columna de comuna a partir de estrato
casen2 <- casen2022 %>%
  mutate(
    estrato_char = as.character(estrato),
    codigo_cut = ifelse(
      nchar(estrato_char) == 6,
      substr(estrato_char, 1, 4),
      substr(estrato_char, 1, 5)
    )
  )

# Resumir por vivienda: ingreso total y número de personas
viviendas <- casen2 %>%
  group_by(id_vivienda, cod_upm, estrato, expr, codigo_cut) %>%
  summarise(
    personas_vivienda = n(),
    ingreso_per_capita = ytrabajocorh / personas_vivienda,
    .groups = "drop"
  )
## Warning: Returning more (or less) than 1 row per `summarise()` group was deprecated in
## dplyr 1.1.0.
## ℹ Please use `reframe()` instead.
## ℹ When switching from `summarise()` to `reframe()`, remember that `reframe()`
##   always returns an ungrouped data frame and adjust accordingly.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
# Definir diseño muestral (solo con una fila por vivienda)
diseño_casen <- svydesign(
  ids = ~cod_upm,
  strata = ~estrato,
  weights = ~expr,
  data = viviendas
)

# Calcular media del ingreso per cápita por comuna
media_comunas <- svyby(
  ~ingreso_per_capita,
  ~codigo_cut,
  diseño_casen,
  svymean,
  na.rm = TRUE
)

# Mostrar tabla interactiva
DT::datatable(
  media_comunas,
  options = list(
    pageLength = 10,
    scrollX = TRUE
  ),
  rownames = FALSE,
  class = 'stripe hover cell-border compact'
)
library(readxl)
## Warning: package 'readxl' was built under R version 4.4.3
# Leer el Excel usando la primera fila como nombres de columna
datos <- read_excel("DPA2018.xls", skip = 0)  # no saltamos filas
## New names:
## • `` -> `...2`
## • `` -> `...3`
## • `` -> `...4`
## • `` -> `...5`
## • `` -> `...6`
## • `` -> `...7`
## • `` -> `...8`
## • `` -> `...9`
## • `` -> `...10`
## • `` -> `...11`
## • `` -> `...12`
## • `` -> `...13`
## • `` -> `...14`
## • `` -> `...15`
## • `` -> `...16`
## • `` -> `...17`
## • `` -> `...18`
## • `` -> `...19`
## • `` -> `...20`
## • `` -> `...21`
## • `` -> `...22`
## • `` -> `...23`
## • `` -> `...24`
## • `` -> `...25`
## • `` -> `...26`
# Asegurarnos de que la primera fila se use como nombres
# Si no se reconoce automáticamente, podemos hacer:
colnames(datos) <- as.character(unlist(datos[1, ]))
datos <- datos[-1, ]  # eliminamos la fila original que era header

# Seleccionar solo las columnas de interés y limpiar código
codigos_comunas <- datos %>%
  select(`Código Comuna desde 2018`, `Nombre Comuna`) %>%
  rename(
    codigo_cut = `Código Comuna desde 2018`,
    comuna = `Nombre Comuna`
  ) %>%
  mutate(
    # Convertir a character y eliminar ceros a la izquierda
    codigo_cut = str_remove_all(as.character(codigo_cut), "^0"),
    # Convertir a número
    codigo_cut = as.numeric(codigo_cut)
  )

# Verificar
head(codigos_comunas)
## # A tibble: 6 × 2
##   codigo_cut comuna       
##        <dbl> <chr>        
## 1      15101 Arica        
## 2      15102 Camarones    
## 3      15201 Putre        
## 4      15202 General Lagos
## 5       1101 Iquique      
## 6       1402 Camiña
# 6️⃣ Hacer join con los nombres oficiales de las comunas
media_comunas <- media_comunas %>%
  mutate(codigo_cut = as.character(codigo_cut)) %>%  # convertir a character
  left_join(
    codigos_comunas %>% mutate(codigo_cut = as.character(codigo_cut)),  # también character
    by = "codigo_cut"
  ) %>%
  select(codigo_cut, comuna, ingreso_per_capita, se)

# 7️⃣ Mostrar tabla interactiva
DT::datatable(
  media_comunas,
  options = list(
    pageLength = 10,
    scrollX = TRUE
  ),
  rownames = FALSE,
  class = 'stripe hover cell-border compact'
)
library(dplyr)
library(plotly)
## 
## Adjuntando el paquete: 'plotly'
## The following object is masked from 'package:ggplot2':
## 
##     last_plot
## The following object is masked from 'package:stats':
## 
##     filter
## The following object is masked from 'package:graphics':
## 
##     layout
# Ordenar de menor a mayor ingreso y fijar el orden de la columna comuna
media_comunas <- media_comunas %>%
  arrange(ingreso_per_capita) %>%
  mutate(comuna = factor(comuna, levels = comuna))  # niveles según ingreso

# Gráfico interactivo con scroll horizontal
fig <- plot_ly(
  media_comunas,
  x = ~comuna,
  y = ~ingreso_per_capita,
  type = 'bar',
  text = ~paste0(
    "Comuna: ", comuna, "<br>",
    "Ingreso: $", format(round(ingreso_per_capita, 0), big.mark = ","), "<br>",
    "SE: $", format(round(se, 0), big.mark = ",")
  ),
  hoverinfo = 'text',
  marker = list(color = 'steelblue')
) %>%
  layout(
    title = "Ingreso per cápita promedio por comuna (todas las comunas)",
    xaxis = list(
      title = "Comuna",
      tickangle = -45,
      fixedrange = FALSE  # permite scroll
    ),
    yaxis = list(title = "Ingreso per cápita (CLP)"),
    margin = list(b = 250)
  ) %>%
  config(displayModeBar = TRUE)

# Mostrar gráfico
fig
# 2) Variables clave
# Estrato: estrato
# UPM: cod_upm
# Peso: expr
# Ingreso principal: ytrabajocor (persona), ytrabajocorh (hogar)


# 3) Crear diseño muestral
options(survey.lonely.psu = "adjust")
diseño_casen <- svydesign(
ids = ~cod_upm,
strata = ~estrato,
weights = ~expr,
data = casen2022
)


diseño_casen <- convey_prep(diseño_casen)


# 4) Estadísticas básicas
media_ingreso <- svymean(~ytrabajocor, diseño_casen, na.rm = TRUE)
mediana_ingreso <- svyquantile(~ytrabajocor, diseño_casen, 0.5, na.rm = TRUE)


# 5) Desigualdad
gini_ingreso <- svygini(~ytrabajocor, diseño_casen, na.rm = TRUE)
theil_ingreso <- svygei(~ytrabajocor, diseño_casen, epsilon = 1, na.rm = TRUE)
mld_ingreso <- svygei(~ytrabajocor, diseño_casen, epsilon = 0, na.rm = TRUE)


# 6) Quantile ratios / S80/S20
p10 <- svyquantile(~ytrabajocor, diseño_casen, 0.10, na.rm = TRUE)
p90 <- svyquantile(~ytrabajocor, diseño_casen, 0.90, na.rm = TRUE)
s20 <- svyquantile(~ytrabajocor, diseño_casen, 0.20, na.rm = TRUE)
s80 <- svyquantile(~ytrabajocor, diseño_casen, 0.80, na.rm = TRUE)


p90_p10_ratio <- coef(p90)/coef(p10)
s80_s20_ratio <- coef(s80)/coef(s20)


# 7) Resultados finales
resultados <- list(
media = media_ingreso,
mediana = mediana_ingreso,
gini = gini_ingreso,
theil = theil_ingreso,
mld = mld_ingreso,
p90_p10_ratio = p90_p10_ratio,
s80_s20_ratio = s80_s20_ratio
)

# 8) Mostrar resultados
resultados
## $media
##               mean     SE
## ytrabajocor 794509 8093.8
## 
## $mediana
## $ytrabajocor
##     quantile ci.2.5 ci.97.5       se
## 0.5   506667 503333  514167 2763.538
## 
## attr(,"hasci")
## [1] TRUE
## attr(,"class")
## [1] "newsvyquantile"
## 
## $gini
##                gini     SE
## ytrabajocor 0.46195 0.0037
## 
## $theil
##                 gei   SE
## ytrabajocor 0.41664 0.01
## 
## $mld
##                 gei     SE
## ytrabajocor 0.42112 0.0063
## 
## $p90_p10_ratio
## ytrabajocor 
##           8 
## 
## $s80_s20_ratio
## ytrabajocor 
##     3.10559
# Suponiendo que ya tienes diseño preparado
# diseño_casen <- svydesign(...) %>% convey_prep()


# Preparar datos para ggplot
casen_plot <- na.omit(casen2022[, c("ytrabajocor", "expr")])


# Calcular percentiles y media
p10 <- coef(svyquantile(~ytrabajocor, diseño_casen, 0.10))
p90 <- coef(svyquantile(~ytrabajocor, diseño_casen, 0.90))
p50 <- coef(svyquantile(~ytrabajocor, diseño_casen, 0.50))
media <- coef(svymean(~ytrabajocor, diseño_casen))
s20 <- coef(svyquantile(~ytrabajocor, diseño_casen, 0.20))
s80 <- coef(svyquantile(~ytrabajocor, diseño_casen, 0.80))


# Filtrar solo valores positivos para escala log
vline_values <- c(media, p50, p10, p90)
vline_values <- vline_values[vline_values > 0]


# Crear gráfico con densidad ponderada y log X
ggplot(casen_plot, aes(x = ytrabajocor)) +
geom_density(aes(weight = expr), fill = "steelblue", alpha = 0.5) +
geom_vline(xintercept = media, color = "green", linetype = "dashed", linewidth = 1) +
geom_vline(xintercept = p50, color = "red", linetype = "dashed", linewidth = 1) +
geom_vline(xintercept = p10, color = "orange", linetype = "dashed", linewidth = 1) +
geom_vline(xintercept = p90, color = "purple", linetype = "dashed", linewidth = 1) +
scale_x_log10(labels = comma) +
labs(title = "Distribución de Ingresos CASEN 2022 (log) con densidad ponderada",
subtitle = "Media (verde), Mediana (roja), P10 (naranja), P90 (morado)",
x = "Ingreso individual (CLP, escala log)",
y = "Densidad ponderada") +
theme_minimal()
## Warning: Removed 1 row containing missing values or values outside the scale range
## (`geom_vline()`).
## Removed 1 row containing missing values or values outside the scale range
## (`geom_vline()`).
## Removed 1 row containing missing values or values outside the scale range
## (`geom_vline()`).