Two scores, two questions
The question behind this map is where in the Chicago area you can get the best mix of affordability, neighborhood quality and day-to-day practicality. That is really two questions, so the page answers them separately.
- Fit to you (your preferences): how close a zip comes to the targets you set. It changes with every target and weight, and it says nothing about whether a place is cheap for what it is.
- Price vs expected (a fixed model): whether a zip sells for less or more per square foot than zips with the same measured schools, safety, commute, walkability, train access, home age and state. It is the same for everyone and ignores your targets.
A desirable zip isn't automatically good value, and a zip that's cheap for its features isn't automatically desirable. The chart above puts the two together.
How the fit score works
- You give each factor a target (the value you want) and a weight (how much it matters). Those are your choices. Everything after that is a fixed rule.
- Percentiles put every factor on the same scale. Minutes, dollars and crime rates can't be compared directly, so each value is replaced by its percentile among the area's zips. A zip at the 70th percentile for price costs more than 70% of zips. Your target is converted the same way.
- Match, 0 to 100. A factor's match is 100 minus the gap, in percentile points, between the zip and your target. If your target is at the 40th percentile and the zip is at the 55th, the match is 85. For schools, safety, price and train station, doing better than your target counts as 100. For commute, walkability and home age, missing in either direction costs points.
- Fit score, 0 to 100. This is the weighted average of the matches: multiply each match by its weight, add them up, and divide by the total weight. A factor weighted 3 counts three times as much as one weighted 1. The detail panel shows the points each factor costs, and they add up to 100 minus the score.
- Missing data is left out, not guessed. The average uses only the factors the zip has data for. A zip is shown but not ranked when the missing factors carry more than 20% of your weight. (Until Oct 22, 2026, a missing value counted as an average one, which flattered zips whose police didn't report crime.)
- With two people, “best for both” is the lower of the two scores, so a zip ranks high only if it suits both of you.
- Related factors. Walkability and distance to a train station move together closely: their correlation across zips is −0.82, on a scale from −1 to 1. Safety and schools move together less strongly. Weighting both factors in a related pair counts much the same thing twice, and the page warns you when that happens.
How “price vs expected” works
This part is a multiple linear regression, which finds the straight-line formula that best predicts a zip's price per square foot from its measured factors. It uses the six factors plus two yes/no terms: one for Indiana (different taxes and tests) and one for the city of Chicago (one school district for the whole city).
log($/sq ft) = b0 + b1·schools + b2·log(1 + crime) + b3·commute + b4·walkability + b5·log(miles to station + 0.25) + b6·home age + b7·Indiana + b8·Chicago
Price is on a log scale, so each coefficient (b1, b2, …) works as a percent change. For example, the table below says one more percentile of school scores goes with prices about 0.5% higher, holding the other factors fixed. Crime and station distance are also logged, because going from 1 to 2 miles matters more than going from 10 to 11. The formula's R² is 0.67, meaning it explains about two thirds of the variation in price across 338 zips.
A zip's expected price is what the formula predicts for it. That prediction comes from a version of the model fitted without that zip, so the zip's own price can't pull the prediction toward itself. Prices within ±10% of expected count as in line. 86 zips are below that range and 99 above it.
Confidence is high, medium or low, depending on how many of these doubts apply to a zip:
- It had fewer than 50 sales. A median from few sales is noisy: about ±6% at 50 sales and ±3% at 200, judged by how much a zip's median moves from year to year.
- A factor is missing.
- It is in Chicago, where the model can't see school differences between neighborhoods.
- It lands in a different class (below, in line or above) in most of 12 other reasonable versions of the model, or with last year's prices.
- The gap is small enough that price noise alone could erase it.
Right now 159 zips are high confidence, 107 medium and 72 low.
What it does not mean. “Below expected” does not mean “undervalued”, and it is not a forecast. The gap is the residual, the part of the price the formula can't explain. It holds everything the model doesn't measure: lot size, condition, taxes, flood risk, a lake view, school quality inside Chicago, or a town's finances. The zips furthest below expected are mostly places with long-running disinvestment, such as Gary, Harvey and parts of Chicago's South and West Sides. That history is exactly what the measured factors miss. The gaps also barely change over time. They correlate at 0.97 with the same model run on last year's prices (0.94 with 2023's), so they are lasting differences between places, not bargains waiting to close.
Is the model any good?
A model always looks better on the data it was fitted to, so the fair test is to predict zips it hasn't seen. This is cross-validation: fit the model on 90% of the zips, predict the other 10%, and repeat ten times so every zip is predicted once. With spatial folds, the held-out 10% is a cluster of neighboring zips, so no zip is predicted with help from the zip next door. That's the stricter test, because neighbors share things no model measures. “Typical error” is the median miss in price per square foot. R² on held-out zips can be below zero, which means the model did worse than predicting the overall average for every zip.
The pre-registered model and the one after it barely beat a flat average once neighbors are held out. The current model cuts the spatial error by about a fifth and explains about half the variation in areas it hasn't seen. Adding the housing mix (share of single-family homes, number of rooms) didn't improve predictions, so it is kept only as a check.
Reading the p-values. A p-value is the chance of seeing an effect at least this large if the factor truly had no effect. Small values, such as under 0.05, suggest the effect is real. The usual formula assumes each zip's residual is independent of the others, but neighboring zips still miss in the same direction. A measure of that clustering called Moran's I is 0.26, where 0 would mean no clustering (it was 0.59 for the pre-registered model). So the “robust” p-values are too optimistic. The “clustered by county” column allows zips in the same county to be related and is the more cautious one to trust.
The data
- Schools: district test scores, as a percentile within the state (Illinois Report Card, Indiana DOE). Chicago is one district, so every city zip gets the same value.
- Safety: crimes per 1,000 residents a year: violent plus property offenses reported to the FBI by the zip's police agency. Inside Chicago, the city's FBI total is spread across zips by where the city's own incident data puts the same offenses, so city neighborhoods are no longer all the same. Offenses are counted where they happen, so downtown zips with few residents get very high rates.
- Commute: the Census average, or the drive time to where you work (OpenStreetMap, no traffic).
- Train station: straight-line miles to the nearest Metra, CTA 'L' or South Shore Line station, averaged over where people in the zip live (Census 2020 population centers).
- Walkability: EPA index, 1 to 20.
- Home age and rent: Census ACS 2020–2024. Rent is per person: the median rent divided by the average bedrooms of rented homes, so a $2,500 two-bedroom is $1,250 each.
- Price: median sale price and price per sq ft, June 2025 to May 2026, all home types together (Redfin). Zips need at least 20 sales.
Limits
- A zip averages very different streets, so treat a result as a place worth a visit, not a verdict.
- The measured factors leave out a lot that people pay for: lot size, condition, taxes and fees, flood and noise risk, parks and the lake, and school quality within Chicago.
- Crime counts depend on what each agency reports; 38 zips have no usable figure, and small towns' rates are noisy.
- Prices are one year of medians, not individual sales, and mix condos and houses. There's no trend or forecast here: nothing on this page predicts appreciation.
- Renters only get the fit score: ACS rents are five-year averages that include long-time tenants, too blunt for a “rent vs expected” estimate.
- Race and ethnicity, and proxies for them, aren't used anywhere.
The pre-registered test (a simpler price model, decided before prices were downloaded) and every later change, with dates and reasons, are in the repo on GitHub, along with the code behind every number here.