# --- Resolución de nombres de variables por patrón ---------------------------
elegir_variable <- function(nombres, exactos = character(), patrones = character()) {
  for (e in exactos) if (e %in% nombres) return(e)
  for (p in patrones) {
    coincide <- grep(p, nombres, value = TRUE, ignore.case = TRUE)
    if (length(coincide) > 0) return(coincide[1])
  }
  NA_character_
}

exigir_variable <- function(valor, concepto, nombres) {
  if (is.na(valor)) {
    stop(
      "No fue posible identificar la variable correspondiente a '", concepto,
      "' en la base leída. Nombres disponibles (primeros 60): ",
      paste(utils::head(nombres, 60), collapse = ", "),
      call. = FALSE
    )
  }
  valor
}

# --- Caché, descarga y extracción --------------------------------------------
dir_cache <- here::here("cache_enemdu")
dir.create(dir_cache, showWarnings = FALSE, recursive = TRUE)

nombre_seguro <- function(x) {
  x <- utils::URLdecode(basename(x))
  gsub("[^A-Za-z0-9._-]+", "_", x)
}

descargar_zip <- function(url, destino) {
  if (file.exists(destino) && file.size(destino) > 10240) return(invisible(destino))

  temporal <- paste0(destino, ".parcial")
  resultado <- tryCatch({
    utils::download.file(url, destfile = temporal, mode = "wb", quiet = TRUE)
    "ok"
  }, error = function(e) conditionMessage(e),
     warning = function(w) conditionMessage(w))

  descarga_valida <- identical(resultado, "ok") &&
    file.exists(temporal) && file.size(temporal) > 10240

  if (!descarga_valida) {
    stop(
      "Falló la descarga de los microdatos.\n",
      "  URL solicitada : ", url, "\n",
      "  Detalle        : ", resultado, "\n",
      "Verifique la conexión a internet, que la dirección siga vigente en el portal del INEC ",
      "y que el archivo corresponda a un ZIP de microdatos. ",
      "Como alternativa, coloque manualmente el ZIP en la carpeta '", dir_cache,
      "' con el nombre '", basename(destino), "' y vuelva a compilar el documento.",
      call. = FALSE
    )
  }

  file.rename(temporal, destino)
  invisible(destino)
}

# Los ZIP del INEC contienen nombres de archivo con tildes codificados en CP-1252.
# Se listan y filtran siempre a nivel de bytes para evitar errores de conversión.
listar_archivos <- function(carpeta, patron) {
  todos <- list.files(carpeta, recursive = TRUE, full.names = TRUE)
  if (length(todos) == 0) return(character(0))
  todos[grepl(patron, basename(todos), ignore.case = TRUE, useBytes = TRUE)]
}

hay_microdatos <- function(carpeta) length(listar_archivos(carpeta, "\\.(sav|dta)$")) > 0

es_zip_valido <- function(ruta) {
  if (!file.exists(ruta) || file.size(ruta) < 100) return(FALSE)
  firma <- readBin(ruta, what = "raw", n = 4)
  identical(as.integer(firma), c(0x50L, 0x4BL, 0x03L, 0x04L))
}

descomprimir_con_locale <- function(ruta_zip, carpeta_destino, locale_ctype) {
  if (!is.null(locale_ctype)) {
    original <- Sys.getlocale("LC_CTYPE")
    aplicado <- suppressWarnings(try(Sys.setlocale("LC_CTYPE", locale_ctype), silent = TRUE))
    if (inherits(aplicado, "try-error") || !nzchar(aplicado)) return(FALSE)
    on.exit(suppressWarnings(Sys.setlocale("LC_CTYPE", original)), add = TRUE)
  }
  intento <- suppressWarnings(try(
    utils::unzip(ruta_zip, exdir = carpeta_destino), silent = TRUE))
  !inherits(intento, "try-error")
}

descomprimir_con_powershell <- function(ruta_zip, carpeta_destino) {
  if (.Platform$OS.type != "windows") return(FALSE)
  zip_abs <- normalizePath(ruta_zip, winslash = "\\", mustWork = FALSE)
  dir_abs <- normalizePath(carpeta_destino, winslash = "\\", mustWork = FALSE)
  orden <- sprintf(
    "Expand-Archive -LiteralPath '%s' -DestinationPath '%s' -Force", zip_abs, dir_abs)
  estado <- suppressWarnings(try(
    system2("powershell",
            args = c("-NoProfile", "-NonInteractive", "-Command", shQuote(orden, type = "cmd")),
            stdout = FALSE, stderr = FALSE),
    silent = TRUE))
  !inherits(estado, "try-error") && identical(as.integer(estado), 0L)
}

descomprimir <- function(ruta_zip, carpeta_destino) {
  if (!dir.exists(carpeta_destino)) dir.create(carpeta_destino, recursive = TRUE)
  if (hay_microdatos(carpeta_destino)) return(TRUE)

  locales <- list(NULL, "C", "Spanish_Ecuador.1252", "Spanish_Spain.1252",
                  "English_United States.1252", "es_EC.ISO8859-1", "C.UTF-8")
  for (lc in locales) {
    if (descomprimir_con_locale(ruta_zip, carpeta_destino, lc) &&
        hay_microdatos(carpeta_destino)) return(TRUE)
  }
  if (descomprimir_con_powershell(ruta_zip, carpeta_destino) &&
      hay_microdatos(carpeta_destino)) return(TRUE)

  hay_microdatos(carpeta_destino)
}

extraer_zip <- function(ruta_zip, carpeta_destino) {
  if (!dir.exists(carpeta_destino)) dir.create(carpeta_destino, recursive = TRUE)
  if (hay_microdatos(carpeta_destino)) return(invisible(carpeta_destino))

  if (!es_zip_valido(ruta_zip)) {
    stop("El archivo descargado no tiene la firma de un ZIP: ", ruta_zip,
         ". Es posible que la descarga esté corrupta o que la dirección devuelva una página ",
         "HTML en lugar del archivo de microdatos. Elimine ese archivo de la carpeta de caché ",
         "y vuelva a intentarlo.", call. = FALSE)
  }

  logrado <- descomprimir(ruta_zip, carpeta_destino)

  # Algunas entregas del INEC empaquetan un ZIP dentro de otro
  anidados <- listar_archivos(carpeta_destino, "\\.zip$")
  for (z in anidados) {
    if (es_zip_valido(z)) descomprimir(z, dirname(z))
  }

  if (!hay_microdatos(carpeta_destino)) {
    stop("No fue posible descomprimir los microdatos de ", basename(ruta_zip),
         ". El archivo contiene nombres con caracteres que la configuración regional actual ",
         "no puede interpretar y ninguna de las alternativas de extracción funcionó. ",
         "Descomprima el ZIP manualmente dentro de la carpeta '", carpeta_destino,
         "' y vuelva a compilar el documento.", call. = FALSE)
  }

  invisible(carpeta_destino)
}

localizar_base_personas <- function(carpeta) {
  archivos <- listar_archivos(carpeta, "\\.(sav|dta)$")
  if (length(archivos) == 0) {
    stop("No se encontraron archivos .sav ni .dta dentro de ", carpeta,
         ". Revise el contenido del ZIP descargado.", call. = FALSE)
  }

  nombres <- basename(archivos)
  personas <- archivos[grepl("person", nombres, ignore.case = TRUE, useBytes = TRUE)]
  if (length(personas) == 0) {
    candidatos <- archivos[!grepl("vivienda|hogar", nombres, ignore.case = TRUE, useBytes = TRUE)]
    if (length(candidatos) == 0) candidatos <- archivos
    personas <- candidatos
  }
  personas[which.max(file.size(personas))]
}

leer_microdatos <- function(ruta) {
  extension <- tolower(sub("^.*\\.([^.]+)$", "\\1", basename(ruta), useBytes = TRUE))
  base <- switch(
    extension,
    sav = haven::read_sav(ruta),
    dta = haven::read_dta(ruta),
    stop("Formato de microdatos no soportado: .", extension,
         ". Se esperaba .sav o .dta.", call. = FALSE)
  )
  janitor::clean_names(base)
}

