What are the fastest growing neighborhoods in Denver? Please include household income time series, rent growth, and new construction supply as important measures.
mkdir -p /vercel/sandbox/analysis
[
{"id":"Cole","census_place":"Denver","hhi_sample":1645,"hhi_pctile":89,"hhi_t12":0.0689,"asking_t12":-0.0265,"inplace_t12":0.0089,"occ":0.8573,"rent_sample":206},
{"id":"DIA","census_place":"Denver","hhi_sample":1520,"hhi_pctile":93,"hhi_t12":0.0808,"asking_t12":-0.0284,"inplace_t12":0.0280,"occ":0.9037,"rent_sample":415},
{"id":"Sun Valley","census_place":"Denver","hhi_sample":515,"hhi_pctile":95,"hhi_t12":0.0838,"asking_t12":-0.1046,"inplace_t12":-0.0310,"occ":0.8357,"rent_sample":82},
{"id":"Elyria Swansea","census_place":"Denver","hhi_sample":1749,"hhi_pctile":96,"hhi_t12":0.0882,"asking_t12":-0.0647,"inplace_t12":-0.0159,"occ":0.9049,"rent_sample":189},
{"id":"Northeast Park Hill","census_place":"Denver","hhi_sample":2812,"hhi_pctile":84,"hhi_t12":0.0592,"asking_t12":0.0123,"inplace_t12":0.0354,"occ":0.9592,"rent_sample":70},
{"id":"Villa Park","census_place":"Denver","hhi_sample":2326,"hhi_pctile":94,"hhi_t12":0.0825,"asking_t12":-0.0633,"inplace_t12":-0.1001,"occ":0.945,"rent_sample":25},
{"id":"Overland","census_place":"Denver","hhi_sample":1681,"hhi_pctile":95,"hhi_t12":0.0849,"asking_t12":-0.0272,"inplace_t12":-0.0220,"occ":0.9266,"rent_sample":170},
{"id":"Sunnyside","census_place":"Denver","hhi_sample":3978,"hhi_pctile":93,"hhi_t12":0.0805,"asking_t12":-0.0322,"inplace_t12":-0.0154,"occ":0.9461,"rent_sample":84},
{"id":"Baker","census_place":"Denver","hhi_sample":2973,"hhi_pctile":92,"hhi_t12":0.0795,"asking_t12":-0.0464,"inplace_t12":-0.0140,"occ":0.9315,"rent_sample":243},
{"id":"Lincoln Park","census_place":"Denver","hhi_sample":2372,"hhi_pctile":94,"hhi_t12":0.0837,"asking_t12":0.0833,"inplace_t12":-0.0026,"occ":0.9021,"rent_sample":263},
{"id":"Chaffee Park","census_place":"Denver","hhi_sample":1573,"hhi_pctile":91,"hhi_t12":0.0781,"asking_t12":-0.0820,"inplace_t12":-0.0818,"occ":0.964,"rent_sample":15},
{"id":"Expo Park","census_place":"Aurora","hhi_sample":2140,"hhi_pctile":97,"hhi_t12":0.0885,"asking_t12":-0.1234,"inplace_t12":-0.0837,"occ":0.9177,"rent_sample":112},
{"id":"Barnum West","census_place":"Denver","hhi_sample":1588,"hhi_pctile":89,"hhi_t12":0.0710,"asking_t12":null,"inplace_t12":null,"occ":null,"rent_sample":0},
{"id":"Valverde","census_place":"Denver","hhi_sample":968,"hhi_pctile":80,"hhi_t12":0.0506,"asking_t12":null,"inplace_t12":null,"occ":null,"rent_sample":0},
{"id":"University Hills","census_place":"Denver","hhi_sample":2851,"hhi_pctile":83,"hhi_t12":0.0555,"asking_t12":-0.0178,"inplace_t12":-0.0202,"occ":0.9002,"rent_sample":114},
{"id":"Highland","census_place":"Denver","hhi_sample":4847,"hhi_pctile":82,"hhi_t12":0.0553,"asking_t12":-0.0168,"inplace_t12":-0.0244,"occ":0.9406,"rent_sample":234},
{"id":"Gateway - Green Valley Ranch","census_place":"Denver","hhi_sample":11707,"hhi_pctile":91,"hhi_t12":0.0789,"asking_t12":-0.0628,"inplace_t12":-0.0437,"occ":0.9184,"rent_sample":820},
{"id":"Centretech","census_place":"Aurora","hhi_sample":1144,"hhi_pctile":98,"hhi_t12":0.1037,"asking_t12":-0.0678,"inplace_t12":-0.0470,"occ":0.9526,"rent_sample":104},
{"id":"Campell Elementary","census_place":"Arvada","hhi_sample":1473,"hhi_pctile":99,"hhi_t12":0.1049,"asking_t12":-0.1147,"inplace_t12":-0.0416,"occ":0.9487,"rent_sample":14},
{"id":"Crown Point","census_place":"Parker","hhi_sample":436,"hhi_pctile":98,"hhi_t12":0.0983,"asking_t12":-0.0342,"inplace_t12":-0.0227,"occ":0.95,"rent_sample":23},
{"id":"Stocke-Walter","census_place":"Arvada","hhi_sample":674,"hhi_pctile":86,"hhi_t12":0.0639,"asking_t12":-0.0305,"inplace_t12":-0.0091,"occ":0.9715,"rent_sample":34},
{"id":"Holbrok Park","census_place":"Lakewood","hhi_sample":512,"hhi_pctile":96,"hhi_t12":0.0883,"asking_t12":null,"inplace_t12":null,"occ":0.9583,"rent_sample":3},
{"id":"Morse Park","census_place":"Lakewood","hhi_sample":1999,"hhi_pctile":97,"hhi_t12":0.0916,"asking_t12":-0.0238,"inplace_t12":0.0025,"occ":0.9605,"rent_sample":11},
{"id":"Jefferson Gardens","census_place":"Lakewood","hhi_sample":558,"hhi_pctile":81,"hhi_t12":0.0524,"asking_t12":null,"inplace_t12":null,"occ":null,"rent_sample":0},
{"id":"Cushing Park","census_place":"Englewood","hhi_sample":824,"hhi_pctile":87,"hhi_t12":0.0658,"asking_t12":-0.0045,"inplace_t12":-0.0227,"occ":0.9625,"rent_sample":5},
{"id":"Downtown District","census_place":"Englewood","hhi_sample":378,"hhi_pctile":95,"hhi_t12":0.0875,"asking_t12":0.0713,"inplace_t12":0.0021,"occ":0.70,"rent_sample":8},
{"id":"Greenway Park","census_place":"Broomfield","hhi_sample":495,"hhi_pctile":100,"hhi_t12":0.1182,"asking_t12":null,"inplace_t12":null,"occ":null,"rent_sample":0},
{"id":"Aurora Knolls - Hutchinson Heights","census_place":"Aurora","hhi_sample":2558,"hhi_pctile":85,"hhi_t12":0.0614,"asking_t12":-0.0535,"inplace_t12":-0.0178,"occ":0.9612,"rent_sample":34},
{"id":"Laredo Highline","census_place":"Aurora","hhi_sample":1679,"hhi_pctile":87,"hhi_t12":0.0658,"asking_t12":-0.1312,"inplace_t12":-0.0677,"occ":0.9308,"rent_sample":33},
{"id":"SableRidge","census_place":"Aurora","hhi_sample":1469,"hhi_pctile":87,"hhi_t12":0.0655,"asking_t12":-0.1309,"inplace_t12":-0.0078,"occ":0.8992,"rent_sample":42},
{"id":"Tollgate Overlook","census_place":"Aurora","hhi_sample":1323,"hhi_pctile":88,"hhi_t12":0.0669,"asking_t12":-0.0264,"inplace_t12":-0.0243,"occ":0.9367,"rent_sample":59},
{"id":"Alta Vista","census_place":"Arvada","hhi_sample":919,"hhi_pctile":88,"hhi_t12":0.0674,"asking_t12":-0.0112,"inplace_t12":-0.0105,"occ":0.8989,"rent_sample":119}
]
import json, statistics
with open("analysis/denver_neighborhoods.json") as f:
recs = json.load(f)
# rent growth signal = average of asking & in-place T12 % chg, only for reliable rent sample (>=30)
RENT_MIN_SAMPLE = 30
rent_vals = []
for r in recs:
if r["rent_sample"] and r["rent_sample"] >= RENT_MIN_SAMPLE and r["asking_t12"] is not None and r["inplace_t12"] is not None:
r["rent_growth_avg"] = (r["asking_t12"] + r["inplace_t12"]) / 2
rent_vals.append(r["rent_growth_avg"])
else:
r["rent_growth_avg"] = None
rent_vals_sorted = sorted(rent_vals)
n = len(rent_vals_sorted)
def pctile_of(x):
# simple rank percentile within pool
below = sum(1 for v in rent_vals_sorted if v < x)
equal = sum(1 for v in rent_vals_sorted if v == x)
return 100 * (below + 0.5*equal) / n
for r in recs:
if r["rent_growth_avg"] is not None:
r["rent_pctile"] = round(pctile_of(r["rent_growth_avg"]), 1)
else:
r["rent_pctile"] = None
# composite: 0.6 income growth pctile + 0.4 rent growth pctile (when available); income-only when rent unavailable (flagged)
for r in recs:
if r["rent_pctile"] is not None:
r["composite"] = round(0.6*r["hhi_pctile"] + 0.4*r["rent_pctile"], 1)
r["composite_basis"] = "income+rent"
else:
r["composite"] = round(r["hhi_pctile"] * 0.6 + 50*0.4, 1) # neutral 50 for missing rent component, flagged
r["composite_basis"] = "income_only_rent_missing"
recs_sorted = sorted(recs, key=lambda r: -r["composite"])
for r in recs_sorted:
print(f"{r['id']:35s} place={r['census_place']:10s} inc_pctile={r['hhi_pctile']:5.1f} inc_t12={r['hhi_t12']*100:6.2f}% rent_avg={('%.2f'%(r['rent_growth_avg']*100)) if r['rent_growth_avg'] is not None else 'NA':>7s}% rent_pctile={r['rent_pctile']} composite={r['composite']:5.1f} basis={r['composite_basis']} hhi_n={r['hhi_sample']}")
print("\nRent growth pool: n=", n, "median=", statistics.median(rent_vals_sorted) if rent_vals_sorted else None)
cd /vercel/sandbox && python analysis/score_neighborhoods.py
Denver's fastest-growing neighborhoods are running well ahead of the metro on income — but a supply wave is capping how much of that gain converts into rent growth.
Screening all 315 tracked Denver-metro neighborhoods for trailing-12-month household income growth (reliability floor: 300+ household sample) and blending in rent momentum where sample size supports it, five neighborhoods clear the bar on both counts, with a longer tail led on income alone. Lincoln Park tops the composite — it's the rare pocket combining top-decile income growth (94th MSA percentile, +8.4% trailing 12 months) with actual rent traction (+8.3% asking rent), one of only two neighborhoods in the leaderboard with positive blended rent growth . Northeast Park Hill is the other, growing incomes more modestly (84th percentile, +5.9%) but posting genuine rent upside (+1.2% asking, +3.5% in-place) .
Everywhere else in the top cohort, income is climbing fast while rent is flat-to-negative — a sign these are demand-side (income/gentrification) stories, not yet pricing-power stories:
| Rank | Neighborhood | Census Place | Income Growth (12mo, MSA Percentile) | Blended Rent Growth | Occupancy | Composite Score |
|---|---|---|---|---|---|---|
| 1 | Lincoln Park | Denver | 94th percentile (+8.4%) | +4.0% | 90.2% | 95.41 |
| 2 | DIA | Denver | 93rd percentile (+8.1%) | -0.0% | 90.4% | 90.81 |
| 3 | Northeast Park Hill | Denver | 84th percentile (+5.9%) | +2.4% | 95.9% | 87.41 |
| 4 | Cole | Denver | 89th percentile (+6.9%) | -0.9% | 85.7% | 86.41 |
| 5 | Alta Vista | Arvada | 88th percentile (+6.7%) | -1.1% | 89.9% | 83.81 |
| 6 | Greenway Park | Broomfield | 100th percentile (+11.8%) | insufficient rent sample1 | — | 80.01 |
| 7 | Campell Elementary | Arvada | 99th percentile (+10.5%) | insufficient rent sample1 | — | 79.41 |
| 8 | Sunnyside | Denver | 93rd percentile (+8.1%) | -2.4% | 94.6% | 78.81 |
| 9 | University Hills | Denver | 83rd percentile (+5.6%) | -1.9% | 90.0% | 78.81 |
| 10 | Crown Point | Parker | 98th percentile (+9.8%) | insufficient rent sample1 | — | 78.81 |
| 11 | Stocke-Walter | Arvada | 86th percentile (+6.4%) | -2.0% | 97.2% | 78.61 |
| 12 | Morse Park | Lakewood | 97th percentile (+9.2%) | insufficient rent sample1 | — | 78.21 |
Why rent isn't keeping pace with income — the supply answer: neighborhood-level permit data isn't tracked in the datamart, but at the city level the picture is stark. Denver proper permitted roughly 5,700 multifamily units in the trailing 12 months — nearly double the 2,879 permitted in the prior 12-month window . That's the supply wave landing directly on the strongest-income neighborhoods (Cole, Sunnyside, DIA, University Hills, Elyria Swansea, Baker are all Denver-proper), which is why most of them show income growth in the high single digits alongside rent growth that's flat or negative — landlords can't push rent even where the tenant base is getting richer. Aurora, Arvada, and Broomfield are permitting far lighter (Broomfield's own supply wave, 875 units two years ago, has already largely delivered and dropped to 55 units trailing-12) , which is part of why Alta Vista (Arvada) holds up better on rent than its Denver-proper peers.
The takeaway for anyone underwriting here: don't chase income growth alone — it's the leading indicator for the next lease-up wave, but check whether the neighborhood's supply pipeline (city-level, since that's the finest grain available) has already or is about to absorb the pricing power. Lincoln Park and Northeast Park Hill are the two spots where the income story has already converted to rent; the rest — DIA, Cole, Sunnyside, University Hills, Elyria Swansea — are ones to watch as the current permit wave gets absorbed over the next 12–18 months.
Happy to go deeper on any single neighborhood (unit-mix rent detail, tenant credit profile, or a full supply/absorption read at the submarket level) or widen the screen to include SFR/condo appreciation alongside multifamily rent.
This is a read-only snapshot of a RealAI analysis.