Housing Forecast Methodologies
Comparison Communities Methodology
Overview
The "How does this place compare to similar communities?" card surfaces a focus community alongside nine comparison entries: its state plus eight peer communities. For municipalities, the peers come from a precomputed national peer model that combines substantive feature similarity (demographics, income, housing, employment) with geographic proximity, so the chosen peers are not just statistical lookalikes — they tend to share regional context as well. For counties, the comparison set is instead the county's eight most-populous member municipalities.

The peer model is a national computation: every municipality in the CommunityScale geography reference — Census places, county subdivisions in the states where those are the functioning municipal units, and consolidated city-counties — is compared against every other, roughly 30,000 in all. A municipality enters the pool if it has a reported population and a mapped boundary, and peers are always drawn from the full national pool: a town in Massachusetts can match a town in Wisconsin if the data says they are alike.
The published peer sets (app/municipality_peer_sets in the data lake) are keyed by (experiment_id, geoid). The card reads the active experiment, takes the top-ranked entries from default_peer_geoids, and renders them next to the focus geo.
What goes into the comparison
Each municipality is described by roughly 100 features pulled from public data:
| Domain | Source | Examples |
|---|---|---|
| Population & age structure | ACS B01001 | Total population; shares under 18, 18–34, 35–64, and 65+ |
| Income | ACS B19013, B19001 | Median household income; full 16-bracket income distribution |
| Race & ethnicity | ACS B19001A–I | Household shares by race/ethnicity of householder |
| Education & poverty | ACS B15003, B17001 | Bachelor's-plus share, less-than-high-school share; poverty rate |
| Household composition | ACS B11012 | Married-couple, cohabiting, living-alone, and with-children shares |
| Housing stock | ACS B25001, B25024, B25034, B25035 | Total units; single-family / small and large multifamily / mobile-home mix; year-built era shares, structure age, vintage diversity |
| Tenure & vacancy | ACS B25002, B25003, B25004 | Owner share, vacancy rate, vacancy composition (for-rent, for-sale, seasonal, other) |
| Housing conditions | ACS B25016 | Overcrowding share, incomplete-plumbing share |
| Cost burden | ACS B25070, B25091 | Renter and owner cost-burden and severe-cost-burden shares |
| Home values | Zillow (via the housing-affordability dataset) | Typical home value; value-to-income and required-to-median-income ratios; 5-yr and 10-yr price change |
| Jobs | LODES WAC (via the job-growth dataset) | Total jobs, 5-yr and 10-yr job growth and trend |
| Size & form | Census TIGER | Land + water area; population density (people per land square mile) |
For the core measures we also compute trajectories: the latest value, the five-year change, the ten-year change, and for the size measures a fitted ten-year slope. This means two communities that look identical today but have moved very differently over the last decade will be treated as less similar than their snapshot suggests. Population, household, housing-unit, job, income, and home-value levels enter on a log scale, so "twice as big" means the same thing for a village and a metropolis.
Missing data
Small municipalities frequently have suppressed or missing ACS cells. The pipeline:
- drops any feature that is missing for more than half of municipalities nationwide;
- standardizes the remaining features (z-scores) so no single high-magnitude metric — typically home value — dominates the distance;
- imputes missing values at the national mean (a neutral value that neither attracts nor repels matches) and appends a low-weight missingness flag per feature, so two municipalities missing the same data read as slightly more alike than a pair where one is measured and the other is not;
- tracks, for every candidate pair, how many features are observed for both communities. Pairs sharing fewer than half of the features are flagged low confidence and pushed below all fully-measured matches in the ranking.
How peers are chosen
The selection runs in four stages:
1. Candidate retrieval
For each focus municipality we pull two candidate pools and merge them:
- the top 100 substantive candidates — nearest neighbors by L2 distance in the standardized feature space; and
- the 30 geographically closest municipalities by straight-line distance.
Including the geographic neighbors guarantees nearby places are at least considered, even when their statistics differ.
2. Combined ranking
Merged candidates are reranked by a score that starts from substantive similarity and subtracts a bounded bonus for physical proximity:
score = min-max(substantive distance) − wlocality × prox(miles)
where:
- min-max(substantive distance) rescales the feature-space distance to 0–1 within the candidate pool (0 = most similar candidate, 1 = least);
- prox(miles) = 1 − log(1 + miles) / log(1 + 200), a proximity bonus that decays logarithmically with straight-line distance and reaches zero at 200 miles;
- wlocality is an adaptive locality weight between 0.05 and 0.45 that depends on the focus municipality's population, interpolated on a log scale between 5,000 and 250,000 people. A town of 5,000 or fewer gets the maximum weight (0.45): its peers should mostly share its regional context, and its feature vector is noisier. A city of 250,000 or more gets the minimum (0.05): its peers are found nationally, on substance almost alone, since its nearest geographic neighbors are often unlike it in scale.
Candidates are then sorted by the low-confidence flag first (peers we are less sure of get pushed to the back), then by the combined score, and the top 20 candidates per municipality are kept. The first 10 form the default peer set; the card displays the top 8 of those alongside the focus community and its state.
3. Regional groupings
Each of the 20 candidates is annotated with its geographic relationship to the focus community (same county, same CBSA, same state, different state), and separate rank sequences are recorded for three filtered views:
- Nearby — same county, same CBSA, or within 250 miles in the same state.
- Outside CBSA — peers from a different metro area.
- Outside state — peers from a different state.
These segments are not surfaced on the public dashboard today, but they let internal analyses ask "what would this list look like if we forced peers to be regional / non-regional?"
4. Geographic context graph (diagnostic)
Alongside the ranking, the pipeline builds a national municipality graph that encodes regional structure. Each municipality is connected to:
- its 10 nearest municipalities by straight-line distance (capped at 200 mi),
- the 10 nearest municipalities in the same county (capped at 150 mi),
- the 15 nearest municipalities in the same CBSA (capped at 250 mi), and
- a 5-neighbor fallback within the same state (capped at 300 mi) for places outside any CBSA,
with edge weights that decay with distance. The graph is embedded into a 32-dimensional vector space using Node2Vec (walks of length 40, ten walks per node, a window of 10, and a fixed random seed for reproducibility; a spectral embedding is the fallback if Node2Vec is unavailable). The embedding captures multi-hop regional structure — two suburbs of the same metro read as close even when they sit on opposite sides of it.
In the current model the embedding distance is published as a diagnostic alongside every candidate pair (geo_distance); the ranking itself uses the explicit distance-based proximity bonus described above.
Output datasets
The pipeline publishes three datasets to the data lake; the card reads the first.
app/municipality_peer_sets — the serving artifact, one row per (experiment_id, geoid):
| Column | Type | Notes |
|---|---|---|
experiment_id | text | Hash-based id encoding the feature set, parameters, and seed used to build the table. Changes whenever the pipeline reruns with different settings. |
geoid | text | Focus municipality GEOID. |
default_peer_geoids | JSON array | Top 10 peers, ordered by rank. |
default_peer_names | JSON array | Place names matching default_peer_geoids. |
app/municipality_peer_candidates — the full top-20 candidate list per municipality, with per-pair substantive distance, geographic-embedding distance, distance in miles, region relation, the nearby / outside-CBSA / outside-state view ranks, shared-feature coverage, and the low-confidence flag.
app/municipality_peer_graph_edges — the geographic context graph's edges, kept inspectable for analysis.
What the card itself shows
The comparison set is 10 entries: the focus community, its state, and — for municipalities — its top 8 ranked peers. County dashboards use the same card with a different composition: the county, its state, and the 8 most-populous municipalities whose primary county it is.
For that set, the card renders one full-width time-series chart at a time, from 2010 through the latest ACS release, with a dropdown to switch among grouped metrics:
- People — residents under 18, median age, population 65+.
- Households — household growth (indexed to 2010), average household size, family households, households headed by someone 65+.
- Income — median household income (inflation-adjusted to the latest year's dollars).
- Housing cost — median home value and median gross rent (both inflation-adjusted).
- Cost burden — cost-burdened renters and cost-burdened homeowners (30%+ of income on housing).
- Tenure & stock — homeownership rate, vacant units, single-family share.
A companion table card presents the same comparison set with current-year values side by side.
Versioning and refresh
The active experiment_id is pinned in the card's data module (cards/charts/comparison_communities/_peer_set.py). Peer relationships change slowly — they are driven by 5-year ACS estimates — so the peer pipeline is excluded from the routine data-refresh schedule and rerun on demand. When it is rerun with new parameters or features, the id changes and the card switches to the new set only when the pinned id is deliberately updated, so a rebuild never silently changes the peers shown on dashboards. Old experiment_id rows remain queryable for backfills and analysis.
Limitations
- Coverage varies by state convention. Census Designated Places, county subdivisions in non-strong-MCD states, and unincorporated areas all map to "municipality" in CommunityScale's terminology, but each ACS table covers them slightly differently. A peer in a CDP-heavy state and a peer in a strong-MCD state are not perfectly comparable.
- Feature noise grows with smallness. Sub-5,000-population municipalities have noisy ACS estimates. The adaptive locality weight partially offsets this — small communities lean on regional peers — but peers for very small municipalities should be read as suggestive rather than authoritative.
- Straight-line distance. The proximity bonus uses straight-line miles, not travel time, so it does not account for terrain, water barriers, or road networks.
- No causal claim. Peers are similar on the dimensions we measure. Two peer communities can still have very different policy environments, school systems, infrastructure histories, and so on. The card is a starting point for comparison, not a substitute for local knowledge.