a_numerico <- function(x) as.numeric(haven::zap_labels(x))

etiqueta_periodo <- function(url, alternativa) {
  nombre <- utils::URLdecode(basename(url))
  captura <- regmatches(nombre, regexpr("(19|20)[0-9]{2}([_-][0-9]{1,2})?", nombre))
  if (length(captura) == 0) return(alternativa)
  gsub("_", "-", captura[1])
}

obtener_base <- function(url, etiqueta) {
  archivo_zip <- file.path(dir_cache, nombre_seguro(url))
  if (!grepl("\\.zip$", archivo_zip, ignore.case = TRUE)) archivo_zip <- paste0(archivo_zip, ".zip")
  carpeta <- file.path(dir_cache, paste0(tools::file_path_sans_ext(basename(archivo_zip)), "_extraido"))

  descargar_zip(url, archivo_zip)
  extraer_zip(archivo_zip, carpeta)
  ruta_base <- localizar_base_personas(carpeta)

  list(
    datos = leer_microdatos(ruta_base),
    archivo = basename(ruta_base),
    etiqueta = etiqueta
  )
}
# Construcción de las variables de condición de actividad según la definición del INEC
preparar_condicion_actividad <- function(base, periodo) {
  nombres <- names(base)

  v_fexp <- exigir_variable(
    elegir_variable(nombres, c("fexp", "f_exp", "fexp_anual"),
                    c("^fexp", "^f_?exp", "factor.*expan")),
    "factor de expansión", nombres)

  v_upm <- exigir_variable(
    elegir_variable(nombres, c("upm", "conglomerado", "id_upm"),
                    c("^upm$", "^id_?upm$", "^conglom", "upm")),
    "conglomerado / unidad primaria de muestreo", nombres)

  v_estrato <- exigir_variable(
    elegir_variable(nombres, c("estrato", "id_estrato"),
                    c("^estrato", "estrat")),
    "estrato de muestreo", nombres)

  v_edad <- exigir_variable(
    elegir_variable(nombres, c("p03", "edad"), c("^p03$", "^edad")),
    "edad de la persona", nombres)

  v_condact <- exigir_variable(
    elegir_variable(nombres, c("condact", "condicion_actividad"),
                    c("^condact", "cond.*activ")),
    "condición de actividad", nombres)

  v_sexo <- elegir_variable(nombres, c("p02", "sexo"), c("^p02$", "^sexo"))
  v_area <- elegir_variable(nombres, c("area"), c("^area$"))

  tabla <- tibble(
    periodo   = periodo,
    fexp      = a_numerico(base[[v_fexp]]),
    upm       = as.character(base[[v_upm]]),
    estrato   = as.character(base[[v_estrato]]),
    edad      = a_numerico(base[[v_edad]]),
    condact   = a_numerico(base[[v_condact]]),
    sexo      = if (is.na(v_sexo)) NA_real_ else a_numerico(base[[v_sexo]]),
    area      = if (is.na(v_area)) NA_real_ else a_numerico(base[[v_area]])
  )

  tabla <- tabla %>%
    mutate(
      pet            = edad >= 15,
      ocupado        = condact %in% 1:6,
      desocupado     = condact %in% 7:8,
      pea            = condact %in% 1:8,
      pei            = condact == 9,
      adecuado       = condact == 1,
      subempleo      = condact %in% 2:3,
      otro_no_pleno  = condact == 4,
      no_remunerado  = condact == 5,
      no_clasificado = condact == 6,
      sexo_etq = case_when(sexo == 1 ~ "Hombre", sexo == 2 ~ "Mujer", TRUE ~ NA_character_),
      area_etq = case_when(area == 1 ~ "Urbana",  area == 2 ~ "Rural", TRUE ~ NA_character_),
      categoria_pea = case_when(
        adecuado       ~ "Empleo adecuado",
        subempleo      ~ "Subempleo",
        otro_no_pleno  ~ "Otro empleo no pleno",
        no_remunerado  ~ "Empleo no remunerado",
        no_clasificado ~ "Empleo no clasificado",
        desocupado     ~ "Desempleo",
        TRUE ~ NA_character_
      )
    ) %>%
    mutate(
      i_participacion  = as.numeric(pea),
      i_adecuado       = as.numeric(adecuado),
      i_subempleo      = as.numeric(subempleo),
      i_otro_no_pleno  = as.numeric(otro_no_pleno),
      i_no_remunerado  = as.numeric(no_remunerado),
      i_desempleo      = as.numeric(desocupado)
    ) %>%
    filter(!is.na(fexp), fexp > 0, !is.na(edad))

  attr(tabla, "variables_usadas") <- c(
    "factor de expansión" = v_fexp,
    "conglomerado (UPM)"  = v_upm,
    "estrato"             = v_estrato,
    "edad"                = v_edad,
    "condición de actividad" = v_condact,
    "sexo"                = ifelse(is.na(v_sexo), "no disponible", v_sexo),
    "área"                = ifelse(is.na(v_area), "no disponible", v_area)
  )
  tabla
}
etq_pre  <- etiqueta_periodo(params$url_pre,  "período previo")
etq_post <- etiqueta_periodo(params$url_post, "período posterior")

cruda_pre  <- obtener_base(params$url_pre,  etq_pre)
cruda_post <- obtener_base(params$url_post, etq_post)

datos_pre  <- preparar_condicion_actividad(cruda_pre$datos,  "Previo")
datos_post <- preparar_condicion_actividad(cruda_post$datos, "Posterior")

vars_pre  <- attr(datos_pre,  "variables_usadas")
vars_post <- attr(datos_post, "variables_usadas")

# Liberar memoria de las bases completas una vez extraídas las variables de interés
rm(cruda_pre, cruda_post); invisible(gc(verbose = FALSE))

datos <- bind_rows(datos_pre, datos_post) %>%
  mutate(
    periodo = factor(periodo, levels = c("Previo", "Posterior")),
    estrato_c = paste(periodo, estrato, sep = "_"),
    upm_c     = paste(periodo, estrato, upm, sep = "_")
  )

etiquetas_periodo <- c("Previo" = paste0("Previo (", etq_pre, ")"),
                       "Posterior" = paste0("Posterior (", etq_post, ")"))

resumen_muestra <- datos %>%
  group_by(periodo) %>%
  summarise(
    registros = n(),
    upm = n_distinct(upm_c),
    estratos = n_distinct(estrato_c),
    poblacion = sum(fexp),
    pet_n = sum(pet, na.rm = TRUE),
    pea_n = sum(pea, na.rm = TRUE)
  )
diseno <- datos %>%
  as_survey_design(
    ids     = upm_c,
    strata  = estrato_c,
    weights = fexp,
    nest    = TRUE
  )

diseno_pet <- diseno %>% filter(pet)
diseno_pea <- diseno %>% filter(pea)
estimar_tasa <- function(design, variable, etiqueta, denominador) {
  design %>%
    group_by(periodo) %>%
    summarise(valor = survey_mean(!!sym(variable), vartype = c("se", "ci"), na.rm = TRUE)) %>%
    transmute(
      indicador = etiqueta,
      denominador = denominador,
      periodo,
      tasa = valor,
      ee = valor_se,
      li = valor_low,
      ls = valor_upp
    )
}

indicadores <- tribble(
  ~variable,          ~etiqueta,                         ~base,        ~denominador,
  "i_participacion",  "Tasa de participación global",    "pet",        "PET",
  "i_adecuado",       "Tasa de empleo adecuado",         "pea",        "PEA",
  "i_subempleo",      "Tasa de subempleo",               "pea",        "PEA",
  "i_otro_no_pleno",  "Tasa de otro empleo no pleno",    "pea",        "PEA",
  "i_no_remunerado",  "Tasa de empleo no remunerado",    "pea",        "PEA",
  "i_desempleo",      "Tasa de desempleo",               "pea",        "PEA"
)

estimaciones <- pmap_dfr(
  list(indicadores$variable, indicadores$etiqueta, indicadores$base, indicadores$denominador),
  function(v, e, b, d) {
    d_obj <- if (b == "pet") diseno_pet else diseno_pea
    estimar_tasa(d_obj, v, e, d)
  }
) %>%
  mutate(indicador = factor(indicador, levels = indicadores$etiqueta))
