Skip to content

Carry ACS margins of error through cleaning, with reliability labels - #178

Merged
duaneatat merged 5 commits into
mainfrom
db/acs-moe-cleaning
Nov 3, 2026
Merged

duaneatat merged 5 commits into
mainfrom
db/acs-moe-cleaning

Conversation

@duaneatat

Copy link
Copy Markdown
Collaborator

Second of three PRs for Census margins of error, stacked on #177 (collection). Merge #177 first and delete its branch, so this PR retargets to main. The cleaned ACS tables now carry the margins collected there. Each margin gets a reliability label (high, medium or low) from its coefficient of variation. The cut points are a CV under 15% for high and over 30% for low, which is a common convention; the Census sets no official threshold. The label gives the frontend one field for flagging shaky small-town estimates.

  • The tidy tables gain value_moe, value_reliability, percent_moe and percent_reliability. A share whose margin rounds to 0.0 points takes its count's label, so tiny shares aren't marked high.
  • The data profile tables gain moe and reliability.
  • The median age, income, home value, housing units and vacancy series gain <measure>_moe and <measure>_reliability.
  • Tract margins: a code the Census couldn't compute is now missing instead of zero, zero estimates count only their largest margin, and 100% shares no longer get a zero margin.
  • Data Chat: the catalog lists the new columns, query results return each margin with its estimate, and the caveat explains how to read margins and when a difference is significant. warehouse.schema.json is regenerated from a scratch build of the ACS tables.
  • On that scratch build (2009–2024), Burlington's 2023 median household income is $68,854 ± $4,182 (high reliability), and town unemployment rates are low reliability in 239 of 256 towns.
  • Run just run-etl 2009 2024 once after merging to backfill margins.

Census code -222222222 means the margin could not be computed because
there were too few sample cases. The tract cleaner mapped it to 0, which
gave the least reliable estimates the tightest intervals. Only
-555555555 (a controlled estimate) has a zero margin.
Sums of many zero estimates overstated the margin: a zero estimate's
margin is modeled, and Census guidance counts only the largest of them
in a sum. Uninsured shares sum 15 C27016 cells, most of them zero, and
the widest tract margin was 215.8 points. Separately, a 100% share made
the proportion formula's term zero and claimed no sampling error at
all; the ratio form now covers that case, as it already did a negative
term.
The cleaned tidy, data profile, series and tract tables now keep the
margins collected in the RAW tables, and each margin gets a high,
medium or low reliability label from its coefficient of variation
(data_cleaning/margins.py). The Data Chat catalog lists the new columns
and explains how to read them. warehouse.schema.json is regenerated
from a scratch build of every ACS table (2009-2024).
Comment thread backend/data_cleaning/margins.py Resolved

@iansargent iansargent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, looks great. I enjoyed reading up on the coefficient of variation (CV). And this is also very exciting because it will allow for more statistical inference when comparing two locations (p-values, z-statistics, t-statistics, etc.)!

Left one comment idea, so lmk if it should be included in another PR.

Thanks Duane!

Add SQL helpers for the margin of a sum of estimates (root sum of squares)
and of a ratio, and use them for the age dependency ratio, the one derived
series the cleaning step computes.

@iansargent iansargent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

acs5Housing_incomeBurden_timeseries (housing_cost_burden) is the only one that needs MOE aggregation. It needs sum_moe_sql for Value and the “All households” Total, and the proportion formula for Percent.

acs5Economics_healthInsurance_timeseries (health_insurance) only needs its published margins passed through.

acs5Economics_medianEarnings_timeseries (median_earnings) also only needs its published margins passed through.

Only commenting this because I have the hunch I'm communicating with Duane's agent lol

…series

The DP03 series pass their published margins through. Housing cost burden sums
bracket and tenure margins by root sum of squares (an aggregate form of
sum_moe_sql) and gives Percent the proportion form.

@iansargent iansargent left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Some changes might have to be made in the next PR since the aggregated MOEs were slightly addressed on the frontend.

Base automatically changed from db/acs-moe-collection to main November 3, 2026 13:49
@duaneatat
duaneatat merged commit fb19103 into main Nov 3, 2026
4 checks passed
@duaneatat
duaneatat deleted the db/acs-moe-cleaning branch November 3, 2026 13:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants