Using acsmoe with tidycensus

tidycensus is the right tool for downloading ACS estimates and margins of error. acsmoe starts after that: it works with estimate/MOE columns that you already have.

Kyle Walker’s tidycensus article on ACS margins of error shows the standard workflow and the standard Census approximation formulas: https://walker-data.com/tidycensus/articles/margins-of-error.html.

That article demonstrates tidycensus::moe_sum(), tidycensus::moe_prop(), tidycensus::moe_ratio(), and tidycensus::moe_product(). It also quotes the Census warning that these approximation methods do not account for correlation or covariance between basic estimates. acsmoe is intended for the same tabular ACS regime, but exposes covariance-aware extensions and a grouped aggregation helper.

Pull data with tidycensus

This example mirrors the shape of Walker’s MOE vignette: pull tract-level ACS age-by-sex cells, then aggregate those cells into a derived total. The call is not evaluated in this vignette because it requires network access and, in many setups, a Census API key.

library(tidycensus)
library(dplyr)
library(acsmoe)

vars <- paste0("B01001_0", c(20:25, 44:49))

ramsey <- get_acs(
  geography = "tract",
  variables = vars,
  state = "MN",
  county = "Ramsey",
  year = 2016
)

ramsey65 <- ramsey |>
  group_by(GEOID) |>
  summarize(
    estimate_65plus = sum(estimate),
    moe_65plus = acs_sum(estimate, moe)$moe,
    .groups = "drop"
  )

With no covariance supplied, acs_sum() intentionally reduces to the same zero-covariance root-sum-square calculation used by tidycensus::moe_sum(). That makes it a drop-in bridge from the standard workflow to more explicit uncertainty propagation.

The package website includes a fuller tidycensus example with evaluated maps when the site is built with Census API credentials. That example is kept out of CRAN vignette evaluation because it requires network access, sf geometries, and current ACS API availability.

Work from paired estimate/MOE columns

Many ACS workflows become wide after tidycensus::get_acs(output = "wide"), or after a user-created join. acs_aggregate() handles this paired-column form.

library(acsmoe)

tracts <- data.frame(
  region = c("north", "north", "south", "south"),
  population = c(1000, 1200, 900, 1100),
  population_moe = c(120, 140, 100, 130),
  households = c(420, 500, 360, 440),
  households_moe = c(60, 70, 50, 65)
)

acs_aggregate(
  tracts,
  group_var = "region",
  value_cols = c("population", "households"),
  moe_cols = c("population_moe", "households_moe")
)
#>   region population population_moe households households_moe
#> 1  north       2200       184.3909        920       92.19544
#> 2  south       2000       164.0122        800       82.00610

The default cov_strategy = "zero" is deliberately conservative in the API sense: it matches the standard Census approximation behavior. It should not be read as a claim that tract estimates are truly independent.

Add covariance when you have it

If a covariance matrix is available from an external method, pass it on the standard-error scale. Do not pass covariance of MOEs.

estimates <- c(1000, 1200)
moes <- c(120, 140)
ses <- moe_to_se(moes)

cov_mat <- matrix(
  c(ses[1]^2, 1500,
    1500, ses[2]^2),
  nrow = 2
)

acs_sum(estimates, moes, cov = cov_mat)
#>   estimate      moe       se
#> 1     2200 205.2234 124.7669

For aggregation, cov_strategy = "constant" accepts a scalar correlation and constructs a valid covariance matrix from the input MOEs. This is useful for sensitivity analysis, not as an automatic estimator of ACS covariance.

acs_aggregate(
  tracts,
  group_var = "region",
  value_cols = "population",
  moe_cols = "population_moe",
  cov_strategy = "constant",
  cov_value = 0.25
)
#>   region population population_moe
#> 1  north       2200       205.9126
#> 2  south       2000       182.7567

What this package does not do

acsmoe does not download ACS data. Use tidycensus for that.

acsmoe does not estimate variance from microdata. Use survey or srvyr for PUMS and replicate-weight workflows.

acsmoe also does not implement regionalization. Walker’s tidycensus MOE article points readers to Spielman and Folch’s regionalization work and an old Python implementation. That historical code lives at https://github.com/geoss/censumander. We used it as development-only reference material for formula checks, but regionalization itself is out of scope for this package.

The boundary is intentional: acsmoe focuses on propagation of uncertainty for tabular estimate/MOE workflows after ACS data have already been obtained.

References