--- title: "Using GERDA in Agent-Run Research Workflows" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Using GERDA in Agent-Run Research Workflows} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ```{r setup} library(gerda) ``` This vignette is a fail-closed workflow for coding agents that use GERDA in a research project. It separates dataset selection, acquisition, schema checks, joins, and provenance so that an agent cannot silently continue after choosing the wrong dataset or losing observations. ## Select from the structured catalog Do not guess a dataset name from a prose description. Query the catalog and require one result before downloading: ```{r} catalog <- gerda_data_list(print_table = FALSE) candidates <- subset( catalog, election_type == "federal" & geographic_level == "municipality" & boundary == "2025" ) candidates[, c( "data_name", "year_start", "year_end", "boundary", "formats" )] stopifnot(nrow(candidates) == 1L) data_name <- candidates$data_name[[1]] ``` The boundary choice is part of the research design. `*_unharm` files use the geography in force at each election; `*_harm` files map observations to a common geography. A suffix such as `_21`, `_23`, or `_25` identifies the target boundary vintage. An agent should record why that vintage matches the analysis. ## Download with errors enabled and save a project snapshot Use RDS for smaller downloads, make load failures fatal, and opt into caching when repeated runs are expected: ```{r, eval = FALSE} elections <- load_gerda_web( data_name, file_format = "rds", on_error = "stop", cache = TRUE ) stopifnot(is.data.frame(elections), nrow(elections) > 0L) ``` The cache improves speed but does not pin an immutable upstream version. For a reproducible project, save the data actually analyzed and its checksum inside the project, subject to the project's data-storage policy: ```{r, eval = FALSE} dir.create("data/derived", recursive = TRUE, showWarnings = FALSE) snapshot_path <- file.path("data/derived", paste0(data_name, ".rds")) saveRDS(elections, snapshot_path) provenance <- list( package = "gerda", package_version = as.character(utils::packageVersion("gerda")), dataset = data_name, format = "rds", retrieved_at_utc = format(Sys.time(), tz = "UTC", usetz = TRUE), snapshot_path = snapshot_path, snapshot_md5 = unname(tools::md5sum(snapshot_path)) ) saveRDS(provenance, "data/derived/gerda_provenance.rds") ``` `load_gerda_web()` reads final data artifacts from the [GERDA data repository](https://github.com/awiedem/german_election_data). Use that repository when an audit requires source files, cleaning scripts, or lineage below the package's final artifacts. Files named `*_raw` are the most source-near GERDA representation; they are not a substitute for inspecting the original source and build pipeline. ## Validate the loaded schema before analysis Geographic identifiers must remain character vectors. Re-read the original source with explicit character column types if an identifier became numeric; padding a damaged numeric value later can conceal leading-zero loss. For the selected municipal panel, a minimal structural audit is: ```{r, eval = FALSE} required <- c("ags", "election_year") stopifnot(all(required %in% names(elections))) if (!is.character(elections$ags) || any(!is.na(elections$ags) & !grepl("^[0-9]{8}$", elections$ags))) { stop("ags must contain eight-digit character identifiers") } if (any(!stats::complete.cases(elections[required]))) { stop("Missing geographic or time keys require investigation") } if (anyDuplicated(elections[required])) { stop("ags + election_year is not unique; identify additional row keys") } expected_years <- sort(unique(elections$election_year)) years_by_unit <- split(elections$election_year, elections$ags) units_with_gaps <- names(Filter( function(x) !identical(sort(unique(x)), expected_years), years_by_unit )) ``` The correct row key varies by dataset. Candidate, election-round, party-long, and `stimme` files need additional key columns. Never resolve duplicate keys by calling `distinct()` unless the duplication has been explained. Inspect missingness in every analysis variable before filtering or estimation; do not silently drop rows with missing outcomes, treatments, identifiers, or years. ## Use guarded joins and inspect their diagnostics The two enrichment helpers enforce a many-to-one left-join contract. They reject numeric or malformed geographic identifiers, duplicate reference keys, columns that would be overwritten, and any join that expands the input row count. Set `unmatched = "error"` in agent-run pipelines: ```{r, eval = FALSE} enriched <- elections |> add_gerda_covariates(unmatched = "error") |> add_gerda_census(unmatched = "error") join_report <- gerda_join_diagnostics(enriched) print(join_report) stopifnot( all(join_report$input_rows == join_report$output_rows), all(join_report$unexpected_unmatched_rows == 0L) ) saveRDS(join_report, "data/derived/gerda_join_diagnostics.rds") ``` For INKAR covariates, election years outside 1995–2022 are retained with missing joined values and reported as `outside_coverage_rows`; they are not treated as unexpected matches. `unmatched = "error"` still stops on valid-year keys that fail to match and on missing join keys in any year. The other modes are `"warn"` (the interactive default) and `"ignore"`. Capture diagnostics immediately after the join pipeline because unrelated data-frame transformations may remove custom attributes. A successful key match does not imply complete covariate values. Individual INKAR and Census variables can still be missing, so inspect payload missingness separately. Census values are a single 2022 snapshot and are time-invariant when attached to elections from other years. Exact identifier matches also do not by themselves establish that the election and covariate boundary vintages are substantively comparable. ## Use bundled data and codebooks directly INKAR covariates and Census 2022 are bundled with the package and require no download. Their codebooks are machine-readable data frames: ```{r} covariates <- gerda_covariates() covariate_codebook <- gerda_covariates_codebook() census <- gerda_census() census_codebook <- gerda_census_codebook() stopifnot( !anyDuplicated(covariates[c("county_code", "year")]), !anyDuplicated(census["ags"]), all(names(covariates) %in% covariate_codebook$variable), all(names(census) %in% census_codebook$variable) ) ``` Before handing the project back, an agent should leave the exact dataset name and boundary rationale, package version, saved snapshot and checksum, complete row-key definition, missing-key and panel-gap checks, join diagnostics, variable missingness summary, and the GERDA citation returned by `citation("gerda")`.