Skip to content

Helper function to read survey microdata #56

Description

@viniciusoike

A proposed helper function to read OD survey data with appropriate weights and strata using {survey}. This function currently works only with São Paulo data. I've tested it only with the 2017 survey, but I'm confident that it should work with other years as well. I assume the user will input a data.frame generated by read_od.

#' Create the survey design for São Paulo's OD data.
#'
#' Create a survey design for São Paulo's OD microdata. Requires the \code{survey}
#' package.
#'
#' @param df A `data.frame` with POD microdata, ideally generated by `read_od`.
#' @param id Choice of identifier. Must be one of `individual` ('pessoa'),
#' `family` ('família), or `household` ('domicílio').
#' @param verbose Logical. If `TRUE` (default), displays validation messages.
#'
#' @return An object of class \code{survey.design} with the appropriate weights and strata.
#' @export
#'
#' @importFrom survey svydesign
#'
#' @examples
#' \dontrun{
#' # Create person-level survey design
#' srv_pess <- design_od(od_data, id = "individual")
#' 
#' # Create family-level survey design silently
#' srv_hh <- design_od(od_data, id = "household", verbose = FALSE)
#' }
design_od <- function(df, id, verbose = TRUE) {
  
  # Input validation
  if (!is.data.frame(df)) {
    cli::cli_abort(c(
      "x" = "Input {.arg df} must be a data.frame",
      "i" = "You provided an object of class {.cls {class(df)}}"
    ))
  }
  
  # Validate id parameter
  valid_ids <- c("individual", "family", "household")
  if (!id %in% valid_ids) {
    cli::cli_abort(c(
      "x" = "Invalid {.arg id} value: {.val {id}}",
      "i" = "Must be one of: {.val {valid_ids}}"
    ))
  }
  
  # Define configurations for each survey level
  config <- list(
    individual = list(
      name = "person",
      id_col = "id_pess",
      weight_col = "fe_pess",
      description = "Individual person weights"
    ),
    family = list(
      name = "family", 
      id_col = "id_fam",
      weight_col = "fe_fam",
      description = "Family-level weights"
    ),
    household = list(
      name = "household",
      id_col = "id_dom", 
      weight_col = "fe_dom",
      description = "Household-level weights"
    )
  )
  
  current_config <- config[[id]]
  
  # Check for missing values in critical columns
  weight_col <- current_config$weight_col
  id_col <- current_config$id_col
  
  # Create survey design
  if (verbose) {
    cli::cli_alert_info("Creating survey design with {current_config$description}")
  }
  
  clean_data <- unique(df, by = id_col)
  final_rows <- nrow(clean_data)
  
  tryCatch({
    out <- survey::svydesign(
      ids = ~ 1,
      weights = stats::as.formula(paste("~", weight_col)),
      strata = ~ zona,
      data = df
    )
    
    if (verbose) {
      cli::cli_alert_success("Survey design created successfully")
      
      # Summary statistics
      n_strata <- length(unique(clean_data$zona))
      
      cli::cli_alert_info("Design summary:")
      cli::cli_ul(c(
        "Survey type: {.val {current_config$name}}-level",
        "Sample size: {.val {final_rows}} observations", 
        "Strata: {.val {n_strata}} zones"
      ))
    }
    
    return(out)
    
  }, error = function(e) {
    cli::cli_abort(c(
      "x" = "Failed to create survey design",
      "!" = "Error: {e$message}",
      "i" = "Check your data structure and column names"
    ))
  })
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions