Author: Luis Burgos
Date: 2026-07-13

# Basic Workflow with svySE

#### Luis Burgos

#### 2026-07-13

# Introduction

`svySE` provides a structured workflow for producing indicator tables from complex survey data.

The package separates two complementary tasks:

1. `svySE_calc()` calculates weighted estimates and sampling errors using sampling weights and, when available, strata and cluster variables.
2. `svySE_simple()` calculates unweighted frequencies and percentages from the observed sample without using the survey design.

Results generated by either function can be exported to `.xlsx` files with `svySE_xlsx()`. The export function can process one result or consolidate several analyses generated from different datasets, indicators, weights, or function calls.

The package is built on top of `survey` for design-based estimation and uses `openxlsx` for workbook generation.

# Package workflow

Step | Function | Purpose
--- | --- | ---
1 | `svySE_cfg()` | Configure estimation options
2A | `svySE_calc()` | Calculate weighted estimates and sampling errors
2B | `svySE_simple()` | Calculate unweighted frequencies and percentages
3 | `svySE_xlsx()` | Export one or multiple results to `.xlsx` files

The two calculation functions are intentionally separated. This avoids performing unnecessary calculations when only sampling errors or only descriptive tables are required.

# Example data

The following simulated dataset contains:

- one grouping variable;
- one stratification variable;
- one cluster variable;
- one sampling weight;
- one division variable;
- two binary indicators.

    library(svySE)

    set.seed(123)

    df <- data.frame(
      dept = rep(c("A", "B", "C"), each = 50),
      strata = rep(c("S1", "S2", "S3"), each = 50),
      cluster = rep(seq_len(30), each = 5),
      service = rep(c("S1", "S2"), length.out = 150),
      weight = runif(150, 10, 50),
      ind_1 = sample(c(0, 1), 150, replace = TRUE),
      ind_2 = sample(c(0, 1), 150, replace = TRUE),
      stringsAsFactors = FALSE
    )

    head(df)
    #>   dept strata cluster service   weight ind_1 ind_2
    #> 1    A     S1       1      S1 21.50310     0     1
    #> 2    A     S1       1      S2 41.53221     1     1
    #> 3    A     S1       1      S1 26.35908     0     0
    #> 4    A     S1       1      S2 45.32070     1     1
    #> 5    A     S1       1      S1 47.61869     0     0
    #> 6    A     S1       2      S2 11.82226     0     0

# Configure sampling error estimation

`svySE_cfg()` defines the settings used by `svySE_calc()`.

    cfg <- svySE_cfg(
      estimator = "prop",
      variance = "taylor",
      lonely_psu = "adjust",
      conf_level = 0.95,
      target = 1,
      valid_values = c(0, 1),
      truncate_lower_ci = TRUE,
      pct_mult = 100,
      deff = TRUE,
      cv = TRUE,
      na_rm = TRUE
    )

    cfg
    #> svySE configuration
    #> --------------------------------------------------
    #> Estimator          : prop
    #> Variance           : taylor
    #> Lonely PSU         : adjust
    #> Confidence level   : 0.95
    #> Target value       : 1
    #> Valid values       : 0, 1
    #> Truncate lower CI  : TRUE
    #> Percentage mult.   : 100
    #> Include DEFF       : TRUE
    #> Include CV         : TRUE
    #> Remove NA          : TRUE

The principal options are:

Argument | Purpose
--- | ---
`estimator` | Selects the estimator used in the analysis
`variance` | Defines the variance estimation method
`lonely_psu` | Specifies how strata containing one PSU are handled
`conf_level` | Defines the confidence level
`target` | Identifies the indicator category of interest
`valid_values` | Defines the valid indicator values
`truncate_lower_ci` | Truncates negative lower confidence limits at zero
`pct_mult` | Converts proportions to the selected percentage scale
`deff` | Enables or disables design-effect calculation
`cv` | Enables or disables coefficient-of-variation calculation
`na_rm` | Controls the treatment of missing values

# Calculate sampling errors

`svySE_calc()` calculates weighted estimates and measures of sampling precision.

    res_error <- svySE_calc(
      data = df,
      indicators = c("ind_1", "ind_2"),
      group_vars = "dept",
      group_labels = "Department",
      strata = "strata",
      cluster = "cluster",
      weight = "weight",
      division = NULL,
      div_weight = NULL,
      cfg = cfg,
      verbose = FALSE
    )

    res_error
    #> svySE sampling error result
    #> --------------------------------------------------
    #> Indicators : ind_1, ind_2
    #> Groups     : dept
    #> Strata     : strata
    #> Cluster    : cluster
    #> Weight     : weight
    #> Division   : NULL
    #> Estimator  : prop
    #> Target     : 1
    #> Strict     : FALSE
    #> Simple tab : No (use svySE_simple())

