Methodology & sources
Data sources
- Demographic, social, economic, and housing variables: U.S. Census Bureau, American Community Survey 5-year estimates for 2020–2024, fetched via the Census Reporter API (a no-key cache of Census API data maintained by the Census Reporter project).
- 2020 Decennial Census counts (race, age, total population): Pulled via the Census API (tract-level
P1_001Ntotal population) and via an IPUMS NHGIS extract of 2020 DHC tables P9 (Hispanic × Race) and P12 (Sex by Age). These are 100% counts, not survey estimates — no margin of error. They show up alongside the ACS-equivalent variables (e.g., "White non-Hispanic (2020 count)" sits next to "White non-Hispanic" from ACS), so you can compare and use the more precise count where 5-year-stale data is acceptable. - Tract geometry: NYC Department of City Planning, 2020 Census Tracts (shoreline-clipped). Tract polygons are simplified to roughly 10% of their original resolution to keep the page lightweight.
- Crime rates: NYC Open Data, NYPD Complaint Data — Historic plus Year-To-Date, restricted to the seven major felonies and aggregated to tracts by point-in-polygon. Window is a rolling 12 months ending on the most recent date present in the data.
- Election results: NYC Board of Elections citywide ED-level CSVs (vote.nyc) for the 2020 and 2024 general elections (president); pre-aggregated mayoral results from nyc-election-archive for 2021 and 2025 mayoral generals. ED polygons aggregated to 2020 census tracts via centroid point-in-polygon.
- Rent-stabilized units: JustFix.nyc's annual compilation of NYC Department of Finance tax-bill scrapes — buildings carrying a "rent-stabilization fee" line item on their property tax bill. BBLs joined to lat/lon via NYC DCP PLUTO, then aggregated to 2020 census tracts by point-in-polygon. Citywide totals (~994K units in ~42K buildings, 2024) closely match NYC HCR's official ~974K count.
- Public-school enrollment by school location: NYC DOE, Demographic Snapshot (c7ru-d68s) for K-12 grade counts plus School Locations (wg9x-4ke6) for lat/lon. Latest year per school (most recently 2021-22). Includes DOE-managed charter schools; excludes private and parochial schools.
Geography
All five New York City boroughs at the 2020 census tract level — 2,325 tracts in total. A tract is a small statistical area that typically contains 1,200–8,000 people. Tract boundaries are stable enough to compare across years but small enough to surface meaningful neighborhood-scale differences.
The "Borough" and "NTA" (Neighborhood Tabulation Area) labels in the detail panel come from NYC DCP's official 2020 designations. Tracts that fall in parks, cemeteries, airports, or industrial areas may show small or zero population.
How values are calculated
Every variable in the map either comes directly from an ACS estimate (medians and totals) or is computed as a share, using the denominator the Census Bureau publishes alongside that table. For example:
- Poverty rate =
B17001_002(residents below poverty) ÷B17001_001(residents for whom poverty status was determined). The denominator excludes people in institutional group quarters, which is why the rate is not divided by total population. - Share Hispanic or Latino =
B03002_012÷B03002_001. The race/ethnicity shares come from table B03002, which separates Hispanic ethnicity from race so the four categories shown (white non-Hispanic, Black non-Hispanic, Asian non-Hispanic, Hispanic any race) do not double-count. - Population density = total population ÷ land area in square miles. Land area is computed from the shape area of the shoreline-clipped tract polygon (NAD83 NY State Plane Long Island, ftUS).
Full variable list
| Field | Source table / formula |
|---|---|
| Total population | B01003_001 |
| Population density | B01003_001 ÷ tract land area (sq mi) |
| Median age | B01002_001 |
| Share under 18 | Σ B01001 male+female under-18 cells ÷ B01003_001 |
| Share 65 and over | Σ B01001 male+female 65+ cells ÷ B01003_001 |
| White, Black, Asian (non-Hispanic), Hispanic | B03002_003 / 004 / 006 / 012 ÷ B03002_001 |
| Median household income | B19013_001 |
| Household income bands | Sums of B19001 cells ÷ B19001_001 |
| Poverty rate | B17001_002 ÷ B17001_001 |
| Receiving SNAP | B22010_002 ÷ B22010_001 |
| Households / avg household size | B11016_001 / B25010_001 |
| Owner-occupied homes | B25003_002 ÷ B25003_001 |
| Median gross rent / home value | B25064_001 / B25077_001 |
| Median rent burden | B25071_001 (median rent as % of household income) |
| Bachelor's+ / HS only | Σ B15003_022–025 / Σ B15003_017–018 ÷ B15003_001 |
| Foreign-born | B05002_013 ÷ B05002_001 |
| Not a U.S. citizen | B05002_021 ÷ B05002_001 |
| Non-English at home | (C16001_001 − C16001_002) ÷ C16001_001 |
| In labor force (16+) | B23025_002 ÷ B23025_001 |
| Commute mode shares | B08301_010 / 019 / 021 ÷ B08301_001 |
| Veterans (18+) | B21001_002 ÷ B21001_001 |
| Unemployment rate | B23025_005 ÷ B23025_003 (BLS-standard: unemployed ÷ civilian labor force, not ÷ total labor force) |
| No home internet | B28002_013 ÷ B28002_001 (no subscription and no other internet access at the residence; households with free access but no subscription are in B28002_012 and are not counted here) |
| Households with no vehicle / 2+ vehicles | B08201_002 / Σ B08201_004-006 ÷ B08201_001 |
| Average vehicles per household | Σ (bucket midpoint × bucket count) ÷ B08201_001 (4+ bucket treated as 4.5) |
| Homeowners / renters without a vehicle | B25044_003 ÷ B25044_002 / B25044_010 ÷ B25044_009 |
| K-12 students in public / private school (by residence) | Σ B14002 K-12 public (or private) male + female cells ÷ total K-12 students. Tracks where the students live. |
| Public-school K-12 enrolled (by school location) | Σ DOE Demographic Snapshot enrollment by school (latest year), aggregated to the tract containing each school's address. Tracks where the schools are. |
| Major-felony rate | (Σ NYPD complaints with KY_CD ∈ {101, 104, 105, 106, 107, 109, 110}) ÷ population × 1,000 |
| Violent-crime rate | (Σ NYPD complaints with KY_CD ∈ {101, 104, 105, 106}) ÷ population × 1,000 |
| Property-crime rate | (Σ NYPD complaints with KY_CD ∈ {107, 109, 110}) ÷ population × 1,000 |
Election details
Vote shares are reported as the candidate's share of major-candidate votes — not of all ballots cast. For the presidential races and the 2021 mayoral race, this means D ÷ (D + R), so the values are directly comparable across years even when third-party candidates differ. For 2025 mayoral, the denominator is Mamdani + Cuomo + Sliwa (the three major candidates); minor candidates are not counted in the denominator.
Each candidate's vote total includes all of their ballot lines. Biden 2020 counts Democratic + Working Families; Harris 2024 counts Democratic + Working Families; Trump 2020 and 2024 count Republican + Conservative; etc. Cross-endorsed ballot lines are summed because they represent votes for the same candidate.
NYC redrew Election District boundaries after the 2020 redistricting cycle, so 2020/2021 results use the pre-redistricting ED geometry (~5,700 EDs) and 2024/2025 results use the post-redistricting geometry (~4,070 EDs). Both are aggregated to the same 2020 census tract boundaries. Because EDs are typically much smaller than tracts (~2–3 EDs per tract on average), the centroid-based join is reliable.
Tracts with fewer than 25 total major-candidate votes for a given election are suppressed (shown as "no data") for that election, because vote shares from tiny samples are noisy.
Rent-stabilized units — how the unit count is derived
NYC's official rent-stabilization registry, maintained by the state's Division of Housing and Community Renewal (DHCR), does not publish unit counts per building. The annual Rent Guidelines Board lists released by NYC RGB are PDFs of building addresses only — no unit data.
The numbers on this map come from a workaround that exploits NYC's tax system. The Department of Finance charges every rent-stabilized building an annual per-unit "Rent Stabilization Fee" on its property tax bill (recently $15 per registered unit, plus a flat administrative charge). The fee line item appears on the property's annual DOF Statement of Account PDF. Divide that fee by the per-unit rate and you've reverse-engineered the registered unit count for that building. The JustFix.nyc team's nyc-doffer project scrapes every NYC tax lot's Statement of Account PDFs annually, extracts the fee, and publishes per-BBL unit counts back to 2018. That's what's aggregated to tracts here.
What the unit counts actually mean and don't mean:
- They are "billed" units, not "currently occupied stabilized" units. A building paying the fee for 50 units may have fewer than 50 still under active stabilization — vacancy decontrol, deregistration in process, and other transitions show up in DOF rolls with a lag.
- Chronic landlord under-registration probably means the true universe is higher. Tenant advocates have long argued that many stabilized units never get registered with HCR (the fee is small enough that some landlords skip it). The map's number is the billed count, which is a floor on the real count, not a ceiling.
- The citywide total cross-checks well. Tract sums for 2024 add to ~994,000 units across ~42,400 buildings. NYC HCR's most recent official building count (2023) is ~42,000 buildings; the standard cited unit total is ~974,000. The ~2% gap reflects the timing/registration mismatches above.
- For the building count specifically, the on-tax-bill list and the DHCR registration list are not identical universes. A handful of buildings appear in one but not the other; the differences are small enough that the tract-level pattern is reliable.
Crime rate details
NYPD's "seven major felonies" (sometimes called Index 7) are the historical FBI Part I categories the department reports for CompStat. We split them into the two standard buckets:
- Violent — murder & non-negligent manslaughter (KY_CD 101), rape (104), robbery (105), felony assault (106).
- Property — burglary (107), grand larceny (109), grand larceny of motor vehicle (110).
- Major-felony total — the sum of the two.
Each complaint is filed to the date the incident was reported to police (RPT_DT), not the date it allegedly occurred — that's the field NYPD uses for its own performance reporting, and the only one that's reliably populated.
Complaints are placed on the tract map by point-in-polygon on the NYPD-reported latitude/longitude. The denominator is total population from the same ACS table used elsewhere on this page (B01003_001), so the rate is per 1,000 residents, not per 1,000 daytime population or per 1,000 visitors. Tracts with fewer than 50 residents (parks, cemeteries, airports, transit yards) are shown as "no data" on the rate maps because the denominator is too small to be meaningful — a single felony in a 12-person tract would produce an 83-per-1,000 rate.
The "rolling 12 months" window spans the most recent 365 days for which combined data are available. Because the Historic dataset is updated quarterly and the YTD dataset daily, this can shift forward each time the build script is re-run.
Margins of error and reliability
ACS is a sample, not a count. Every cell estimate comes with a margin of error (MOE) at 90% confidence. For tracts with small populations or small subgroups, the MOE can be a substantial fraction of the estimate itself — sometimes larger than the estimate. We surface this in three places:
- Tooltips show "± X" alongside the value, with a colored badge indicating the Census Bureau's three reliability tiers: reliable (MOE ≤ 20% of estimate; CV ≤ 0.12), use with caution (20–66%; CV 0.12–0.40), and unreliable (>66%; CV > 0.40).
- The tract-detail panel shows ± for every variable that has a propagated MOE.
- A "Fade unreliable estimates" checkbox in the sidebar adds a 25% opacity overlay and dashed border to any tract whose value falls in the "unreliable" tier — so users can see uncertainty visually without losing the underlying data.
MOEs for derived shares are propagated using the Census Bureau's standard formulas from the ACS General Handbook (Appendix 3): moe(p) = √[moe(x)² − p² · moe(y)²] / y for proportions where the numerator is a subset of the denominator (with a fall-back to the independent-ratio formula when the radicand is negative). Sum-of-cells MOEs use √Σmoe². Medians use the published MOE directly.
The undocumented-population proxy
The U.S. Census Bureau does not ask about immigration status, so there is no direct tract-level count of undocumented residents. Every published "undocumented by tract" figure is a model output, not a measurement. The closest publicly available variable that gets at this population without modeling assumptions is B05002_021 — "Foreign-born, not a U.S. citizen." We surface it as "Not a U.S. citizen."
This variable over-counts the undocumented population because it also includes:
- Lawful permanent residents (green-card holders) who haven't naturalized
- Visa holders — students (F-1), workers (H-1B, L-1, O-1, etc.), exchange visitors (J-1), and others
- Refugees, asylees, and other humanitarian admissions
- TPS (Temporary Protected Status) recipients
- DACA recipients (technically without status but with deferred action)
Calibrating it to undocumented requires a multiplier. National estimates (Pew, MPI, CMS): roughly a quarter of all foreign-born residents are undocumented. For NYC specifically, the Center for Migration Studies estimates roughly a third of non-citizens are undocumented, and the share varies by country-of-origin neighborhood (lower in Soviet-era Russian-speaking enclaves, higher in some Latin American and West African corridors). A tract with 1,000 "Not a U.S. citizen" residents likely contains around 300–400 undocumented residents, with substantial neighborhood-level variation.
This map does not multiply by that factor — we show the raw proxy and ask the reader to understand it as such. CMS publishes more authoritative estimates at sub-city geographies (Community District level); when this map grows a Community District aggregation, those will be layered in directly.
What is not yet here
- NYSED non-public (private + parochial) school rosters. Available only as Microsoft Access files; requires
mdbtoolsor equivalent to parse. Public-school enrollment from the NYC DOE Demographic Snapshot is in. - LEHD LODES jobs/commute data. Administrative payroll records — better than ACS B23025 for employment counts. A natural next addition.
- Hispanic country-of-origin breakdown at tract level (Mexican, Dominican, Salvadoran, etc.). 2020 DHC does not publish this at tract level — only state and finer subgroups. ACS B03001 has it but with high margins of error at tract level.
Color scales
The map offers two binning options for every variable:
- Quantile (default): seven equally populated bins so each color contains roughly the same number of tracts. This emphasizes relative ranking across the city and is the right choice for surfacing patterns.
- Linear: a continuous gradient from the lowest to highest value, which preserves the absolute spacing between tracts but can be visually dominated by outliers.
Each thematic group uses a different single-hue palette to make the active variable immediately recognizable.
Known limits
- Tract-level estimates are 5-year rolling averages (2020–2024). Conditions in any single year may differ.
- Tracts with very small denominators (e.g., few owner-occupied units) will show missing values or unstable percentages. Missing values are shown in gray.
- "No data" can mean the Census Bureau suppressed an estimate for confidentiality, the denominator was zero, or the tract has no relevant population.
- Race/ethnicity categories follow Census Bureau conventions and do not represent every possible identity. People of "two or more races" and "some other race" are not shown separately here.
- Income brackets reflect dollar values not adjusted for inflation or cost-of-living differences across tracts.
- The "Public-school K-12 enrolled (by school location)" and "Public K-12 schools in tract" variables come from NYC DOE roster data — actual student counts, not survey estimates — and are far more accurate than the ACS-based numbers. But they answer a different question: where the schools are, not where the students live. Tracts with no school in them score zero on these regardless of how many school-age kids live there. The ACS "by residence" numbers in the same group answer the residence question. Together they triangulate.
- NYSED non-public (private + parochial) enrollment is not yet integrated. The data exists at data.nysed.gov but is published only as Microsoft Access (.accdb) files, which require additional tooling (mdbtools or Office) to parse. Wiring it up is a known TODO.
- School enrollment is split only into "public" and "private." The Census Bureau folds parochial (Catholic, Jewish day, Islamic, and other religious) schools into the "private" bucket, and there is no tract-level public dataset that separates parochial from secular private. Specialized public schools, charters, and gifted-and-talented programs are all counted as "public."
- K-12 enrollment in B14002 is by student residence, not by school location, so a tract's "% in private school" reflects where the families live, not where the schools are.
- Vehicle counts come from a survey question about vehicles "kept at home"; company cars and ride-share vehicles a household uses but doesn't own are not counted.
Reproducing this map
The Python build script (build.py) fetches all ACS variables from Census Reporter, joins them to NYC DCP tract geometry, computes the derived shares, and writes tracts.geojson and variables.json into the docs/ folder. The page is plain HTML/JS — Leaflet for the map, d3 for color scales, no build step. Source is available on GitHub.