contrastar <- function(design, variable, etiqueta) {
  formula_test <- stats::as.formula(paste(variable, "~ periodo"))
  prueba <- survey::svyttest(formula_test, design)

  ic <- as.numeric(prueba$conf.int)
  gl <- as.numeric(prueba$parameter)
  ee <- (ic[2] - ic[1]) / (2 * stats::qt(0.975, gl))

  tibble(
    indicador = etiqueta,
    diferencia = as.numeric(prueba$estimate),
    ee = ee,
    li = ic[1],
    ls = ic[2],
    gl = gl,
    estadistico_t = as.numeric(prueba$statistic),
    valor_p = as.numeric(prueba$p.value),
    dme = (stats::qt(0.975, gl) + stats::qt(0.80, gl)) * ee
  )
}

contrastes <- pmap_dfr(
  list(indicadores$variable, indicadores$etiqueta, indicadores$base),
  function(v, e, b) {
    d_obj <- if (b == "pet") diseno_pet else diseno_pea
    contrastar(d_obj, v, e)
  }
) %>%
  mutate(indicador = factor(indicador, levels = indicadores$etiqueta))

1 Resumen

Este informe evalúa si el conjunto de regulaciones laborales adoptadas en Ecuador durante la emergencia sanitaria por COVID-19 —en particular la Ley Orgánica de Apoyo Humanitario y su figura del contrato especial emergente, la reducción emergente de la jornada y la ampliación de las causales de terminación contractual— se asocia a un cambio detectable en la estructura del mercado laboral ecuatoriano. Para ello se comparan dos rondas de la Encuesta Nacional de Empleo, Desempleo y Subempleo (ENEMDU): una correspondiente al período previo (2019-12) y otra al período posterior (2022-12).

Las estimaciones se construyen respetando el diseño muestral complejo de la encuesta (estratificación, conglomeración y ponderación), y el contraste entre períodos se realiza mediante pruebas t que incorporan la varianza correcta del diseño. Se reportan la estimación puntual de cada tasa, su error estándar, su intervalo de confianza al 95 %, la diferencia entre períodos con su intervalo y el valor p asociado.

n_sig_res <- sum(contrastes$valor_p < 0.05)
n_tot_res <- nrow(contrastes)
lista_sig <- paste(as.character(contrastes$indicador[contrastes$valor_p < 0.05]), collapse = ", ")

cat(paste0(
  "En el contraste realizado, ",
  if (n_sig_res == 0) {
    paste0("ninguno de los ", n_tot_res, " indicadores examinados presenta una diferencia ",
           "estadísticamente significativa al 5 %. El informe desarrolla por qué ese resultado no ",
           "equivale a haber demostrado la ausencia de efecto, y qué magnitud de efecto habría sido ",
           "detectable con la precisión disponible.")
  } else {
    paste0(n_sig_res, " de los ", n_tot_res, " indicadores examinados presentan una diferencia ",
           "estadísticamente significativa al 5 % (", lista_sig, "), mientras que los restantes no ",
           "permiten rechazar la hipótesis nula. El informe discute ambos tipos de resultado: por qué ",
           "un valor p pequeño no mide la magnitud ni el origen del efecto, y por qué la ausencia de ",
           "rechazo tampoco demuestra la ausencia de cambio.")
  }
))

En el contraste realizado, 2 de los 6 indicadores examinados presentan una diferencia estadísticamente significativa al 5 % (Tasa de empleo adecuado, Tasa de otro empleo no pleno), mientras que los restantes no permiten rechazar la hipótesis nula. El informe discute ambos tipos de resultado: por qué un valor p pequeño no mide la magnitud ni el origen del efecto, y por qué la ausencia de rechazo tampoco demuestra la ausencia de cambio.

El análisis es explícitamente descriptivo-inferencial y no causal. Una comparación antes-después sobre dos cortes transversales no aísla el efecto de la regulación: cualquier diferencia observada mezcla el efecto normativo con el shock sanitario, el ciclo económico, los cambios en el operativo de campo de la encuesta y la evolución demográfica. La sección de limitaciones desarrolla este punto y detalla qué haría falta para acercarse a una identificación causal creíble.


2 Datos

2.1 Fuente y cobertura

La ENEMDU es la operación estadística oficial del Instituto Nacional de Estadística y Censos (INEC) para la medición del mercado laboral ecuatoriano. Se trata de una encuesta por muestreo probabilístico de hogares, con marco basado en el Marco Maestro de Muestreo, diseño estratificado bietápico y representatividad nacional, por área (urbana y rural) y para las ciudades autorrepresentadas.

Los microdatos se descargan directamente desde el portal del INEC a partir de las dos direcciones declaradas en los parámetros del documento. La descarga se almacena en una carpeta local de caché (cache_enemdu, relativa al directorio del proyecto) de modo que compilaciones sucesivas no repitan la transferencia. El documento detecta automáticamente la extensión del archivo de microdatos (.sav o .dta) y lo lee con el motor correspondiente de haven; dentro del ZIP se identifica la base de personas por el nombre del archivo, descartando las bases de vivienda y hogar.

tibble(
  Período = c(etiquetas_periodo[["Previo"]], etiquetas_periodo[["Posterior"]]),
  Dirección = c(params$url_pre, params$url_post)
) %>%
  gt() %>%
  tab_header(title = "Orígenes de los microdatos utilizados") %>%
  cols_align(align = "left") %>%
  tab_options(table.font.size = px(12), data_row.padding = px(5))
Orígenes de los microdatos utilizados
Período Dirección
Previo (2019-12) https://www.ecuadorencifras.gob.ec/documentos/web-inec/EMPLEO/2019/Diciembre/BDD_ENEMDU_2019_12_SPSS.zip
Posterior (2022-12) https://www.ecuadorencifras.gob.ec/documentos/web-inec/EMPLEO/2022/Diciembre_2022/1_BDD_ENEMDU_2022_12_SPSS.zip

2.2 Variables originales empleadas

Los nombres de las variables de la ENEMDU han cambiado entre versiones de la base. Para evitar supuestos frágiles, el documento resuelve cada concepto buscando por patrón sobre los nombres reales de cada archivo, previa normalización con janitor::clean_names(). La siguiente tabla documenta qué variable original se utilizó en cada período.

tibble(
  Concepto = names(vars_pre),
  `Período previo` = as.character(vars_pre),
  `Período posterior` = as.character(vars_post[names(vars_pre)])
) %>%
  gt() %>%
  tab_header(
    title = "Correspondencia entre conceptos y variables originales",
    subtitle = "Resolución automática por patrón sobre los nombres de cada base"
  ) %>%
  cols_align(align = "left") %>%
  tab_options(table.font.size = px(12), data_row.padding = px(5))
Correspondencia entre conceptos y variables originales
Resolución automática por patrón sobre los nombres de cada base
Concepto Período previo Período posterior
factor de expansión fexp fexp
conglomerado (UPM) upm upm
estrato estrato estrato
edad p03 p03
condición de actividad condact condact
sexo p02 p02
área area area

2.3 Definición de la condición de actividad

La clasificación sigue la definición oficial del INEC, basada en las recomendaciones de la Organización Internacional del Trabajo y operacionalizada en la variable derivada de condición de actividad que el propio Instituto incluye en la base de personas. Sobre ella se construyen los agregados del siguiente modo:

  • Población en edad de trabajar (PET): personas de 15 años o más, determinada a partir de la variable de edad.
  • Población económicamente activa (PEA): personas de la PET clasificadas como ocupadas (empleo adecuado, subempleo por insuficiencia de tiempo, subempleo por insuficiencia de ingresos, otro empleo no pleno, empleo no remunerado y empleo no clasificado) o como desocupadas (desempleo abierto y desempleo oculto).
  • Población económicamente inactiva (PEI): personas de la PET que no están ocupadas ni desocupadas.
  • Empleo adecuado: ocupados que perciben ingresos laborales iguales o superiores al salario mínimo y trabajan igual o más de la jornada legal, o que trabajando menos no desean trabajar horas adicionales.
  • Subempleo: ocupados que perciben ingresos inferiores al salario mínimo o trabajan menos de la jornada legal, y que además desean y están disponibles para trabajar horas adicionales.
  • Empleo no remunerado: ocupados que no perciben ingresos por su trabajo, categoría que agrupa principalmente a trabajadores del hogar no remunerados y trabajadores no remunerados en otros hogares.
  • Desempleo: personas de la PEA sin empleo que buscaron trabajo y estuvieron disponibles (desempleo abierto), o que no buscaron por razones de mercado pero estuvieron disponibles (desempleo oculto).

