Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,13 @@
^\.github$
^doc$
^Meta$
^\.Rprofile$
^\.gitattributes$
^scripts$
^AGENTS\.md$
^CLAUDE\.md$
^\.claude$
^README\.Rmd$
^data-raw$
^vignettes/.*\.Rmd\.orig$
^vignettes/precompile\.R$
37 changes: 37 additions & 0 deletions .github/workflows/R-CMD-check.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
on:
push:
branches: [main, master]
pull_request:
workflow_dispatch:

name: R-CMD-check.yaml

permissions: read-all

jobs:
R-CMD-check:
runs-on: ubuntu-latest
env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
IPUMS_API_KEY: ${{ secrets.IPUMS_API_KEY }}
CENSUS_API_KEY: ${{ secrets.CENSUS_API_KEY }}
R_KEEP_PKG_SOURCE: yes
steps:
- uses: actions/checkout@v4

- uses: r-lib/actions/setup-pandoc@v2

- uses: r-lib/actions/setup-r@v2
with:
use-public-rspm: true

- uses: r-lib/actions/setup-r-dependencies@v2
with:
extra-packages: any::rcmdcheck
needs: check

- uses: r-lib/actions/check-r-package@v2
with:
upload-snapshots: true
build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")'
1 change: 1 addition & 0 deletions .github/workflows/pkgdown.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ jobs:
env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
IPUMS_API_KEY: ${{ secrets.IPUMS_API_KEY }}
CENSUS_API_KEY: ${{ secrets.CENSUS_API_KEY }}
permissions:
contents: write
steps:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ config.yml

# Cached crosswalks
/crosswalks-cache
data-raw/cache/
docs
CLAUDE.md
settings.local.json
Expand Down
22 changes: 14 additions & 8 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,37 +1,43 @@
Package: crosswalk
Type: Package
Title: Translate data across space and time
Version: 0.0.0.9001
Description: A unified, reproducible interface to access crosswalks and interpolate data.
Title: Translate Data across Space and Time
Version: 0.0.0.9002
Description: Provides a standardized interface for accessing and applying
geographic crosswalks between United States Census geographies, both across
geographies (e.g., tracts to ZIP Code Tabulation Areas) and across time
(e.g., 2010 tracts to 2020 tracts). Also supports interpolating crosswalked data,
with built-in diagnostics describing join quality.
License: MIT + file LICENSE
Authors@R:
Authors@R:
person(given = "Will", family = "Curran-Groome", email = "wcurrangroome@urban.org", role = c("aut", "cre"))
Encoding: UTF-8
LazyData: true
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.3.3
Depends:
URL: https://ui-research.github.io/crosswalk/, https://github.com/UI-Research/crosswalk
BugReports: https://github.com/UI-Research/crosswalk/issues
Depends:
R (>= 4.1.0)
Imports:
dplyr,
dplyr (>= 1.1.0),
httr,
httr2,
janitor,
purrr,
readr,
rlang,
rvest,
stats,
stringr,
tibble,
tidyr,
tidytable,
utils
Suggests:
ggplot2,
scales,
testthat (>= 3.0.0),
tidycensus,
knitr,
rmarkdown
Config/testthat/edition: 3
VignetteBuilder: knitr
URL: https://ui-research.github.io/crosswalk/
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export(crosswalk_data)
export(get_available_crosswalks)
export(get_crosswalk)
export(list_nhgis_crosswalks)
importFrom(rlang,":=")
122 changes: 122 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# crosswalk 0.0.0.9002

## New features

