Research Note · R-CITY.rent-cagr-regression · Fitted 2026-05-21

What predicts city rent growth?

Across the top-250 U.S. cities by population, we computed each city's 5-year ZORI rent-index CAGR (2021-04 → 2026-04) and pre-registered six hypotheses about what should predict it — education share, IRS net migration, unemployment, income level, population size, and asset-price co-movement. We ran an OLS regression, refused to fabricate features, and report the honest answer — including the one significant predictor that came out with the wrong sign.

Sample
n = 244
cities w/ real 2021-04 + 2026-04 ZORI
Target mean
5.14%
annualised ZORI growth, 2021–2026
R² in-sample
0.482
adjusted 0.457, F-test p < 1e-21
R² out-of-sample
+0.276
held-out 20% (n_test = 49) — positive, unlike BAH
Best predictor
ZHVI 5y CAGR
β = +0.0071, p < 0.001, r = +0.57
Question & data
Pre-registered

What we measured

One row per city, top-250 by population. Target = the 5-year compound annual growth rate of the Zillow ZORI rent index from 2021-042026-04, computed in this script directly from the monthly ZORI city series. Cities missing either endpoint were dropped, not imputed (no fabrication of the target). 6 of 250 cities dropped → n = 244.

Features: ZHVI 5y CAGR (asset-price control, mirror of BAH's H1); median household income, median age, population, lat/lon (city-level from ACS / Census); bachelor_plus_pct, unemployment_pct, IRS net migration (joined from the nearest ZCTA centroid in zip_master.json). Cities' centroids were matched to the closest ZCTA from the 2023 Census Gazetteer by haversine.

Source — Zillow Research (City ZORI / ZHVI) · Census ACS 2023 5-yr (B19013 / B01002 / B15003 / B23025) · IRS SOI 2022 migration · Census 2023 Gazetteer (centroids)
Hypotheses (stated before running the model)
Six priors, one rejected with wrong sign at p<0.001
H1
City rent CAGR co-moves with ZHVI 5y CAGR.
Asset prices and rents are driven by overlapping local demand — expect a strong positive correlation. Mirror of the BAH study's H1.
supported (dominant)  β_std = +0.0071 · p < 0.001 · bivariate r = +0.57
H2
Cities with higher college-educated share grew rents faster.
bachelor_plus_pct from the nearest ZCTA. Predicted positive: educated renters bid rents up.
inconclusive  β_std = +0.0002 · p = 0.77 · bivariate r = −0.08. Near-zero in both partial and bivariate. Education share predicts rent level, not CAGR — or the nearest-ZCTA proxy is too noisy for large multi-ZCTA cities.
H3
More IRS net migration in → faster rent growth.
Naive demand-side story: people moving in pushes rents up.
REJECTED — wrong sign at p < 0.001  β_std = −0.0030 · p = 0.0002 · bivariate r = −0.06. Cities with the biggest IRS net inflow grew rents slower. Almost certainly a COVID base-effect: Sun Belt destinations spiked rents in 2020–21, then mean-reverted; coastal origin cities (NYC, SF) recovered from a 2021 low.
H4
High-unemployment cities grew rents slower.
Predicted inverse: weak labor ⇒ weak demand ⇒ weak rent growth.
inconclusive  β_std = +0.0003 (sign opposite of pre-reg) · p = 0.72 · bivariate r = +0.10. Not significant in either direction.
H5
Higher-income cities grew rents faster (pricing-power thesis).
Predicted positive: wealthy cities have residents who can absorb rent increases.
weakly rejected  β_std = −0.0017 (wrong sign) · p = 0.097 · bivariate r = −0.29. The bivariate negative is substantial: high-income cities grew rents slower in this window. Pricing-power thesis is not supported here.
H6
Bigger cities grew rents faster (size effect).
log_population. Predicted positive: durable demand at scale.
inconclusive  β_std = −0.0010 (slight wrong sign) · p = 0.22 · bivariate r = −0.17. The size advantage doesn't show up in this window.
OLS coefficients (standardized features)
Load-bearing model
Feature β 95% CI low 95% CI high t p bivariate r
const+0.0508+0.0493+0.0523+67.20<0.001
zhvi_5y_cagr p<.001+0.0071+0.0055+0.0087+8.57<0.001+0.57
lon p<.001+0.0042+0.0023+0.0060+4.45<0.001+0.39
abs_lat p<.001+0.0031+0.0015+0.0046+3.85<0.001+0.06
irs_net_migration_2022 wrong sign−0.0030−0.0046−0.0015−3.79<0.001−0.06
median_income p<.10−0.0017−0.0038+0.0003−1.670.097−0.29
log_population n.s.−0.0010−0.0026+0.0006−1.240.215−0.17
unemployment_pct_acs n.s.+0.0003−0.0013+0.0019+0.360.717+0.10
bachelor_plus_pct n.s.+0.0002−0.0014+0.0019+0.290.772−0.08
median_age n.s.+0.0003−0.0013+0.0020+0.420.678−0.10

Read: a one-standard-deviation increase in ZHVI 5y CAGR is associated with a +0.71 percentage-point increase in city ZORI 5y CAGR — the dominant signal. The lon and abs_lat coefficients say that, after controlling for ZHVI, eastern and northern cities grew rents faster than western and southern ones (about +0.42 and +0.31 pp/yr per sigma respectively). The IRS-migration coefficient is negative and significant — the opposite of the pre-registered direction.

Source — scripts/run_regression.py · regression_results.json
Fit quality — the honest part
In-sample and out-of-sample

In-sample

R² (train, n=195)0.482
Adjusted R²0.457
F-test p-value2.2×10⁻²²
MAPE train0.185

Model is jointly highly significant. ZHVI does most of the work; geographic gradient adds genuine signal on top.

Out-of-sample

R² (test, n=49)+0.276
RMSE test0.0145
MAPE test0.168
Target std0.0149

Beats the unconditional mean out-of-sample. RMSE (0.0145) is the same order as the target std (0.0149); the model explains roughly a quarter of held-out variance. Honest: this is real signal, not a great forecaster.

Comparison to R-BAH.regression-v2
Cities beat MHAs on every metric
MetricBAH (n=177)City (n=244)
Target mean6.33%/yr BAH CAGR5.14%/yr ZORI CAGR
Target std2.30%1.49%
R² train0.2400.482
Adjusted R²0.2000.457
R² test−0.272+0.276
F-test p3.8×10⁻⁶2.2×10⁻²²
Strongest predictorZHVI 5y CAGRZHVI 5y CAGR

The city regression beats the BAH regression on every fit-quality metric. R² test is positive (0.28) rather than negative (−0.27). City-level rent CAGR is a more tractable prediction problem than MHA-level BAH CAGR — larger n, the target is the rent index itself rather than an administrative pass-through, and the geographic gradient that died in the BAH study survives here at p<0.001.

Biggest surprise
Migration sign flipped

IRS net migration is a statistically significant negative predictor of rent CAGR at p < 0.001 with the wrong pre-registered sign. We expected “more people moving in → faster rent growth”; we got “more people moving in → slower rent growth.”

The likely mechanism is a COVID base-effect inside the 2021→2026 window. Destination cities for the 2020–22 migration wave (Sun Belt, Florida, Tennessee) had already spiked their rents by 2021. The 2021→2026 CAGR captures their subsequent normalization — high level, low subsequent CAGR. Origin cities (NYC, SF) bottomed out in 2021 and recovered, giving them higher CAGR off a low base. This is exactly what a naive “follow the migration map” thesis would miss.

Operational implication: WindMayor should NOT underwrite a long-rent thesis in high-inflow destination metros purely on net-migration data. The migration signal in this window is contrarian.

What this means for the WindMayor thesis
Mostly supported, with caveats
Limitations & what we did not do
No p-hacking
Reproduce
Single command

The regression is one Python script. It reads the cities panel, the cached Zillow ZORI/ZHVI city CSVs, the Census ZCTA gazetteer, and zip_master.json — computes per-city features in parallel via ThreadPoolExecutor, fits OLS, and writes the JSON result. No external network calls; data is from cache.

$ python3 projects/city-rent-research/scripts/run_regression.py
[run] loading cities, ZORI, ZHVI, ZCTA centroids, zip_master...
[run] 250 cities loaded
[zipmaster] loaded 31,646 ZCTA records
[zillow] ZORI: 4,430 city series
[zcta] loaded 33,791 ZCTA centroids
[zillow] ZHVI all-homes: 21,356 city series
[panel] computing features for 250 cities (parallel)...
[panel] 250 rows built
[panel] dropped 6 rows missing zori_5y_cagr target -> 244 cities for fit
[panel] 244 rows after final dropna
[fit] OLS on n_train=195 features=9

=== OLS coefficient summary (standardized) ===
  const                          beta=+0.0508  t=+67.20  p=0.000
  zhvi_5y_cagr                   beta=+0.0071  t= +8.57  p=0.000 ***
  bachelor_plus_pct              beta=+0.0002  t= +0.29  p=0.772
  irs_net_migration_2022         beta=-0.0030  t= -3.79  p=0.000 ***
  unemployment_pct_acs           beta=+0.0003  t= +0.36  p=0.717
  median_income                  beta=-0.0017  t= -1.67  p=0.097 *
  log_population                 beta=-0.0010  t= -1.24  p=0.215
  abs_lat                        beta=+0.0031  t= +3.85  p=0.000 ***
  lon                            beta=+0.0042  t= +4.45  p=0.000 ***
  median_age                     beta=+0.0003  t= +0.42  p=0.678

  n        = 244
  n_train  = 195, n_test = 49
  R^2 train = 0.4819
  R^2 test  = 0.2762
  MAPE test = 0.1676