Estimating Intercensal Population for Trend Analysis
This guide, part of Small-Area Population Denominators, produces a year-by-year small-area population series between two censuses — the denominator a ten-year rate trend needs and neither census can supply on its own.
Problem Context & Constraints
A rate trend from 2010 to 2020 at tract level has a denominator problem at both ends. The 2010 census gives an exact count for 2010 that grows stale through the decade. The 2020 census gives an exact count for 2020 that did not exist before. A series that uses the 2010 count until 2019 and switches to the 2020 count in 2020 puts a step into every rate at the switch: a tract that grew by 20% over the decade shows a rate 20% too high by 2019, then drops abruptly when the denominator catches up. The step looks like a decline in disease, and it is entirely an artefact.
Intercensal estimation replaces the two point measurements with a smooth path between them, anchored exactly at both census dates. It is a model, not a measurement, and it has to be treated as one: its assumption is that population changed smoothly, which is wrong wherever a large development opened or a facility closed mid-decade.
Three constraints shape a defensible method. The estimates must be anchored exactly at both census counts, so the series agrees with the enumerations. Small-area estimates should sum to published county estimates where those exist for each year, because county estimates use administrative data — births, deaths, migration — that small-area interpolation cannot see. And boundary changes between censuses must be resolved first, since the two counts are only comparable on common geography.
Prerequisites
python3.11pandas2.2.2,numpy1.26.4- Census counts at both endpoints on common, harmonised geography — see Crosswalking 2010 to 2020 Census Tracts for Trend Analysis
- Annual published county population estimates for every intervening year
- Optionally, an annual small-area indicator such as housing unit counts, to shape the path
Step-by-Step Solution
Interpolate between the anchors — geometrically by default, or shaped by a housing indicator where one exists — then rake each year’s small-area estimates to the published county total.
# Intercensal small-area population: anchored interpolation raked to county totals.
# Pinned: pandas==2.2.2, numpy==1.26.4
import logging
import numpy as np
import pandas as pd
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
log = logging.getLogger("intercensal")
def interpolate(anchors: pd.DataFrame, years: range, shape: pd.DataFrame | None = None,
method: str = "geometric") -> pd.DataFrame:
"""anchors: geoid, county, pop_start, pop_end (common geography)
shape: optional geoid, year, indicator (e.g. housing units) to shape the path
"""
y0, y1 = years.start, years.stop - 1
rows = []
for r in anchors.itertuples():
for y in years:
t = (y - y0) / (y1 - y0)
if shape is not None:
s = shape[(shape["geoid"] == r.geoid)].set_index("year")["indicator"]
if y0 in s and y1 in s and s[y1] != s[y0]:
t = float(np.clip((s.get(y, np.nan) - s[y0]) / (s[y1] - s[y0]), 0, 1))
if method == "geometric" and r.pop_start > 0 and r.pop_end > 0:
p = r.pop_start * (r.pop_end / r.pop_start) ** t
else:
p = r.pop_start + t * (r.pop_end - r.pop_start)
rows.append({"geoid": r.geoid, "county": r.county, "year": y, "pop": p})
df = pd.DataFrame(rows)
log.info("interpolated %d areas over %d years (%s%s)", anchors.shape[0], len(years),
method, ", shaped by indicator" if shape is not None else "")
return df
def rake_to_county(est: pd.DataFrame, county_totals: pd.DataFrame) -> pd.DataFrame:
"""Scale each county-year's areas so they sum to the published county estimate.
Census years are left untouched so the anchors stay exact.
"""
s = est.groupby(["county", "year"])["pop"].sum().rename("sum_est").reset_index()
j = est.merge(s, on=["county", "year"]).merge(county_totals, on=["county", "year"],
how="left")
anchor_years = {est["year"].min(), est["year"].max()}
factor = np.where(j["year"].isin(anchor_years) | j["county_total"].isna(), 1.0,
j["county_total"] / j["sum_est"])
j["pop_raked"] = j["pop"] * factor
adj = pd.Series(factor[~j["year"].isin(anchor_years)])
log.info("raked to county totals: median factor=%.4f, range %.4f..%.4f",
adj.median(), adj.min(), adj.max())
if (adj - 1).abs().max() > 0.05:
log.warning("some county-years needed >5%% adjustment — check the anchors and "
"the boundary harmonisation for those counties")
return j[["geoid", "county", "year", "pop", "pop_raked"]]
Raking to county totals is what brings administrative information into a small-area series that otherwise knows only two numbers. A county’s annual estimate reflects births, deaths and migration each year; raking forces the tracts inside it to agree with that total while preserving their relative shares from the interpolation. The anchor years are exempted so the census counts remain exact.
The size of the raking factor is a diagnostic. Factors close to one mean the interpolation and the county estimate agree. A factor that departs substantially from one says the smooth path is missing something real — a recession, a large development, an institutional closure — and the tracts in that county deserve a closer look before their rates are trusted.
Validation & Edge Cases
Boundary changes not resolved. Interpolating between a 2010 tract count and a 2020 count for a tract that was split in 2020 produces nonsense. Harmonise both censuses to one geography first; the crosswalk’s own allocation error then enters the series.
Abrupt changes smoothed away. A facility that opened in 2016 appears as a steady rise from 2010. Where an indicator such as housing units or group-quarters counts exists, use it to shape the path; where not, flag tracts with large decade change for manual review.
Negative or zero populations. Linear interpolation between a positive and zero endpoint — a demolished housing project — produces tiny populations in late years with enormous rates. Treat those tracts separately rather than letting the arithmetic run.
County estimates revised after the fact. Published county estimates are revised with each vintage. Rake to one declared vintage of county estimates and record it; re-raking to a later vintage changes every small-area value.
Post-censal years. Extending the method beyond the last census requires projection, not interpolation, and the uncertainty grows each year. Label post-censal estimates as such and carry wider uncertainty.
INFO intercensal interpolated 1842 areas over 11 years (geometric, shaped by indicator)
INFO intercensal raked to county totals: median factor=0.9981, range 0.9412..1.0634
WARNING intercensal some county-years needed >5% adjustment — check the anchors and the boundary harmonisation for those counties
Mixing intercensal and post-censal estimates in one series. A trend from 2015 to 2025 spans the 2020 census, so its early years can be intercensal while its later years can only be post-censal projections. The two have different error properties and will be revised differently when the next census arrives. Carry a column indicating which kind each year is, and treat the post-censal end of any trend line as provisional.
Uncertainty that grows between the anchors. The estimate is exact at both census years and least certain in the middle of the decade, where it is furthest from either measurement. A trend analysis that weights every year equally treats the mid-decade denominators as though they were as good as the census ones. Where the analysis supports it, carry an uncertainty that peaks mid-decade and falls to zero at the anchors, and prefer methods that down-weight the least certain years.
Tracts that swap between urban and rural character. A tract that urbanises over the decade changes not only its population but its age structure, and a total-population interpolation says nothing about the latter. Where rates are age-standardised, interpolate each age band separately or accept that the stratified denominators are less reliable than the total in exactly the tracts that changed most.
The uncertainty shape described above is worth drawing, because it is the reason the anchor years deserve more weight than the rest:
Compliance Notes
Record the anchor censuses and their geography, the crosswalk used to harmonise them, the interpolation method and any shaping indicator with its source, the county estimate vintage used for raking, the raking factors by county and year, and the tracts flagged for manual review.
Trend products built on intercensal denominators should say so, and should identify the post-censal years whose denominators are projections. When the next census arrives, the intercensal series for the preceding decade will be re-estimated and every rate in it will move slightly — a known, scheduled revision that should be anticipated in the product’s documentation rather than surprising the partners who cited the earlier values.
Related Topics
- Small-Area Population Denominators — the parent guide on denominator sources.
- Crosswalking 2010 to 2020 Census Tracts for Trend Analysis — harmonising the two anchors onto one geography.
- Versioning Boundary Files Across Vintages — keeping each vintage of population and geography reproducible.