* **Arbitrary-year county crosswalks.** `get_crosswalk()` now supports
county -> county crosswalks between any pair of years from 2000 through 2025
(e.g., `2014 -> 2019` or `2014 -> 2023`), accounting for counties redefined
outside decennial censuses: Broomfield County, CO created (2002 vintage);
Clifton Forge, VA merge (2002); the 2007-2008 southeast Alaska
reorganizations (2008-2009); the York/Newport News, VA territory exchange
(2008); the Bedford, VA merge and Petersburg Borough, AK reorganization
(2014); the Shannon -> Oglala Lakota, SD and Wade Hampton -> Kusilvak, AK
FIPS changes (2015); the Valdez-Cordova, AK split (2020 vintage); and the
Connecticut planning regions (2022). Crosswalks compose all events in the
requested interval on top of an identity mapping over the full county
universe, with population-based allocation factors for splits and part
transfers. County crosswalks are forward-only. The event registry, mappings,
and county universes are curated from the Census Bureau's "Substantial
Changes to Counties and County Equivalent Entities" documentation by
`data-raw/build_county_events_sysdata.R` and shipped as internal package
data, so county requests work offline.
* **Sub-county geographies affected by county changes.** Tract, block group,
and block crosswalks are available for same-decade year pairs from 2010
onward (e.g., tract `2014 -> 2019`), applying county-prefix relabels for
FIPS renames/merges and the CT Data Collaborative crosswalk for
Connecticut's 2022 change. Year pairs spanning a decennial census (e.g.,
tract `2014 -> 2023`) are planned automatically as chains through NHGIS
crosswalks, including exact backward relabels to reach an NHGIS-served
vintage (e.g., tract `2017 -> 2023` plans `2017 -> 2014 -> 2020/22 -> 2023`).
* The chain planner's temporal-path search now spans multiple sources: hops
are tagged with the crosswalk source that serves them, and NHGIS is
preferred when both NHGIS and the county-events engine can serve a hop.
`get_available_crosswalks()` lists the new county-events combinations.
* Multi-step plans whose final GeoCorr step targets a year with county codes
differing from the GeoCorr vintage now emit a warning.
* `crosswalk_data()` now always computes and attaches the `join_quality`
attribute; `show_join_quality = FALSE` and `silent = TRUE` suppress only the
printed diagnostics. Previously, disabling the messages also silently
omitted the attribute.
* `get_available_crosswalks()` gains a `crosswalk_source` column identifying
which source serves each combination, and now lists each combination exactly
once: the 2020 <-> 2022 pairs, which both CTData and the county-events
engine can serve, are listed under `ctdata_2020_2022`, mirroring how
`get_crosswalk()` routes direct requests.

## Documentation

* Three new vignettes: `vignette("crosswalk")` (getting started; the core
workflow, multi-step chains, metadata, join quality, caching),
`vignette("how-interpolation-works")` (what the interpolation math assumes,
choosing a `weight`, accuracy against published values, diagnosing joins),
and `vignette("county-crosswalks")` (county change events and
arbitrary-year county crosswalks).
* The "Standardizing Longitudinal Data" vignette is now precomputed from
`vignettes/standardizing-longitudinal-data.Rmd.orig` (see
`vignettes/precompile.R`), so building the package requires neither network
access nor an IPUMS API key. It also now displays the join-quality
diagnostics it narrates and closes with a worked example of a split tract's
six-year series.
* The structures of the `join_quality` and `crosswalk_metadata` attributes are
now fully documented (in `?crosswalk_data` and `?get_crosswalk`
respectively), along with caching read-back behavior, the empty-crosswalk
return for nested geographies, and multi-step attribute semantics.
* Added a package-level help topic (`?crosswalk`) and a curated pkgdown
reference index; the README was slimmed to a quick start that links to the
vignettes.

## Breaking changes