Las tasas se calculan con los denominadores oficiales: la tasa de participación global sobre la PET y las tasas de empleo adecuado, subempleo, otro empleo no pleno, empleo no remunerado y desempleo sobre la PEA.

resumen_muestra %>%
  mutate(periodo = recode(as.character(periodo), !!!etiquetas_periodo)) %>%
  gt() %>%
  tab_header(title = "Tamaño muestral y estructura del diseño por período") %>%
  cols_label(
    periodo = "Período", registros = "Registros", upm = "UPM",
    estratos = "Estratos", poblacion = "Población expandida",
    pet_n = "Registros PET", pea_n = "Registros PEA"
  ) %>%
  fmt_number(columns = c(registros, upm, estratos, pet_n, pea_n), decimals = 0, sep_mark = ".") %>%
  fmt_number(columns = poblacion, decimals = 0, sep_mark = ".") %>%
  tab_options(table.font.size = px(12), data_row.padding = px(5))
Tamaño muestral y estructura del diseño por período
Período Registros UPM Estratos Población expandida Registros PET Registros PEA
Previo (2019-12) 59.208 2.438 163 17.454.560 45.082 29.404
Posterior (2022-12) 28.993 1.288 150 18.183.483 22.637 14.633

3 Metodología

3.1 Declaración del diseño muestral

La ENEMDU no es una muestra aleatoria simple: combina estratificación geográfica, selección de unidades primarias de muestreo (UPM) dentro de cada estrato y ponderadores que corrigen probabilidades desiguales de selección y no respuesta. Ignorar esa estructura produce errores estándar sesgados a la baja y, por tanto, intervalos de confianza artificialmente estrechos y pruebas de hipótesis excesivamente liberales.

El diseño se declara con srvyr::as_survey_design() indicando el conglomerado como identificador de primera etapa, el estrato como variable de estratificación y el factor de expansión como ponderador, con anidamiento de las UPM dentro de los estratos. Como las dos rondas provienen de muestras independientes, los identificadores de estrato y UPM se hacen únicos por período antes de apilar las bases; de este modo el período queda absorbido dentro de la estructura de estratificación y las varianzas de cada ronda se estiman por separado, sin inducir correlaciones espurias entre conglomerados de rondas distintas. Los estratos con una sola UPM se tratan con la opción de ajuste centrada en la media global, que es la práctica habitual y conservadora en este contexto.

Todas las cifras reportadas —proporciones, errores estándar, intervalos y contrastes— se calculan sobre este objeto de diseño. No se emplean promedios ni proporciones simples en ningún punto del análisis.

3.2 Estimación por dominios

Las tasas laborales son estimaciones de dominio: la tasa de desempleo, por ejemplo, se define sobre la PEA y no sobre toda la muestra. El tratamiento correcto consiste en subconjuntar el objeto de diseño —no el data frame subyacente— de modo que las UPM y estratos del dominio conserven la información necesaria para la estimación de varianza. Eso es lo que hace el filtrado aplicado sobre el diseño para construir los dominios PET y PEA.

3.3 Contraste entre períodos

Para cada indicador se estima la diferencia entre el período posterior y el previo mediante survey::svyttest(), que ajusta un modelo lineal ponderado del indicador binario sobre el período y contrasta el coeficiente asociado usando la varianza del diseño. La diferencia estimada es, por construcción, la diferencia de proporciones entre ambos períodos; el intervalo de confianza y el valor p provienen de la distribución t con los grados de libertad del diseño, calculados como el número de UPM menos el número de estratos.

Adicionalmente se reporta la diferencia mínima detectable (DMD): el tamaño de efecto que el diseño habría detectado con una potencia del 80 % a un nivel de significación del 5 %, calculado como \((t_{0{,}975;\,gl} + t_{0{,}80;\,gl}) \times EE_{\text{diferencia}}\). Esta magnitud es imprescindible para interpretar correctamente un resultado no significativo: sin ella, la ausencia de evidencia no puede distinguirse de la falta de precisión.


4 Resultados

4.1 Tasas por período

estadisticos <- c("Tasa", "Error estándar", "IC 95 % inf", "IC 95 % sup")
orden_columnas <- c(
  "indicador", "denominador",
  as.vector(t(outer(unname(etiquetas_periodo), estadisticos, paste, sep = "__")))
)

estimaciones %>%
  mutate(periodo_etq = recode(as.character(periodo), !!!etiquetas_periodo)) %>%
  select(indicador, denominador, periodo_etq,
         Tasa = tasa, `Error estándar` = ee, `IC 95 % inf` = li, `IC 95 % sup` = ls) %>%
  pivot_wider(
    names_from = periodo_etq,
    values_from = all_of(estadisticos),
    names_glue = "{periodo_etq}__{.value}"
  ) %>%
  select(all_of(orden_columnas)) %>%
  gt() %>%
  tab_header(
    title = "Indicadores del mercado laboral por período",
    subtitle = "Estimaciones con diseño muestral complejo, error estándar e intervalo de confianza al 95 %"
  ) %>%
  cols_label(indicador = "Indicador", denominador = "Denominador") %>%
  tab_spanner_delim(delim = "__") %>%
  fmt_percent(columns = where(is.numeric), decimals = 2,
              dec_mark = ",", sep_mark = ".") %>%
  cols_align(align = "left", columns = c(indicador, denominador)) %>%
  tab_options(table.font.size = px(11), data_row.padding = px(4))
Indicadores del mercado laboral por período
Estimaciones con diseño muestral complejo, error estándar e intervalo de confianza al 95 %
Indicador Denominador
Previo (2019-12)
Posterior (2022-12)
Tasa Error estándar IC 95 % inf IC 95 % sup Tasa Error estándar IC 95 % inf IC 95 % sup
Tasa de participación global PET 65,30% 0,39% 64,54% 66,07% 64,58% 0,85% 62,91% 66,26%
Tasa de empleo adecuado PEA 38,85% 0,66% 37,56% 40,14% 36,04% 1,20% 33,69% 38,38%
Tasa de subempleo PEA 17,79% 0,49% 16,82% 18,76% 19,37% 0,87% 17,67% 21,07%
Tasa de otro empleo no pleno PEA 28,00% 0,43% 27,16% 28,85% 30,51% 0,92% 28,70% 32,32%
Tasa de empleo no remunerado PEA 10,92% 0,56% 9,83% 12,02% 10,70% 1,20% 8,35% 13,04%
Tasa de desempleo PEA 3,84% 0,23% 3,39% 4,30% 3,19% 0,25% 2,70% 3,69%

Los niveles estimados reproducen el orden de magnitud de las cifras oficiales publicadas por el INEC para cada ronda, lo que ofrece una verificación externa de que la construcción de variables y la declaración del diseño son correctas.

4.2 Comparación gráfica

estimaciones %>%
  ggplot(aes(x = tasa, y = fct_rev(indicador), colour = periodo)) +
  geom_linerange(aes(xmin = li, xmax = ls), linewidth = 1.1,
                 position = position_dodge(width = 0.55)) +
  geom_point(size = 2.8, position = position_dodge(width = 0.55)) +
  scale_colour_manual(values = paleta, labels = unname(etiquetas_periodo)) +
  scale_x_continuous(labels = label_percent(accuracy = 1)) +
  labs(
    title = "Indicadores del mercado laboral ecuatoriano",
    subtitle = "Estimación puntual e intervalo de confianza al 95 %",
    x = "Tasa estimada", y = NULL,
    caption = "Fuente: elaboración propia con microdatos ENEMDU (INEC). Estimaciones bajo diseño muestral complejo."
  )