The returned object has class `"svySE_result"`.

    class(res_error)
    #> [1] "svySE_result" "list"

Results are organized by indicator and division.

    names(res_error$results)
    #> [1] "ind_1" "ind_2"
    names(res_error$results$ind_1$error)
    #> [1] "TOTAL"

The national and grouped results for the first indicator can be inspected with:

    res_error$results$ind_1$error$TOTAL
    #>       dept   est_abs  est_pct   se_abs   se_pct  ci_l_abs ci_l_pct  ci_u_abs
    #> 1 NACIONAL 2274.6821 50.26904 227.0537 4.182067 1829.6650 42.07234 2719.6992
    #> 2        A  760.7571 49.39397 134.7782 7.768911  496.5967 34.16719 1024.9174
    #> 3        B  933.1969 64.17897 109.2721 5.617541  719.0274 53.16879 1147.3664
    #> 4        C  580.7282 37.93676 146.4508 8.122624  293.6899 22.01671  867.7664
    #>   ci_u_pct        cv      deff n_unw
    #> 1 58.46574  8.319368 1.0781551    72
    #> 2 64.62076 15.728459 1.2665513    23
    #> 3 75.18915  8.752931 0.6797495    30
    #> 4 53.85680 21.410960 1.4608894    19

The sampling error table contains:

Column | Description
--- | ---
`est_abs` | Weighted absolute estimate
`est_pct` | Weighted percentage estimate
`se_abs` | Standard error of the absolute estimate
`se_pct` | Standard error of the percentage
`ci_l_abs` | Lower confidence limit for the absolute estimate
`ci_l_pct` | Lower confidence limit for the percentage
`ci_u_abs` | Upper confidence limit for the absolute estimate
`ci_u_pct` | Upper confidence limit for the percentage
`cv` | Coefficient of variation
`deff` | Design effect
`n_unw` | Unweighted number of target cases

# Supported survey designs

`svySE_calc()` can be used with different combinations of design variables.

Design | `strata` | `cluster`
--- | --- | ---
Weight only | `NULL` | `NULL`
Stratified | Variable name | `NULL`
Clustered | `NULL` | Variable name
Stratified and clustered | Variable name | Variable name

## Weight-only design

    res_weight <- svySE_calc(
      data = df,
      indicators = "ind_1",
      group_vars = "dept",
      group_labels = "Department",
      strata = NULL,
      cluster = NULL,
      weight = "weight",
      cfg = cfg,
      verbose = FALSE
    )

    res_weight
    #> svySE sampling error result
    #> --------------------------------------------------
    #> Indicators : ind_1
    #> Groups     : dept
    #> Strata     : NULL
    #> Cluster    : NULL
    #> Weight     : weight
    #> Division   : NULL
    #> Estimator  : prop
    #> Target     : 1
    #> Strict     : FALSE
    #> Simple tab : No (use svySE_simple())

## Stratified design

    res_strata <- svySE_calc(
      data = df,
      indicators = "ind_1",
      group_vars = "dept",
      group_labels = "Department",
      strata = "strata",
      cluster = NULL,
      weight = "weight",
      cfg = cfg,
      verbose = FALSE
    )

    res_strata
    #> svySE sampling error result
    #> --------------------------------------------------
    #> Indicators : ind_1
    #> Groups     : dept
    #> Strata     : strata
    #> Cluster    : NULL
    #> Weight     : weight
    #> Division   : NULL
    #> Estimator  : prop
    #> Target     : 1
    #> Strict     : FALSE
    #> Simple tab : No (use svySE_simple())

## Clustered design

    res_cluster <- svySE_calc(
      data = df,
      indicators = "ind_1",
      group_vars = "dept",
      group_labels = "Department",
      strata = NULL,
      cluster = "cluster",
      weight = "weight",
      cfg = cfg,
      verbose = FALSE
    )

    res_cluster
    #> svySE sampling error result
    #> --------------------------------------------------
    #> Indicators : ind_1
    #> Groups     : dept
    #> Strata     : NULL
    #> Cluster    : cluster
    #> Weight     : weight
    #> Division   : NULL
    #> Estimator  : prop
    #> Target     : 1
    #> Strict     : FALSE
    #> Simple tab : No (use svySE_simple())

## Stratified and clustered design

    res_complex <- svySE_calc(
      data = df,
      indicators = "ind_1",
      group_vars = "dept",
      group_labels = "Department",
      strata = "strata",
      cluster = "cluster",
      weight = "weight",
      cfg = cfg,
      verbose = FALSE
    )

    res_complex
    #> svySE sampling error result
    #> --------------------------------------------------
    #> Indicators : ind_1
    #> Groups     : dept
    #> Strata     : strata
    #> Cluster    : cluster
    #> Weight     : weight
    #> Division   : NULL
    #> Estimator  : prop
    #> Target     : 1
    #> Strict     : FALSE
    #> Simple tab : No (use svySE_simple())

