# ============================================================================= # Programa de identificación de la informalidad en la # Encuesta Nacional de Ocupación y Empleo (ENOE) # # Programa Universitario de Estudios del Desarrollo # # Autor: Fernando Ricalde Zamora # # ============================================================================= # ----------------------------------------------------------------------------- # 0. Configuración del entorno # ----------------------------------------------------------------------------- rm(list = ls()) # Ajustar la ruta de trabajo a la conveniencia del usuario. #setwd("~") if (!require("pacman")) install.packages("pacman") library(pacman) p_load(tidyverse, survey, srvyr, janitor, gt, scales) options(survey.lonely.psu = "adjust") options(na.print = "NA") # ----------------------------------------------------------------------------- # 1. Carga de microdatos # ----------------------------------------------------------------------------- # Trimestre 3 - 2012 sdem12 <- read.csv("sdemt312.csv") %>% janitor::clean_names() coe12 <- read.csv("coe1t312.csv") %>% janitor::clean_names() # Trimestre 3 - 2014 (disponible para análisis comparativo) # sdem14 <- read.csv("SDEMT314.csv") %>% janitor::clean_names() # coe14 <- read.csv("COE1T314.csv") %>% janitor::clean_names() # ----------------------------------------------------------------------------- # 2. Folio único para unir bases (necesario en microdatos previos a 2020Q1) # ----------------------------------------------------------------------------- construir_folio <- function(df) { df %>% mutate(folio = paste0( sprintf("%02d", ent), sprintf("%02d", con), sprintf("%04d", upm), sprintf("%02d", v_sel), sprintf("%02d", n_hog) )) } sdem12 <- construir_folio(sdem12) coe12 <- construir_folio(coe12) # ----------------------------------------------------------------------------- # 3. Unión SDEM + COE1 # ----------------------------------------------------------------------------- # Se traen las variables del cuestionario de ocupación y empleo necesarias # para reconstruir CLASE2 y otras categorías de interés. vars_coe <- c("folio", "n_ren", "p1", "p1a1", "p1a2", "p1a3", "p1b", "p1c", "p1d", "p1e", "p4", "p4a", "p4b", "p4c", "p4e", "p4g") base_total <- sdem12 %>% left_join(coe12 %>% select(any_of(vars_coe)), by = c("folio", "n_ren")) # ----------------------------------------------------------------------------- # 4. Recodificación de variables clave # ----------------------------------------------------------------------------- ## 4.1 CLASE2X --- replicación de la condición de población ocupada ---------- # # Árbol del cuestionario COE1: # p1 == 1 .................. trabajó la semana de referencia # p1a1 == 1 .................. tuvo trabajo o ayudó en un negocio # p1a2 == 1 .................. realizó alguna actividad económica # p1c in {1,2,3,4} .......... produjo o vendió bienes con fin económico # p1d == 1 .................. ausente con vínculo laboral # p1d in {2,9} & p1e == 1 ... ausente y regresará al trabajo # # Cualquier afirmación al árbol implica clase2 = 1 (ocupado). base_total <- base_total %>% mutate(clase2x = case_when( p1 == 1 ~ 1, p1a1 == 1 ~ 1, p1a2 == 1 ~ 1, p1c %in% 1:4 ~ 1, p1d == 1 ~ 1, p1d %in% c(2, 9) & p1e == 1 ~ 1, TRUE ~ 0 )) # --- Validación de concordancia con la variable oficial clase2 -------------- cat("\n--- Concordancia clase2 (oficial) vs clase2x (replicado) ---\n") print( janitor::tabyl(base_total, clase2, clase2x) %>% janitor::adorn_totals(c("row", "col")) ) cat(" NOTA METODOLÓGICA: La replicación del algoritmo de clasificación de población ocupada (clase2x) a partir de las preguntas filtro del COE1 alcanza una concordancia cercana al 99 % con la variable oficial clase2 del SDEM en el caso del tercer trimestre de 2012, sin falsos positivos. La pequeña fracción de discrepancias (~1.3 %) corresponde a registros con COE1 parcialmente respondido (únicamente p1 = 2 y p1a2 = 2), cuya clasificación, intuyo, INEGI completa mediante imputación con base en información del panel y del COE2, no reconstruible a partir del COE1 únicamente. Para todo el análisis sustantivo se utiliza la variable oficial clase2. ") ## 4.2 Filtros estándar para la población ocupada ----------------------------- ocupados <- base_total %>% filter(r_def == 0, c_res %in% c(1, 3), eda %in% 14:98, clase2 == 1) ## 4.3 EMP_PPAL2 --- empleo principal formal/informal ------------------------ # 1 = informal, 2 = formal ocupados <- ocupados %>% mutate(emp_ppal2 = case_when( tue2 == 5 ~ 1, pos_ocu == 3 & rama == 6 ~ 1, pos_ocu == 4 ~ 1, seg_soc %in% c(2, 3) & tue2 == 6 ~ 1, pos_ocu == 1 & remune2c %in% 1:2 & !(tue2 %in% c(5, 6, 7)) & seg_soc %in% c(2, 3) ~ 1, pos_ocu == 5 & seg_soc %in% c(2, 3) ~ 1, TRUE ~ 2 )) cat("\n--- Concordancia emp_ppal (oficial) vs emp_ppal2 (replicado) ---\n") print(janitor::tabyl(ocupados, emp_ppal, emp_ppal2)) ## 4.4 Columnas y filas de la matriz de Hussmanns ---------------------------- ocupados <- ocupados %>% mutate( # MH_COL: posición en la ocupación × condición de formalidad mh_col2 = case_when( pos_ocu %in% c(1, 5) & remune2c == 1 & emp_ppal2 == 1 ~ 1, pos_ocu %in% c(1, 5) & remune2c == 1 & emp_ppal2 == 2 ~ 2, ((pos_ocu == 1 & remune2c == 2) | pos_ocu == 5) & emp_ppal2 == 1 ~ 3, ((pos_ocu == 1 & remune2c == 2) | pos_ocu == 5) & emp_ppal2 == 2 ~ 4, pos_ocu == 2 & emp_ppal2 == 1 ~ 5, pos_ocu == 2 & emp_ppal2 == 2 ~ 6, pos_ocu == 3 & emp_ppal2 == 1 ~ 7, pos_ocu == 3 & emp_ppal2 == 2 ~ 8, pos_ocu == 4 & emp_ppal2 == 1 ~ 9, pos_ocu == 4 & emp_ppal2 == 2 ~ 10, TRUE ~ NA_integer_ ), # MH_FIL: tipo de unidad económica mh_fil3 = case_when( tue2 == 5 & ambito1 != 1 ~ 1, # Sector informal tue1 == 3 & tue2 == 6 ~ 2, # Trabajo doméstico tue2 != 5 & tue1 != 3 & ambito1 != 1 ~ 3, # Empresas e instituciones ambito1 == 1 | (tue1 == 3 & tue2 == 7) ~ 4, # Ámbito agropecuario TRUE ~ NA_integer_ ), # Celdas: arábigos = empleo informal; romanos = empleo formal celda = case_when( mh_col2 == 1 & mh_fil3 == 1 ~ "1", mh_col2 == 3 & mh_fil3 == 1 ~ "2", mh_col2 == 5 & mh_fil3 == 1 ~ "3", mh_col2 == 7 & mh_fil3 == 1 ~ "4", mh_col2 == 9 & mh_fil3 == 1 ~ "5", mh_col2 == 1 & mh_fil3 == 2 ~ "6", mh_col2 == 3 & mh_fil3 == 2 ~ "7", mh_col2 == 1 & mh_fil3 == 3 ~ "8", mh_col2 == 3 & mh_fil3 == 3 ~ "9", mh_col2 == 9 & mh_fil3 == 3 ~ "10", mh_col2 == 1 & mh_fil3 == 4 ~ "11", mh_col2 == 3 & mh_fil3 == 4 ~ "12", mh_col2 == 7 & mh_fil3 == 4 ~ "13", mh_col2 == 9 & mh_fil3 == 4 ~ "14", mh_col2 == 2 & mh_fil3 == 2 ~ "I", mh_col2 == 4 & mh_fil3 == 2 ~ "II", mh_col2 == 2 & mh_fil3 == 3 ~ "III", mh_col2 == 4 & mh_fil3 == 3 ~ "IV", mh_col2 == 6 & mh_fil3 == 3 ~ "V", mh_col2 == 8 & mh_fil3 == 3 ~ "VI", mh_col2 == 2 & mh_fil3 == 4 ~ "VII", mh_col2 == 4 & mh_fil3 == 4 ~ "VIII", mh_col2 == 6 & mh_fil3 == 4 ~ "IX", TRUE ~ NA_character_ ) ) # ----------------------------------------------------------------------------- # 5. Diseño muestral complejo # ----------------------------------------------------------------------------- design12t3 <- svydesign( ids = ~upm, strata = ~est, weights = ~fac, data = ocupados, nest = TRUE ) # ----------------------------------------------------------------------------- # 6. Función que produce la matriz de Hussmanns con formato presentable # ----------------------------------------------------------------------------- # - Distingue celdas conceptualmente imposibles (—) de celdas con valor cero. # - Añade totales marginales por fila y por columna. # - Sombrea columnas informales (ámbar) y formales (azul). generar_tabla_hussmans <- function(design, trimestre_label = "Trimestre 3 de 2012", escala = 1e6, etiqueta_escala = "Millones de personas") { # 6.1 Totales ponderados por celda totales <- svytotal(~celda, design, na.rm = TRUE) totales_df <- as.data.frame(totales) %>% tibble::rownames_to_column("celda") %>% mutate(celda = sub("^celda", "", celda)) %>% select(celda, total) # 6.2 Estructura conceptual de la matriz: NA = imposibilidad por definición estructura <- tribble( ~Unidad, ~A_Inf, ~A_For, ~NA_Inf, ~NA_For, ~E_Inf, ~E_For, ~CP_Inf, ~CP_For, ~NR_Inf, ~NR_For, "Sector Informal", "1", NA, "2", NA, "3", NA, "4", NA, "5", NA, "Trabajo doméstico", "6", "I", "7", "II", NA, NA, NA, NA, NA, NA, "Empresas/Instituciones", "8", "III", "9", "IV", NA, "V", NA, "VI", "10", NA, "Ámbito agropecuario", "11", "VII", "12", "VIII", NA, "IX", "13", NA, "14", NA ) # 6.3 Sustituir códigos por totales (en la escala indicada) estructura_num <- estructura %>% mutate(across(-Unidad, ~ totales_df$total[match(., totales_df$celda)] / escala)) # 6.4 Marginales estructura_num <- estructura_num %>% rowwise() %>% mutate(Total = sum(c_across(-Unidad), na.rm = TRUE)) %>% ungroup() fila_total <- estructura_num %>% summarise(across(-Unidad, ~ sum(., na.rm = TRUE))) %>% mutate(Unidad = "Total") %>% select(Unidad, everything()) estructura_final <- bind_rows(estructura_num, fila_total) # 6.5 Composición visual con gt cols_inf <- c("A_Inf", "NA_Inf", "E_Inf", "CP_Inf", "NR_Inf") cols_for <- c("A_For", "NA_For", "E_For", "CP_For", "NR_For") estructura_final %>% gt(rowname_col = "Unidad") %>% tab_header( title = md("**Matriz de Hussmanns de la población ocupada**"), subtitle = md(paste0("*", trimestre_label, " — ", etiqueta_escala, "*")) ) %>% tab_spanner("Subordinados remunerados", columns = c(A_Inf, A_For)) %>% tab_spanner("No asalariados", columns = c(NA_Inf, NA_For)) %>% tab_spanner("Empleadores", columns = c(E_Inf, E_For)) %>% tab_spanner("Cuenta propia", columns = c(CP_Inf, CP_For)) %>% tab_spanner("No remunerados", columns = c(NR_Inf, NR_For)) %>% cols_label( A_Inf = "Inf", A_For = "For", NA_Inf = "Inf", NA_For = "For", E_Inf = "Inf", E_For = "For", CP_Inf = "Inf", CP_For = "For", NR_Inf = "Inf", NR_For = "For", Total = "Total" ) %>% fmt_number(columns = where(is.numeric), decimals = 2) %>% sub_missing(missing_text = "—") %>% # Sombreado por bloque formal/informal tab_style( style = cell_fill(color = "#FFF4E5"), locations = cells_body(columns = all_of(cols_inf)) ) %>% tab_style( style = cell_fill(color = "#E8F1FA"), locations = cells_body(columns = all_of(cols_for)) ) %>% # Fila y columna de totales tab_style( style = list(cell_text(weight = "bold"), cell_fill(color = "#E5E5E5")), locations = cells_body(rows = Unidad == "Total") ) %>% tab_style( style = list(cell_text(weight = "bold"), cell_fill(color = "#E5E5E5")), locations = cells_body(columns = Total) ) %>% # Encabezados con peso visual tab_style( style = cell_text(weight = "bold"), locations = cells_column_labels() ) %>% tab_style( style = cell_text(weight = "bold"), locations = cells_column_spanners() ) %>% tab_style( style = cell_text(weight = "bold"), locations = cells_stub() ) %>% tab_options( table.font.size = "small", heading.align = "center", data_row.padding = px(4), column_labels.font.weight = "bold", table.border.top.style = "solid", table.border.top.width = px(2) ) %>% tab_source_note( source_note = md(paste0( "*Fuente: elaboración propia con base en microdatos de la ENOE, INEGI. ", "Inf = empleo informal; For = empleo formal. ", "Las celdas con '—' corresponden a combinaciones conceptualmente imposibles.*" ) ) ) } # ----------------------------------------------------------------------------- # 7. Ejecución y salida # ----------------------------------------------------------------------------- tabla_2012t3 <- generar_tabla_hussmans(design12t3, "Trimestre 3 de 2012") tabla_2012t3 # Para exportar la tabla: gtsave(tabla_2012t3, "hussmanns_2012t3.html")