Tasas laborales estimadas por período con intervalos de confianza al 95 % bajo diseño muestral complejo.

Tasas laborales estimadas por período con intervalos de confianza al 95 % bajo diseño muestral complejo.

composicion <- diseno_pea %>%
  filter(!is.na(categoria_pea)) %>%
  group_by(periodo, categoria_pea) %>%
  summarise(p = survey_mean(vartype = "ci")) %>%
  ungroup() %>%
  mutate(
    categoria_pea = fct_reorder(categoria_pea, p, .fun = max),
    periodo_etq = factor(recode(as.character(periodo), !!!etiquetas_periodo),
                         levels = rev(unname(etiquetas_periodo)))
  )

composicion %>%
  ggplot(aes(x = p, y = periodo_etq, fill = categoria_pea)) +
  geom_col(width = 0.55, colour = "white", linewidth = 0.3) +
  scale_x_continuous(labels = label_percent(accuracy = 1), expand = expansion(mult = c(0, 0.01))) +
  scale_fill_brewer(palette = "Set2", direction = -1) +
  labs(
    title = "Composición de la PEA por período",
    subtitle = "Participación de cada categoría de condición de actividad dentro de la PEA",
    x = "Participación en la PEA", y = NULL,
    caption = "Fuente: elaboración propia con microdatos ENEMDU (INEC)."
  ) +
  guides(fill = guide_legend(nrow = 2, reverse = TRUE))
Composición de la población económicamente activa según categoría de condición de actividad.

Composición de la población económicamente activa según categoría de condición de actividad.

4.3 Contraste de diferencias

contrastes %>%
  select(indicador, diferencia, ee, li, ls, gl, valor_p, dme) %>%
  gt() %>%
  tab_header(
    title = "Diferencia entre el período posterior y el previo",
    subtitle = "Prueba t con varianza del diseño muestral complejo"
  ) %>%
  cols_label(
    indicador = "Indicador", diferencia = "Diferencia (p.p.)", ee = "Error estándar (p.p.)",
    li = "IC 95 % inferior", ls = "IC 95 % superior", gl = "Grados de libertad",
    valor_p = "Valor p", dme = "Diferencia mínima detectable (p.p.)"
  ) %>%
  fmt(columns = c(diferencia, ee, li, ls, dme),
      fns = function(x) fmt_n(x * 100, 2)) %>%
  fmt_number(columns = gl, decimals = 0, sep_mark = ".") %>%
  fmt(columns = valor_p, fns = fmt_p) %>%
  cols_align(align = "left", columns = indicador) %>%
  tab_options(table.font.size = px(11), data_row.padding = px(4))
Diferencia entre el período posterior y el previo
Prueba t con varianza del diseño muestral complejo
Indicador Diferencia (p.p.) Error estándar (p.p.) IC 95 % inferior IC 95 % superior Grados de libertad Valor p Diferencia mínima detectable (p.p.)
Tasa de participación global -0,72 0,94 -2,56 1,12 3.412 0,444 2,63
Tasa de empleo adecuado -2,81 1,37 -5,49 -0,13 3.412 0,040 3,83
Tasa de subempleo 1,58 1,00 -0,38 3,53 3.412 0,113 2,79
Tasa de otro empleo no pleno 2,50 1,02 0,51 4,50 3.412 0,014 2,85
Tasa de empleo no remunerado -0,23 1,32 -2,82 2,36 3.412 0,864 3,70
Tasa de desempleo -0,65 0,34 -1,32 0,02 3.412 0,058 0,96
contrastes %>%
  ggplot(aes(x = diferencia, y = fct_rev(indicador))) +
  geom_vline(xintercept = 0, linetype = "dashed", colour = "grey40") +
  geom_linerange(aes(xmin = li, xmax = ls), linewidth = 1.1, colour = "#2C6E9B") +
  geom_point(size = 2.8, colour = "#2C6E9B") +
  scale_x_continuous(labels = label_percent(accuracy = 0.1)) +
  labs(
    title = "Diferencia entre el período posterior y el previo",
    subtitle = "Estimación puntual e intervalo de confianza al 95 % (puntos porcentuales)",
    x = "Diferencia", y = NULL,
    caption = "Fuente: elaboración propia con microdatos ENEMDU (INEC)."
  )
Diferencias estimadas entre períodos con intervalos de confianza al 95 %. La línea vertical marca la ausencia de diferencia.

Diferencias estimadas entre períodos con intervalos de confianza al 95 %. La línea vertical marca la ausencia de diferencia.

n_sig <- sum(contrastes$valor_p < 0.05)
n_tot <- nrow(contrastes)
sig_txt <- if (n_sig == 0) {
  "ninguno de los indicadores presenta una diferencia estadísticamente significativa al 5 %"
} else {
  paste0(n_sig, " de ", n_tot, " indicadores presentan una diferencia estadísticamente significativa al 5 % (",
         paste(as.character(contrastes$indicador[contrastes$valor_p < 0.05]), collapse = "; "), ")")
}
dme_media <- mean(contrastes$dme) * 100
cat(paste0(
  "Bajo el diseño declarado y con el nivel de significación convencional, ", sig_txt,
  ". La diferencia mínima detectable promedio del conjunto de contrastes es de aproximadamente ",
  fmt_n(dme_media, 2), " puntos porcentuales, con un rango entre ",
  fmt_n(min(contrastes$dme) * 100, 2), " y ", fmt_n(max(contrastes$dme) * 100, 2),
  " puntos porcentuales según el indicador."
))

Bajo el diseño declarado y con el nivel de significación convencional, 2 de 6 indicadores presentan una diferencia estadísticamente significativa al 5 % (Tasa de empleo adecuado; Tasa de otro empleo no pleno). La diferencia mínima detectable promedio del conjunto de contrastes es de aproximadamente 2,79 puntos porcentuales, con un rango entre 0,96 y 3,83 puntos porcentuales según el indicador.


5 Discusión

5.1 Lectura de los contrastes

sentido <- function(x) if (x > 0) "al alza" else "a la baja"

descr_ind <- function(fila) {
  paste0(
    "**", as.character(fila$indicador), "**: ", fmt_n(abs(fila$diferencia) * 100, 2),
    " p.p. ", sentido(fila$diferencia),
    " (IC 95 %: ", fmt_n(fila$li * 100, 2), " a ", fmt_n(fila$ls * 100, 2),
    " p.p.; p = ", fmt_p(fila$valor_p), ")"
  )
}

sig <- contrastes %>% filter(valor_p < 0.05) %>% arrange(desc(abs(diferencia)))
nosig <- contrastes %>% filter(valor_p >= 0.05) %>% arrange(desc(abs(diferencia)))

parrafos <- character(0)

if (nrow(sig) > 0) {
  parrafos <- c(parrafos, paste0(
    "Los indicadores cuyo cambio entre períodos resulta incompatible con la hipótesis nula al 5 % son: ",
    paste(purrr::map_chr(seq_len(nrow(sig)), ~ descr_ind(sig[.x, ])), collapse = "; "),
    ". En estos casos el intervalo de confianza excluye el cero, de modo que los datos sí permiten ",
    "descartar la igualdad entre períodos. Conviene separar dos afirmaciones distintas: que la ",
    "diferencia es estadísticamente detectable, que es lo que muestra el contraste, y que la ",
    "diferencia sea atribuible al cambio regulatorio, que el diseño empleado no permite sostener."
  ))
}

if (nrow(nosig) > 0) {
  parrafos <- c(parrafos, paste0(
    "Los indicadores para los que no se rechaza la hipótesis nula al 5 % son: ",
    paste(purrr::map_chr(seq_len(nrow(nosig)), ~ descr_ind(nosig[.x, ])), collapse = "; "),
    ". Para estos, el intervalo de confianza contiene el cero, lo que no equivale a haber ",
    "demostrado ausencia de cambio."
  ))
}

