Los diseños muestrales complejos requieren un tratamiento especial
para la estimación correcta de la varianza. metasurvey gestiona el
diseño muestral de forma automática mediante el objeto
Survey, de modo que el usuario pueda concentrarse en el
análisis y no en los detalles técnicos.
Esta viñeta aborda los siguientes temas:
surveyUtilizamos el conjunto de datos Academic Performance Index (API) del
paquete survey. Incluye versiones estratificadas, por
conglomerados y de muestreo aleatorio simple.
El diseño más sencillo utiliza ponderadores de probabilidad sin conglomerados ni estratificación:
Muchas encuestas nacionales utilizan muestreo estratificado
multietápico. Se pasan strata y psu a
Survey$new():
dt_clus <- data.table(apiclus1)
svy_strat_clus <- Survey$new(
data = dt_strat,
edition = "2000",
type = "api",
psu = NULL,
strata = "stype",
engine = "data.table",
weight = add_weight(annual = "pw")
)
cat_design(svy_strat_clus)
#> [1] "\n Design: Not initialized (lazy initialization - will be created when needed)\n"Validación cruzada con el paquete survey:
design_strat <- svydesign(
id = ~1, strata = ~stype, weights = ~pw,
data = dt_strat
)
direct_strat <- svymean(~api00, design_strat)
wf_strat <- workflow(
list(svy_strat_clus),
survey::svymean(~api00, na.rm = TRUE),
estimation_type = "annual"
)
cat("Direct estimate:", round(coef(direct_strat), 2), "\n")
#> Direct estimate: 662.29
cat("Workflow estimate:", round(wf_strat$value, 2), "\n")
#> Workflow estimate: 662.29
cat("Match:", all.equal(
as.numeric(coef(direct_strat)),
wf_strat$value,
tolerance = 1e-6
), "\n")
#> Match: TRUEPara ejemplos reales de diseños estratificados por conglomerados con datos de CASEN, PNADc, ENIGH y DHS, ver el artículo de encuestas internacionales.
# Check design type
cat_design_type(svy_simple, "annual")
#> [1] "None"
# View metadata
get_metadata(svy_simple)
#> Type: API
#> Edition: 2000
#> Periodicity: Annual
#> Engine: data.table
#> Design:
#> Design: Not initialized (lazy initialization - will be created when needed)
#>
#> Steps: None
#> Recipes: NoneMuchas encuestas proporcionan distintos ponderadores según el período de análisis (por ejemplo, anual vs. mensual). metasurvey asocia etiquetas de periodicidad a columnas de ponderadores:
set.seed(42)
dt_multi <- copy(dt_strat)
dt_multi[, pw_monthly := pw * runif(.N, 0.9, 1.1)]
svy_multi <- Survey$new(
data = dt_multi,
edition = "2000",
type = "api",
psu = NULL,
engine = "data.table",
weight = add_weight(annual = "pw", monthly = "pw_monthly")
)
# Use different weight types in workflow()
annual_est <- workflow(
list(svy_multi),
survey::svymean(~api00, na.rm = TRUE),
estimation_type = "annual"
)
monthly_est <- workflow(
list(svy_multi),
survey::svymean(~api00, na.rm = TRUE),
estimation_type = "monthly"
)
cat("Annual estimate:", round(annual_est$value, 1), "\n")
#> Annual estimate: 662.3
cat("Monthly estimate:", round(monthly_est$value, 1), "\n")
#> Monthly estimate: 662.5Para encuestas que proporcionan réplicas bootstrap (como la ECH de
Uruguay), se utiliza add_replicate() dentro de
add_weight():
# This example requires external files
svy_boot <- load_survey(
path = "data/main_survey.csv",
svy_type = "ech",
svy_edition = "2023",
svy_weight = add_weight(
annual = add_replicate(
weight_var = "pesoano",
replicate_path = "data/bootstrap_replicates.csv",
replicate_id = c("numero" = "id"),
replicate_pattern = "bsrep[0-9]+",
replicate_type = "bootstrap"
)
)
)Cuando se configuran ponderadores de réplicas,
workflow() los utiliza automáticamente para la estimación
de varianza mediante survey::svrepdesign().
metasurvey utiliza data.table por defecto para una
manipulación rápida de los datos:
step_compute(), step_recode() y
step_join() se ejecutan de forma eager al crearse;
step_filter() y step_validate() quedan siempre
diferidos hasta bake_steps(), porque el filtrado afecta el
diseño muestral. La configuración de procesamiento diferido solo
controla step_rename() y step_remove()
(diferidos por defecto):
Estimación estándar de la varianza a partir del diseño muestral:
results <- workflow(
list(svy_simple),
survey::svymean(~api00, na.rm = TRUE),
survey::svytotal(~enroll, na.rm = TRUE),
estimation_type = "annual"
)
results
#> stat variable value se cv
#> <char> <char> <num> <num> <num>
#> 1: survey::svymean: api00 api00 662.2874 9.585429e+00 0.01447322
#> 2: survey::svytotal: enroll enroll 3687177.5324 1.645323e+05 0.04462283
#> confint_lower confint_upper evaluate
#> <num> <num> <char>
#> 1: 643.5003 681.0745 Excellent
#> 2: 3364700.1537 4009654.9112 ExcellentSe pueden calcular estimaciones para subpoblaciones con
survey::svyby():
domain_results <- workflow(
list(svy_simple),
survey::svyby(~api00, ~stype, survey::svymean, na.rm = TRUE),
estimation_type = "annual"
)
domain_results
#> stat variable value se cv confint_lower
#> <char> <char> <num> <num> <num> <num>
#> 1: survey::svyby: api00 api00 674.43 12.49343 0.01852443 649.9433
#> 2: survey::svyby: api00 api00 625.82 15.34078 0.02451309 595.7526
#> 3: survey::svyby: api00 api00 636.60 16.50239 0.02592270 604.2559
#> confint_upper evaluate stype
#> <num> <char> <fctr>
#> 1: 698.9167 Excellent E
#> 2: 655.8874 Excellent H
#> 3: 668.9441 Excellent Mratio_result <- workflow(
list(svy_simple),
survey::svyratio(~api00, ~api99),
estimation_type = "annual"
)
ratio_result
#> stat variable denominator value se
#> <char> <char> <char> <num> <num>
#> 1: survey::svyratio: api00/api99 api00 api99 1.052261 0.00379243
#> cv confint_lower confint_upper evaluate
#> <num> <num> <num> <char>
#> 1: 0.003604079 1.044828 1.059694 ExcellentAl construir pipelines complejos, conviene verificar cada paso de forma independiente:
Se pueden comparar los resultados de workflow() de
metasurvey con llamadas directas al paquete survey:
# Method 1: Direct survey package
design <- svydesign(id = ~1, weights = ~pw, data = dt_strat)
direct_mean <- svymean(~api00, design)
# Method 2: metasurvey workflow
wf_result <- workflow(
list(svy_simple),
survey::svymean(~api00, na.rm = TRUE),
estimation_type = "annual"
)
cat("Direct estimate:", round(coef(direct_mean), 2), "\n")
#> Direct estimate: 662.29
cat("Workflow estimate:", round(wf_result$value, 2), "\n")
#> Workflow estimate: 662.29
cat("Match:", all.equal(
as.numeric(coef(direct_mean)),
wf_result$value,
tolerance = 1e-6
), "\n")
#> Match: TRUECon view_graph() se puede visualizar el grafo de
dependencias entre steps:
# Requires the visNetwork package
svy_viz <- step_compute(svy_simple,
api_diff = api00 - api99,
high_growth = as.integer(api00 - api99 > 50)
)
view_graph(svy_viz, init_step = "Load API data")El DAG interactivo no se renderiza en esta vignette para reducir el tamaño del paquete; puede explorarse ejecutando el código anterior en una sesión interactiva de R.
Se puede evaluar la calidad de las estimaciones a partir del coeficiente de variación:
results_quality <- workflow(
list(svy_simple),
survey::svymean(~api00, na.rm = TRUE),
survey::svymean(~enroll, na.rm = TRUE),
estimation_type = "annual"
)
for (i in seq_len(nrow(results_quality))) {
cv_pct <- results_quality$cv[i] * 100
cat(
results_quality$stat[i], ":",
round(cv_pct, 1), "% CV -",
evaluate_cv(cv_pct), "\n"
)
}
#> survey::svymean: api00 : 1.4 % CV - Excellent
#> survey::svymean: enroll : 4.5 % CV - ExcellentSe puede verificar que los recipes y sus steps sean consistentes:
# Create steps and recipe
svy_rt <- step_compute(svy_simple, api_diff = api00 - api99)
my_recipe <- steps_to_recipe(
name = "API Test",
user = "QA Team",
svy = svy_rt,
description = "Recipe for validation",
steps = get_steps(svy_rt)
)
# Check documentation is correct
doc <- my_recipe$doc()
cat("Input variables:", toString(doc$input_variables), "\n")
#> Input variables: api00, api99
cat("Output variables:", toString(doc$output_variables), "\n")
#> Output variables: api_diff
# Validate against the survey
my_recipe$validate(svy_rt)
#> [1] TRUEAntes de poner en producción un pipeline de procesamiento de encuestas, se recomienda verificar lo siguiente:
surveyvalidate_pipeline <- function(svy) {
data <- get_data(svy)
checks <- list(
has_data = !is.null(data),
has_rows = nrow(data) > 0,
has_weights = all(
unlist(svy$weight)[is.character(unlist(svy$weight))] %in% names(data)
)
)
passed <- all(unlist(checks))
if (passed) {
message("All validation checks passed")
} else {
failed <- names(checks)[!unlist(checks)]
warning("Failed checks: ", toString(failed))
}
invisible(checks)
}
validate_pipeline(svy_simple)workflow(),
RecipeWorkflow y estimaciones publicablesRotativePanelSurvey y PoolSurvey