* `get_crosswalk()` now filters NHGIS crosswalks to a single interpolation
weight matching the `weight` argument (`"population"`, `"housing"`, or
`"land"`). Previously, NHGIS crosswalks were returned in long format with one
row per source-target pair *per weighting factor* (typically eight rows per
pair), and applying them with `crosswalk_data()` silently multiply-counted
values by roughly the number of weighting factors. NHGIS `weighting_factor`
values no longer carry a `weight_` prefix (e.g., `"population"` rather than
`"weight_population"`).
* `crosswalk_data()` now errors when a crosswalk contains multiple rows per
source-target GEOID pair (which would multiply-count values), and when the
input data contains duplicated GEOIDs (panel data must be split by period and
crosswalked separately).
* `crosswalk_data()` no longer emits a `geoid = NA` row aggregating data rows
that failed to match the crosswalk; unmatched rows are dropped and reported
via the `join_quality` attribute.
* `crosswalk_data()` now renames only `target_geoid` -> `geoid` and
`target_geography_name` -> `geography_name` in its output; user columns
containing the string "target_" are no longer mangled.

## Bug fixes

* NHGIS 1990 tract GEOIDs are now right-padded with the implied `"00"` tract
suffix, so all 1990 tract GEOIDs have the standard 11 characters (previously
~66% of rows had 9-character GEOIDs).
* Cached NHGIS and GeoCorr crosswalks are now read back with explicit column
types; previously, type-guessing could silently strip leading zeros from
GEOIDs (e.g., ZCTA `"00601"` became `601`) on cache reads.
* GeoCorr 2018 block-level queries are now chunked into groups of at most 13
states; previously, chunks of 17 states exceeded the GeoCorr API limit.
* Block-level GeoCorr crosswalks assembled from multiple chunked requests no
longer retain the human-readable label row of each chunk after the first.
* Numeric crosswalk columns (`allocation_factor_target_to_source`,
`population_2020`, `housing_2020`, `land_area_sqmi`, etc.) are no longer
coerced to character by `get_crosswalk()`.
* When only `source_year` is provided, the GeoCorr version is now selected from
the source year; previously `get_crosswalk(..., source_year = 2015)` silently
used GeoCorr 2022 (2020 Census geography).
* The nested-geography check in `get_crosswalk()` now recognizes alternate
spellings (e.g., `"blockgroup"`, `"bg"`).
* NHGIS download failures (e.g., an invalid API key) now raise a clear error
with the HTTP status instead of a warning about an empty zip file.
* The `api_key` argument to the internal NHGIS fetcher is no longer ignored.
* County 2020 -> 2022 crosswalk metadata no longer claims all allocation
factors equal 1 (Connecticut county records are population-weighted).
* `get_ctdata_crosswalk()` now fails informatively when `tidycensus` is not
installed (county crosswalks only).

# crosswalk 0.0.0.9001

* Support for chains of three or more crosswalks.
* Silent mode via `silent` argument and the `crosswalk.silent` option.
* `get_available_crosswalks()` lists all supported combinations.
* GeoCorr 2018 support for 2010s-era geographies.
24 changes: 24 additions & 0 deletions R/crosswalk-package.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#' @details
#' The package standardizes access to geographic crosswalks from four sources:
#' GeoCorr (Missouri Census Data Center) for same-year geography changes, IPUMS
#' NHGIS for cross-decade changes, the CT Data Collaborative for Connecticut's
#' 2020-2022 county changes, and a curated registry of county boundary change
#' events for county crosswalks between any pair of years from 2000 onward.
#'
#' Typical usage involves two functions:
#' - [get_crosswalk()] fetches one or more crosswalks (chaining them
#' automatically when both geography and year change)
#' - [crosswalk_data()] applies crosswalk(s) to a dataset, interpolating count
#' and non-count variables and reporting join quality
#'
#' Two helpers support discovery:
#' - [get_available_crosswalks()] lists every supported combination of source
#' and target geography and year
#' - [list_nhgis_crosswalks()] lists the NHGIS subset with source URLs
#'
#' To get oriented, see `vignette("crosswalk")`. For the interpolation
#' methodology and guidance on choosing weights, see
#' `vignette("how-interpolation-works")`.
#'
#' @keywords internal
"_PACKAGE"
Loading
Loading