if (nrow(sig) > 0 && nrow(nosig) > 0) {
  parrafos <- c(parrafos, paste0(
    "El patrón conjunto es informativo por sí mismo: el volumen de la fuerza de trabajo se mantiene ",
    "relativamente estable mientras la composición interna del empleo se desplaza. Es decir, el ",
    "movimiento no se produce tanto en el margen de entrada y salida del mercado laboral como en la ",
    "calidad de la ocupación de quienes ya estaban dentro."
  ))
}

cat(paste(parrafos, collapse = "\n\n"))

Los indicadores cuyo cambio entre períodos resulta incompatible con la hipótesis nula al 5 % son: Tasa de empleo adecuado: 2,81 p.p. a la baja (IC 95 %: -5,49 a -0,13 p.p.; p = 0,040); Tasa de otro empleo no pleno: 2,50 p.p. al alza (IC 95 %: 0,51 a 4,50 p.p.; p = 0,014). En estos casos el intervalo de confianza excluye el cero, de modo que los datos sí permiten descartar la igualdad entre períodos. Conviene separar dos afirmaciones distintas: que la diferencia es estadísticamente detectable, que es lo que muestra el contraste, y que la diferencia sea atribuible al cambio regulatorio, que el diseño empleado no permite sostener.

Los indicadores para los que no se rechaza la hipótesis nula al 5 % son: Tasa de subempleo: 1,58 p.p. al alza (IC 95 %: -0,38 a 3,53 p.p.; p = 0,113); Tasa de participación global: 0,72 p.p. a la baja (IC 95 %: -2,56 a 1,12 p.p.; p = 0,444); Tasa de desempleo: 0,65 p.p. a la baja (IC 95 %: -1,32 a 0,02 p.p.; p = 0,058); Tasa de empleo no remunerado: 0,23 p.p. a la baja (IC 95 %: -2,82 a 2,36 p.p.; p = 0,864). Para estos, el intervalo de confianza contiene el cero, lo que no equivale a haber demostrado ausencia de cambio.

El patrón conjunto es informativo por sí mismo: el volumen de la fuerza de trabajo se mantiene relativamente estable mientras la composición interna del empleo se desplaza. Es decir, el movimiento no se produce tanto en el margen de entrada y salida del mercado laboral como en la calidad de la ocupación de quienes ya estaban dentro.

5.2 Qué dice y qué no dice un resultado no significativo

Cuando el intervalo de confianza de la diferencia contiene el cero, la lectura correcta es que los datos son compatibles con la hipótesis nula de igualdad entre períodos, no que la igualdad haya quedado demostrada. La prueba de hipótesis frecuentista es asimétrica: permite acumular evidencia en contra de la nula, nunca a favor de ella. No rechazar significa únicamente que la evidencia disponible resulta insuficiente para descartar que la diferencia poblacional sea cero; la misma evidencia es también compatible con diferencias pequeñas pero reales, y con cualquier valor contenido dentro del intervalo.

Por eso el objeto informativo relevante no es el valor p sino el intervalo de confianza. Un intervalo que se extiende, por ejemplo, entre −1,5 y +1,2 puntos porcentuales dice algo sustantivo: efectos de más de un punto y medio en cualquier dirección son improbables a la luz de los datos. Un intervalo que se extiende entre −6 y +5 puntos porcentuales, en cambio, es prácticamente ininformativo: no descarta efectos de magnitud económica considerable. Los intervalos reportados en la tabla de contrastes deben leerse con ese criterio, indicador por indicador.

La simetría del argumento también obliga a moderar la lectura en sentido contrario. Un valor p pequeño indica que los datos son poco probables bajo la hipótesis nula, pero no mide la magnitud del efecto ni su relevancia sustantiva, y menos aún su origen causal. Con muestras grandes, como las que resultan de acumular rondas de la ENEMDU, diferencias de magnitud modesta alcanzan significación estadística con facilidad; la pregunta pertinente pasa entonces a ser si el tamaño estimado es económicamente relevante, no si el valor p cruza un umbral convencional.

5.3 Potencia y tamaño de efecto detectable

La diferencia mínima detectable cuantifica la capacidad discriminatoria del ejercicio. Con la precisión alcanzada, el análisis habría detectado con probabilidad del 80 % diferencias del orden de la magnitud reportada en la última columna de la tabla de contrastes. Efectos menores que ese umbral tienen alta probabilidad de pasar inadvertidos aunque existan: en esos casos, el no rechazo es un resultado esperable incluso bajo un efecto real no nulo.

Esta distinción importa para la lectura de política pública. Las regulaciones laborales de emergencia se diseñaron para operar sobre márgenes específicos —preservación del vínculo laboral mediante reducción de jornada, flexibilización de la contratación en sectores afectados— cuyos efectos agregados plausibles sobre tasas nacionales son de pocos puntos porcentuales o menos. Si la diferencia mínima detectable del ejercicio es del mismo orden o mayor que el efecto teóricamente esperable, el diseño no tiene resolución suficiente para pronunciarse, y concluir “no hubo efecto” sería una inferencia injustificada.

tam_max <- max(resumen_muestra$registros)
tam_min <- min(resumen_muestra$registros)
razon <- tam_max / tam_min

cat(paste0(
  "En este ejercicio la diferencia mínima detectable se sitúa entre ",
  fmt_n(min(contrastes$dme) * 100, 2), " y ", fmt_n(max(contrastes$dme) * 100, 2),
  " puntos porcentuales según el indicador. La precisión no es simétrica entre períodos: ",
  "la ronda con mayor número de registros aporta errores estándar sensiblemente menores, y la ",
  "varianza de la diferencia queda dominada por la ronda más pequeña. ",
  if (razon >= 3) paste0(
    "La razón entre los tamaños muestrales de ambas rondas es de aproximadamente ",
    fmt_n(razon, 1), " a 1, lo que indica que se están comparando rondas de distinta naturaleza ",
    "—por ejemplo, una ronda mensual frente a una base acumulada—. Esa asimetría no invalida el ",
    "contraste, pero introduce diferencias de cobertura temporal y de construcción de ponderadores ",
    "que se suman a cualquier efecto sustantivo."
  ) else ""
))

En este ejercicio la diferencia mínima detectable se sitúa entre 0,96 y 3,83 puntos porcentuales según el indicador. La precisión no es simétrica entre períodos: la ronda con mayor número de registros aporta errores estándar sensiblemente menores, y la varianza de la diferencia queda dominada por la ronda más pequeña.

limite <- contrastes %>% filter(valor_p < 0.05, abs(diferencia) < dme)
anchos <- contrastes %>% filter(valor_p >= 0.05, dme > 0.03)

if (nrow(limite) > 0) {
  cat(paste0(
    "\n\nUn matiz que conviene explicitar: en ",
    paste(as.character(limite$indicador), collapse = " y en "),
    " la diferencia estimada es menor que la propia diferencia mínima detectable. ",
    "Ambas cosas son compatibles —el umbral corresponde a una potencia del 80 %, no a un mínimo ",
    "por debajo del cual el rechazo sea imposible—, pero implican que el rechazo se produce en el ",
    "límite de la resolución del diseño. Un estudio con esta precisión tenía menos de 80 % de ",
    "probabilidad de detectar un efecto de la magnitud que efectivamente estimó, de modo que la ",
    "estimación puntual es frágil: conviene tratarla como evidencia de que hubo movimiento, no ",
    "como una medición fiable de su tamaño."
  ))
}

Un matiz que conviene explicitar: en Tasa de empleo adecuado y en Tasa de otro empleo no pleno la diferencia estimada es menor que la propia diferencia mínima detectable. Ambas cosas son compatibles —el umbral corresponde a una potencia del 80 %, no a un mínimo por debajo del cual el rechazo sea imposible—, pero implican que el rechazo se produce en el límite de la resolución del diseño. Un estudio con esta precisión tenía menos de 80 % de probabilidad de detectar un efecto de la magnitud que efectivamente estimó, de modo que la estimación puntual es frágil: conviene tratarla como evidencia de que hubo movimiento, no como una medición fiable de su tamaño.