# Domain estimation

The `division` argument generates separate results for each observed category of a division variable.

    res_domain <- svySE_calc(
      data = df,
      indicators = "ind_1",
      group_vars = "dept",
      group_labels = "Department",
      strata = "strata",
      cluster = "cluster",
      weight = "weight",
      division = "service",
      div_weight = NULL,
      cfg = cfg,
      verbose = FALSE
    )

    names(res_domain$results$ind_1$error)
    #> [1] "TOTAL" "S1"    "S2"

The `TOTAL` element contains the complete estimate, while the remaining elements contain the corresponding division results.

    res_domain$results$ind_1$error$S1
    #>       dept   est_abs  est_pct    se_abs    se_pct ci_l_abs ci_l_pct  ci_u_abs
    #> 1 NACIONAL 1135.5816 50.12187 158.81289  5.725684 824.3141 38.89974 1446.8492
    #> 2        A  283.6570 38.83620  81.08334  9.105621 124.7366 20.98951  442.5775
    #> 3        B  568.1100 72.84501 100.44052  9.339124 371.2502 54.54066  764.9698
    #> 4        C  283.8146 37.57349  92.51339 10.743894 102.4917 16.51584  465.1376
    #>   ci_u_pct       cv      deff n_unw
    #> 1 61.34401 11.42352 1.0036189    35
    #> 2 56.68288 23.44622 0.8612027     9
    #> 3 91.14935 12.82054 1.1615982    17
    #> 4 58.63113 28.59435 1.2557022     9

When a division requires a different expansion factor, it can be provided through `div_weight`.

# Calculate simple indicator tables

`svySE_simple()` calculates unweighted frequencies and percentages without requiring sampling weights, strata, or clusters.

    res_simple <- svySE_simple(
      data = df,
      indicators = c("ind_1", "ind_2"),
      group_vars = "dept",
      group_labels = "Department",
      division = NULL,
      target = 1,
      valid_values = c(0, 1),
      pct_mult = 100,
      verbose = FALSE
    )

    res_simple
    #> svySE simple result
    #> --------------------------------------------------
    #> Indicators : ind_1, ind_2
    #> Groups     : dept
    #> Division   : NULL
    #> Target     : 1
    #> Weighted   : No
    #> Warning    : Results describe the observed sample only.

The returned object has class `"svySE_simple_result"`.

    class(res_simple)
    #> [1] "svySE_simple_result" "list"

A simple table can be inspected with:

    res_simple$results$ind_1$simple$TOTAL
    #>       dept freq_0 pct_0 freq_1 pct_1 freq_total pct_total
    #> 1 NACIONAL     78    52     72    48        150       100
    #> 2        A     27    54     23    46         50       100
    #> 3        B     20    40     30    60         50       100
    #> 4        C     31    62     19    38         50       100

The table contains:

Column | Description
--- | ---
`freq_0` | Frequency of non-target cases
`pct_0` | Percentage of non-target cases
`freq_1` | Frequency of target cases
`pct_1` | Percentage of target cases
`freq_total` | Total number of valid observations
`pct_total` | Total percentage

The results generated by `svySE_simple()` describe the observed sample only. Because no sampling weights or design variables are used, these percentages should not be interpreted as population estimates.

# Simple tables by division

A division variable can also be used with `svySE_simple()`.

    res_simple_domain <- svySE_simple(
      data = df,
      indicators = "ind_1",
      group_vars = "dept",
      group_labels = "Department",
      division = "service",
      target = 1,
      valid_values = c(0, 1),
      pct_mult = 100,
      verbose = FALSE
    )

    names(res_simple_domain$results$ind_1$simple)
    #> [1] "TOTAL" "S1"    "S2"

# Export sampling errors to XLSX

The following example writes a sampling error workbook to a temporary file.

    file_err <- tempfile(fileext = ".xlsx")

    export_error <- svySE_xlsx(
      x = res_error,
      file_err = file_err,
      file_tab = NULL,
      cols_err = svySE_cols_err("full"),
      overwrite = TRUE
    )

    file.exists(file_err)
    #> [1] TRUE

# Export simple tables to XLSX

    file_tab <- tempfile(fileext = ".xlsx")

    export_simple <- svySE_xlsx(
      x = res_simple,
      file_err = NULL,
      file_tab = file_tab,
      cols_tab = svySE_cols_tab("full"),
      overwrite = TRUE
    )

    file.exists(file_tab)
    #> [1] TRUE

# Export multiple analyses

`svySE_xlsx()` can receive a named list containing several result objects.

    results <- list(
      Main_errors = res_error,
      Domain_errors = res_domain,
      Main_simple = res_simple,
      Domain_simple = res_simple_domain
    )

The function automatically identifies the class of each object:

