R/county.R

Defines functions as.data.frame.county print.county county_meta

Documented in as.data.frame.county county_meta print.county

# R/county.R
# S3 class "county": lightweight metadata objects stored in data/.
# The full microdata are NOT stored here; they live in the repository and
# are loaded via get_county_data() or as.data.frame().

# ----------------------------------------------------------------------------
# Class documentation
# ----------------------------------------------------------------------------

#' The county class
#'
#' A `county` object is a lightweight metadata summary for a single county's
#' ballot data file. It does **not** contain the microdata itself (one row
#' per voter); it only stores precomputed statistics about that county's
#' races, used to build help pages and to give a quick overview before
#' downloading the full dataset.
#'
#' One such object exists per county (e.g. [Lee]), stored as a small package
#' data object. Use [county_meta()] to retrieve one by county code, or
#' [get_county_data()] / `as.data.frame()` to load the actual microdata.
#'
#' @section Structure:
#' A `county` object is a named list with the class attribute
#' `c("county", "list")` and the following elements:
#' \describe{
#'   \item{`county`}{Character. The county code (e.g. `"Lee"`).}
#'   \item{`n_voters_total`}{Integer. Total number of voters (rows) in the
#'     county's microdata file, regardless of eligibility in any
#'     particular race.}
#'   \item{`n_precincts_total`}{Integer. Total number of distinct precincts
#'     in the county.}
#'   \item{`elections`}{Character vector. Race codes present in this
#'     county's data file (a subset of [election_catalog]).}
#'   \item{`voters_by_election`}{Named integer vector. Number of eligible
#'     (non-`NA`) voters for each race in `elections`.}
#'   \item{`precincts_by_election`}{Named integer vector. Number of
#'     precincts with at least one eligible voter, for each race.}
#'   \item{`options_by_election`}{Named list of character vectors. The
#'     vote options observed in the data for each race (candidate codes
#'     or combinations, plus `"A"`/`"I"` when present).}
#' }
#'
#' @seealso [Lee], [county_meta()], [get_county_data()], [list_counties()],
#'   [election_catalog]
#' @name county
NULL


# ----------------------------------------------------------------------------
# county_meta(): symmetric accessor to get_county_data()
# ----------------------------------------------------------------------------

#' Retrieve county metadata
#'
#' Returns the `county` metadata object for a given county. This is the
#' metadata counterpart to [get_county_data()]: where `get_county_data()`
#' loads the full microdata (heavy), `county_meta()` returns the lightweight
#' summary (counts, races, observed vote options).
#'
#' @param county Character string (county code, e.g. `"Lee"`) or an object
#'   of class `county` (e.g. `Lee`). If an object is supplied, it is
#'   returned unchanged.
#'
#' @return An object of class `county`. See [county] for its structure.
#'
#' @examples
#' county_meta("Lee")
#' county_meta(Lee)      # pass-through: already a county object
#'
#' @seealso [county], [get_county_data()], [list_counties()]
#' @export
county_meta <- function(county) {
  if (inherits(county, "county")) return(county)

  if (!is.character(county) || length(county) != 1L)
    stop(
      "'county' must be a county code (character string) or a county object.",
      call. = FALSE
    )

  if (!county %in% names(county_catalog))
    stop(
      sprintf(
        "County '%s' not found in the catalog.\nUse list_counties() to see available counties.",
        county
      ),
      call. = FALSE
    )

  get(county, envir = asNamespace("eiballots"))
}


# ----------------------------------------------------------------------------
# print method
# ----------------------------------------------------------------------------

#' Print a county metadata object
#'
#' Displays the schema and summary statistics for the county's ballot data:
#' races present, vote options observed in each race, total voters,
#' precincts, and per-race eligibility counts.
#'
#' @param x A `county` object (e.g. `Lee`).
#' @param ... Ignored.
#' @seealso [county], [county_meta()], [as.data.frame.county]
#' @return The input object `x`, returned invisibly. Called for its
#'   side effect of printing a structured metadata summary to the console.
#' @export
print.county <- function(x, ...) {
  cat(sprintf("Ballot data metadata - %s County (Florida 2000)\n", x$county))
  cat(strrep("-", 60), "\n")
  cat(sprintf("  %-22s %s\n", "Total voters in file:",
              format(x$n_voters_total, big.mark = ",")))
  cat(sprintf("  %-22s %d\n", "Total precincts:", x$n_precincts_total))
  cat(sprintf("  %-22s %d\n", "Races available:", length(x$elections)))
  cat("\n")

  cat(sprintf("  %-8s  %12s  %9s  %s\n",
              "Race", "Elig. voters", "Precincts", "Observed options"))
  cat(sprintf("  %-8s  %12s  %9s  %s\n",
              strrep("-", 8), strrep("-", 12), strrep("-", 9), strrep("-", 17)))
  for (e in x$elections) {
    n_v  <- x$voters_by_election[[e]]
    n_p  <- x$precincts_by_election[[e]]
    opts <- x$options_by_election[[e]]
    opts_str <- if (length(opts) == 0L) "-"
    else if (length(opts) <= 6L) paste(opts, collapse = ", ")
    else sprintf("%s, ... (%d total)",
                 paste(opts[1:5], collapse = ", "), length(opts))
    cat(sprintf("  %-8s  %12s  %9s  %s\n",
                e,
                if (is.na(n_v)) "-" else format(n_v, big.mark = ","),
                if (is.na(n_p)) "-" else as.character(n_p),
                opts_str))
  }

  cat(sprintf('\n  Load full data: get_county_data("%s")\n', x$county))
  cat(sprintf('  Race details:   ?election_catalog\n'))
  invisible(x)
}


# ----------------------------------------------------------------------------
# as.data.frame method - triggers the actual data download/load
# ----------------------------------------------------------------------------

#' Load the full microdata for a county object
#'
#' Calls [get_county_data()] using the county code stored in the object.
#' This is the data counterpart to [county_meta()]: it converts the
#' lightweight metadata object into the full voter-level data.
#'
#' @param x A `county` object (e.g. `Lee`).
#' @param source Character string or `NULL`. Either a local directory path
#'   (e.g. `"~/data/"`) or the base URL of a remote repository. If `NULL`,
#'   the source is resolved automatically (see Details).
#' @param ... Ignored.
#' @return A `data.frame` with one row per voter.
#'
#' @details
#' To avoid specifying `source` in every call, set `options(data_dir)` once
#' at the start of your session or in your `.Rprofile`:
#' ```r
#' options(data_dir = "~/my_data/")   # local files
#' # or leave unset to use the online OSF repository
#' ```
#'
#' @examples
#' \donttest{
#' lee <- as.data.frame(Lee)
#' }
#'
#' @seealso [county], [county_meta()], [get_county_data()]
#' @export
as.data.frame.county <- function(x, ..., source = NULL) {
  get_county_data(x$county, source = source)
}

Try the eiballots package in your browser

Any scripts or data that you put into this service are public.

eiballots documentation built on Sept. 26, 2026, 5:06 p.m.