if (nrow(anchos) > 0) {
  cat(paste0(
    "\n\nEn el otro extremo, para ",
    paste(as.character(anchos$indicador), collapse = ", "),
    " el intervalo de confianza es lo bastante ancho como para no descartar efectos de magnitud ",
    "económicamente relevante. En estos casos el no rechazo refleja falta de precisión antes que ",
    "evidencia de estabilidad, y no debe leerse como ausencia de cambio."
  ))
}

En el otro extremo, para Tasa de empleo no remunerado el intervalo de confianza es lo bastante ancho como para no descartar efectos de magnitud económicamente relevante. En estos casos el no rechazo refleja falta de precisión antes que evidencia de estabilidad, y no debe leerse como ausencia de cambio.

Existen tres caminos para ganar potencia sin cambiar la pregunta. El primero es acumular rondas: las bases trimestrales y anuales de la ENEMDU multiplican el tamaño muestral efectivo respecto de una ronda mensual. El segundo es restringir el análisis a los subgrupos donde el efecto esperado es mayor —por rama de actividad, tamaño de establecimiento o formalidad— aceptando el costo de una menor precisión por celda y el riesgo de comparaciones múltiples. El tercero es reemplazar el contraste de medias por un modelo que absorba variación mediante covariables, lo que reduce la varianza residual sin alterar la estimación de interés.

5.4 Interpretación sustantiva

hay_sig <- nrow(sig) > 0

texto_apertura <- if (!hay_sig) {
  paste0(
    "Al nivel de agregación nacional y con la resolución disponible, la estructura de la condición ",
    "de actividad no muestra un desplazamiento detectable entre ambos períodos."
  )
} else if (nrow(nosig) == 0) {
  paste0(
    "Todos los indicadores examinados se desplazan de forma estadísticamente detectable entre ambos ",
    "períodos, lo que describe un mercado laboral cuya estructura no es la misma al inicio y al ",
    "final del intervalo considerado."
  )
} else {
  paste0(
    "El cuadro que emerge es mixto: parte de los indicadores se desplaza de forma estadísticamente ",
    "detectable y parte no. La estructura de la ocupación cambia, mientras que los márgenes ligados ",
    "a la participación en la fuerza de trabajo se mantienen dentro de los límites de la ",
    "incertidumbre muestral."
  )
}

cat(paste0(
  texto_apertura, " Lo que el ejercicio no puede hacer, en ningún escenario, es atribuir ese ",
  "resultado a las regulaciones laborales de emergencia. Las diferencias estimadas son compatibles ",
  "con varias historias distintas y mutuamente no excluyentes: que el efecto neto de la regulación ",
  "haya sido de la magnitud observada; que el desplazamiento provenga del shock sanitario y de la ",
  "contracción económica, con la regulación jugando un papel menor o incluso amortiguador; que ",
  "efectos de signo opuesto se cancelen parcialmente en el agregado —por ejemplo, preservación de ",
  "empleos formales acompañada de deterioro en su calidad—; que parte del movimiento corresponda a ",
  "los cambios metodológicos del operativo de campo entre 2020 y 2021; o que el efecto se concentre ",
  "en márgenes que las tasas de condición de actividad no capturan, como horas trabajadas, ",
  "modalidad contractual o ingresos laborales reales.\n\n",
  "Ninguna de esas explicaciones puede descartarse ni confirmarse con la evidencia presentada. ",
  "El ejercicio establece con solidez *qué* cambió y con cuánta precisión se estima ese cambio; ",
  "no establece *por qué* cambió, y presentar lo segundo como si se derivara de lo primero sería ",
  "el error de lectura más costoso que este informe podría inducir."
))

El cuadro que emerge es mixto: parte de los indicadores se desplaza de forma estadísticamente detectable y parte no. La estructura de la ocupación cambia, mientras que los márgenes ligados a la participación en la fuerza de trabajo se mantienen dentro de los límites de la incertidumbre muestral. Lo que el ejercicio no puede hacer, en ningún escenario, es atribuir ese resultado a las regulaciones laborales de emergencia. Las diferencias estimadas son compatibles con varias historias distintas y mutuamente no excluyentes: que el efecto neto de la regulación haya sido de la magnitud observada; que el desplazamiento provenga del shock sanitario y de la contracción económica, con la regulación jugando un papel menor o incluso amortiguador; que efectos de signo opuesto se cancelen parcialmente en el agregado —por ejemplo, preservación de empleos formales acompañada de deterioro en su calidad—; que parte del movimiento corresponda a los cambios metodológicos del operativo de campo entre 2020 y 2021; o que el efecto se concentre en márgenes que las tasas de condición de actividad no capturan, como horas trabajadas, modalidad contractual o ingresos laborales reales.

Ninguna de esas explicaciones puede descartarse ni confirmarse con la evidencia presentada. El ejercicio establece con solidez qué cambió y con cuánta precisión se estima ese cambio; no establece por qué cambió, y presentar lo segundo como si se derivara de lo primero sería el error de lectura más costoso que este informe podría inducir.


6 Limitaciones

6.1 La comparación antes-después no identifica causalidad

El diseño empleado es una comparación de dos cortes transversales separados en el tiempo. Su validez causal descansa en un supuesto que es, en este contexto, manifiestamente falso: que en ausencia del cambio regulatorio los indicadores del período posterior habrían sido iguales a los del período previo. Entre ambos momentos ocurrieron, de manera simultánea e inseparable:

  1. El shock sanitario y sus restricciones: confinamientos, cierre de establecimientos, restricciones a la movilidad y contracción de la demanda agregada, todos con efectos directos sobre el empleo e independientes del contenido de la norma laboral.
  2. La contracción y posterior recuperación macroeconómica, con caída del producto, ajuste fiscal y variación del precio del petróleo, factores que operan sobre el mercado laboral por canales ajenos a la regulación.
  3. Cambios en el operativo estadístico: durante buena parte de 2020 la ENEMDU se levantó por vía telefónica y luego bajo modalidad mixta, con modificaciones en el tamaño y distribución de la muestra y en la construcción de los factores de ponderación. El propio INEC advierte que estos cambios afectan la comparabilidad histórica y motivaron un recálculo de indicadores. Una parte de cualquier diferencia observada puede ser de origen metodológico y no sustantivo.
  4. Cambios demográficos y migratorios, incluidos los flujos migratorios regionales, que alteran la composición de la población en edad de trabajar.
  5. Otras modificaciones normativas y de política, en materia tributaria, de seguridad social y de subsidios, adoptadas en el mismo período.

Cualquier diferencia estimada es la suma de todos estos componentes más el efecto de la regulación laboral. El ejercicio no ofrece manera de separarlos.

6.2 Limitaciones adicionales

  • Solapamiento muestral: la ENEMDU tiene un esquema de rotación panel. Según la distancia temporal entre las rondas comparadas, una fracción de las viviendas puede repetirse, lo que vulnera el supuesto de independencia entre muestras que asume el contraste aplicado y tiende a producir errores estándar conservadores o anticonservadores según el signo de la correlación intertemporal. Un tratamiento riguroso exigiría emparejar los registros y aplicar un contraste para muestras dependientes.
  • Dominio de análisis: el ejercicio se realiza a nivel nacional agregado. La regulación de emergencia tuvo alcance heterogéneo por sector, tamaño de empresa y grado de formalidad, y el agregado nacional promedia y diluye esas diferencias.
  • Variables no observadas: la condición de actividad no captura dimensiones sobre las que la regulación operaba de manera directa, como la duración de la jornada efectiva, la modalidad contractual, la antigüedad en el puesto o el nivel de los ingresos laborales.
mes_de <- function(etq) {
  partes <- strsplit(etq, "-", fixed = TRUE)[[1]]
  if (length(partes) == 2) as.integer(partes[2]) else NA_integer_
}
mes_pre <- mes_de(etq_pre)
mes_post <- mes_de(etq_post)
mismo_mes <- !is.na(mes_pre) && !is.na(mes_post) && mes_pre == mes_post