- `"svySE_result"` objects are exported to `file_err`;
- `"svySE_simple_result"` objects are exported to `file_tab`.

    multiple_err <- tempfile(fileext = ".xlsx")
    multiple_tab <- tempfile(fileext = ".xlsx")

    export_multiple <- svySE_xlsx(
      x = results,
      file_err = multiple_err,
      file_tab = multiple_tab,
      cols_err = svySE_cols_err("full"),
      cols_tab = svySE_cols_tab("full"),
      overwrite = TRUE
    )

    file.exists(multiple_err)
    #> [1] TRUE
    file.exists(multiple_tab)
    #> [1] TRUE

This is useful when several indicators are calculated from different datasets, survey weights, domains, or analytical processes.

# Export selected results

The `select` argument restricts the export to chosen elements of a named list.

## Select sampling error results

    selected_err <- tempfile(fileext = ".xlsx")

    svySE_xlsx(
      x = results,
      select = c("Main_errors", "Domain_errors"),
      file_err = selected_err,
      file_tab = NULL,
      overwrite = TRUE
    )

    file.exists(selected_err)
    #> [1] TRUE

## Select simple results

    selected_tab <- tempfile(fileext = ".xlsx")

    svySE_xlsx(
      x = results,
      select = c("Main_simple", "Domain_simple"),
      file_err = NULL,
      file_tab = selected_tab,
      overwrite = TRUE
    )

    file.exists(selected_tab)
    #> [1] TRUE

# Customize exported columns

## Sampling error columns

The complete set of sampling error columns can be requested with:

    svySE_cols_err("full")
    #>  [1] "est_abs"  "est_pct"  "se_abs"   "se_pct"   "ci_l_abs" "ci_l_pct"
    #>  [7] "ci_u_abs" "ci_u_pct" "cv"       "deff"     "n_unw"

A custom selection can be defined with:

    error_columns <- svySE_cols_err(
      type = "custom",
      cols = c(
        "est_pct",
        "se_pct",
        "ci_l_pct",
        "ci_u_pct",
        "cv",
        "deff",
        "n_unw"
      )
    )

    error_columns
    #> [1] "est_pct"  "se_pct"   "ci_l_pct" "ci_u_pct" "cv"       "deff"     "n_unw"

Use the custom selection during export:

    custom_err <- tempfile(fileext = ".xlsx")

    svySE_xlsx(
      x = res_error,
      file_err = custom_err,
      file_tab = NULL,
      cols_err = error_columns,
      overwrite = TRUE
    )

    file.exists(custom_err)
    #> [1] TRUE

## Simple table columns

    svySE_cols_tab("full")
    #> [1] "freq_0"     "pct_0"      "freq_1"     "pct_1"      "freq_total"
    #> [6] "pct_total"

A custom selection can be defined with:

    simple_columns <- svySE_cols_tab(
      type = "custom",
      cols = c(
        "freq_1",
        "pct_1",
        "freq_total"
      )
    )

    simple_columns
    #> [1] "freq_1"     "pct_1"      "freq_total"

Use the custom selection during export:

    custom_tab <- tempfile(fileext = ".xlsx")

    svySE_xlsx(
      x = res_simple,
      file_err = NULL,
      file_tab = custom_tab,
      cols_tab = simple_columns,
      overwrite = TRUE
    )

    file.exists(custom_tab)
    #> [1] TRUE

# Main functions

Function | Description
--- | ---
`svySE_cfg()` | Configure sampling error estimation
`svySE_calc()` | Calculate weighted estimates and sampling errors
`svySE_simple()` | Calculate unweighted frequencies and percentages
`svySE_xlsx()` | Export one or multiple results to `.xlsx` files
`svySE_cols_err()` | Select sampling error columns
`svySE_cols_tab()` | Select simple table columns

# Technical foundation

`svySE` uses established R packages for its internal workflow.

Package | Role
--- | ---
`survey` | Design-based estimation and variance calculation
`openxlsx` | Creation and formatting of `.xlsx` workbooks
`stats` | Statistical formulas, coefficients, and confidence intervals
`svySE` | High-level workflow for indicators, sampling errors, tables, and export

`svySE` does not replace `survey`. It provides a structured interface for repeated indicator production, quality measurement, and export.

# Summary

This vignette demonstrated how to:

1. configure sampling error estimation with `svySE_cfg()`;
2. calculate weighted estimates and sampling errors with `svySE_calc()`;
3. work with weight-only, stratified, clustered, and combined designs;
4. calculate domain estimates;
5. generate unweighted descriptive tables with `svySE_simple()`;
6. export sampling errors and simple tables separately;
7. consolidate several analyses in one export process;
8. select specific results before exporting;
9. customize the columns included in `.xlsx` files.

The same workflow can be applied to official survey datasets by specifying the corresponding indicators, grouping variables, weights, strata, and clusters.