cat(paste0(
  "\n- **Comparabilidad de las rondas**: comparar rondas de distinta naturaleza —mensual frente a ",
  "acumulada trimestral o anual— implica diferencias en tamaño muestral, cobertura temporal y ",
  "construcción de ponderadores que se suman a cualquier efecto sustantivo. ",
  if (razon >= 3) paste0(
    "En la compilación actual los tamaños muestrales difieren en una razón de aproximadamente ",
    fmt_n(razon, 1), " a 1, lo que sugiere que las rondas seleccionadas no son del mismo tipo. ",
    "Se recomienda repetir el ejercicio con rondas homólogas."
  ) else paste0(
    "En la compilación actual los tamaños muestrales de ambas rondas son del mismo orden, ",
    "lo que indica que se están comparando rondas del mismo tipo."
  ),
  "\n",
  "- **Estacionalidad**: los indicadores laborales ecuatorianos presentan patrones estacionales ",
  "marcados, en particular por el empleo temporal de fin de año. ",
  if (mismo_mes) paste0(
    "En la compilación actual ambas rondas corresponden al mismo mes del año, de modo que la ",
    "estacionalidad queda controlada por construcción y no explica las diferencias estimadas."
  ) else paste0(
    "En la compilación actual las rondas no corresponden al mismo mes del año, de modo que las ",
    "diferencias estimadas confunden el efecto de interés con el componente estacional."
  )
))
  • Comparabilidad de las rondas: comparar rondas de distinta naturaleza —mensual frente a acumulada trimestral o anual— implica diferencias en tamaño muestral, cobertura temporal y construcción de ponderadores que se suman a cualquier efecto sustantivo. En la compilación actual los tamaños muestrales de ambas rondas son del mismo orden, lo que indica que se están comparando rondas del mismo tipo.
  • Estacionalidad: los indicadores laborales ecuatorianos presentan patrones estacionales marcados, en particular por el empleo temporal de fin de año. En la compilación actual ambas rondas corresponden al mismo mes del año, de modo que la estacionalidad queda controlada por construcción y no explica las diferencias estimadas.

6.3 Qué haría falta para acercarse a una identificación causal

Ninguna estrategia disponible con datos de encuesta de hogares resuelve el problema por completo, pero varias lo mejoran sustancialmente:

  • Diferencias en diferencias con grupo de comparación: aprovechar que la regulación de emergencia aplicaba con distinta intensidad según el sector o el régimen contractual, y comparar la trayectoria de los grupos más expuestos con la de los menos expuestos, verificando previamente el supuesto de tendencias paralelas sobre varios períodos anteriores a la norma.
  • Control sintético: construir una combinación ponderada de unidades de comparación —países de la región, o provincias con distinta exposición— que reproduzca la trayectoria pre-tratamiento del Ecuador, y contrastar contra ella la evolución posterior.
  • Discontinuidad en la regla de aplicación: si la norma establecía umbrales —de tamaño de empresa, de caída de ingresos, de sector— comparar unidades apenas por encima y por debajo del umbral permite una identificación local creíble.
  • Datos longitudinales o registros administrativos: la matriz de transición laboral de la propia ENEMDU y, sobre todo, los registros de afiliación al Instituto Ecuatoriano de Seguridad Social permiten seguir a las mismas personas y empresas en el tiempo, observar transiciones y no únicamente saldos, y medir directamente el uso de las figuras contractuales creadas por la norma.
  • Series de mayor frecuencia con modelo de intervención: modelar la serie mensual de los indicadores con un componente de intervención en la fecha de vigencia de la norma, controlando estacionalidad y tendencia, permite al menos separar el salto puntual asociado a la entrada en vigor del movimiento tendencial.

Cada una de estas alternativas requiere supuestos propios, y ninguna es automáticamente superior. Lo que sí es claro es que la comparación de dos medias entre dos cortes transversales, aun ejecutada correctamente desde el punto de vista de la inferencia por muestreo, no constituye evidencia causal sobre el efecto de la regulación.


7 Referencias

  • Instituto Nacional de Estadística y Censos. Metodología de la Encuesta Nacional de Empleo, Desempleo y Subempleo (ENEMDU). Quito: INEC.
  • Instituto Nacional de Estadística y Censos. Guía de uso de la base de datos ENEMDU. Quito: INEC.
  • Instituto Nacional de Estadística y Censos. Cálculo de errores estándar y declaración de muestras complejas de la ENEMDU. Quito: INEC.
  • Instituto Nacional de Estadística y Censos. Empleo según condición de actividad: actualización metodológica. Quito: INEC.
  • Organización Internacional del Trabajo. Resoluciones de la 19.ª Conferencia Internacional de Estadísticos del Trabajo sobre estadísticas del trabajo, la ocupación y la subutilización de la fuerza de trabajo. Ginebra: OIT, 2013.
  • Lumley, T. Complex Surveys: A Guide to Analysis Using R. Hoboken: Wiley, 2010.
  • Lumley, T. survey: Analysis of Complex Survey Samples. Paquete de R.
  • Freedman Ellis, G. y Schneider, B. srvyr: ‘dplyr’-Like Syntax for Summary Statistics of Survey Data. Paquete de R.
  • Wasserstein, R. L. y Lazar, N. A. “The ASA Statement on p-Values: Context, Process, and Purpose”. The American Statistician, 70(2), 2016.
  • Greenland, S. et al. “Statistical tests, P values, confidence intervals, and power: a guide to misinterpretations”. European Journal of Epidemiology, 31(4), 2016.
  • Angrist, J. D. y Pischke, J.-S. Mostly Harmless Econometrics: An Empiricist’s Companion. Princeton: Princeton University Press, 2009.

8 Información de la sesión

sessionInfo()
#> R version 4.4.2 (2024-10-31 ucrt)
#> Platform: x86_64-w64-mingw32/x64
#> Running under: Windows 11 x64 (build 26100)
#> 
#> Matrix products: default
#> 
#> 
#> locale:
#> [1] LC_COLLATE=Spanish_Ecuador.utf8  LC_CTYPE=Spanish_Ecuador.utf8   
#> [3] LC_MONETARY=Spanish_Ecuador.utf8 LC_NUMERIC=C                    
#> [5] LC_TIME=Spanish_Ecuador.utf8    
#> 
#> time zone: America/Guayaquil
#> tzcode source: internal
#> 
#> attached base packages:
#> [1] grid      stats     graphics  grDevices utils     datasets  methods  
#> [8] base     
#> 
#> other attached packages:
#>  [1] here_1.0.1      knitr_1.50      gt_1.3.0        scales_1.4.0   
#>  [5] janitor_2.2.1   srvyr_1.3.1     survey_4.4-2    survival_3.7-0 
#>  [9] Matrix_1.7-1    haven_2.5.4     lubridate_1.9.4 forcats_1.0.0  
#> [13] stringr_1.5.1   dplyr_1.1.4     purrr_1.0.4     readr_2.1.5    
#> [17] tidyr_1.3.1     tibble_3.2.1    ggplot2_4.0.3   tidyverse_2.0.0
#> 
#> loaded via a namespace (and not attached):
#>  [1] sass_0.4.9         generics_0.1.3     xml2_1.3.8         stringi_1.8.4     
#>  [5] lattice_0.22-6     hms_1.1.3          digest_0.6.37      magrittr_2.0.3    
#>  [9] evaluate_1.0.3     timechange_0.3.0   RColorBrewer_1.1-3 fastmap_1.2.0     
#> [13] rprojroot_2.0.4    jsonlite_1.8.9     DBI_1.2.3          jquerylib_0.1.4   
#> [17] cli_3.6.3          mitools_2.4        rlang_1.1.4        splines_4.4.2     
#> [21] withr_3.0.2        cachem_1.1.0       yaml_2.3.10        tools_4.4.2       
#> [25] tzdb_0.4.0         vctrs_0.6.5        R6_2.6.1           lifecycle_1.0.4   
#> [29] snakecase_0.11.1   fs_1.6.5           pkgconfig_2.0.3    pillar_1.10.2     
#> [33] bslib_0.9.0        gtable_0.3.6       glue_1.8.0         Rcpp_1.0.14       
#> [37] xfun_0.52          tidyselect_1.2.1   rstudioapi_0.18.0  farver_2.1.2      
#> [41] htmltools_0.5.8.1  labeling_0.4.3     rmarkdown_2.30     compiler_4.4.2    
#> [45] S7_0.2.0