CENSUS GEOGRAPHY / PYTHON + CSV
Add county information to ZCTAs in Python
Add county names and FIPS codes to 2020 Census ZCTA records. Compare all county overlaps with a largest-land-county selection.
The example and its inputs
Suppose your planning team has a list of areas identified by 2020 Census ZCTA codes. You want county names and FIPS codes for routing each area to a county team, while retaining the county overlaps for planning discussions.
input.csv contains three fictional planning records, repeated in the areas list in the Python code below. The area IDs are invented; the ZCTAs are real. They show a leading-zero identifier, an area contained within one county, and an area crossing a county boundary. The input CSV is optional because the code includes these records.
crosswalk.csv contains four unchanged rows from the paid 2020 ZCTA → County Crosswalk, with all 14 columns and every county relationship for these ZCTAs. It supplies county names, FIPS codes, land-area shares and the largest-land-county flag.
01 / SAMPLE DATA
Download the product sample
Use a separate working folder for this example and save this CSV there. Keep ZCTAs and FIPS codes as text so that 02108 retains its leading zero.
This guide uses 2020 Census ZCTAs, which are geographic areas. A postal ZIP or street address needs a different lookup; this file does not establish its county.
02 / PYTHON
Code example
Run this in one Jupyter cell, an IPython session, or a Python script. It uses Python’s standard library. The example selects the county with the largest share of each ZCTA’s land for the routing table, and keeps every overlap in a separate county_relationships list.
Paths are relative to your working folder; use from pathlib import Path; print(Path.cwd()) to check it, or supply full paths.
import csv
from collections import defaultdict
# Fictional planning records, also provided in input.csv. These are 2020 ZCTAs.
areas = [
{"area_id": "A1", "zcta": "02108"},
{"area_id": "A2", "zcta": "27513"},
{"area_id": "A3", "zcta": "27519"},
]
with open("crosswalk.csv", newline="", encoding="utf-8-sig") as f:
crosswalk = list(csv.DictReader(f))
by_zcta = defaultdict(list)
seen = set()
for row in crosswalk:
key = (row["zcta_code"], row["county_fips"])
if not all(key) or key in seen:
raise ValueError("Missing or duplicate ZCTA/county pair")
seen.add(key)
by_zcta[key[0]].append(row)
# Each included ZCTA needs all its county relationships and exactly one primary.
for code, matches in by_zcta.items():
if (any(int(r["county_count"]) != len(matches) for r in matches)
or sum(r["is_primary_land_county"] == "1" for r in matches) != 1):
raise ValueError(f"Incomplete county relationships for {code}")
results = []
county_relationships = []
for area in areas:
matches = by_zcta.get(area["zcta"], [])
primary = next((r for r in matches if r["is_primary_land_county"] == "1"), None)
results.append({
**area,
"county_fips": primary["county_fips"] if primary else "",
"county_name": primary["county_name"] if primary else "",
"state": primary["state_abbreviation"] if primary else "",
"county_count": len(matches),
"status": "largest_land_county" if primary else "not_in_reference",
})
for match in matches:
county_relationships.append({"area_id": area["area_id"], **match})
print("Area | ZCTA | Selected county | FIPS | County count")
for row in results:
county = f'{row["county_name"]}, {row["state"]}' if row["county_name"] else row["status"]
print(f'{row["area_id"]} | {row["zcta"]} | {county} | '
f'{row["county_fips"] or "—"} | {row["county_count"]}')
print("\nAll relationships for areas crossing county lines")
for row in county_relationships:
if int(row["county_count"]) > 1:
share = float(row["zcta_land_share"])
print(f'{row["zcta_code"]} | {row["county_name"]} | '
f'{share:.2%} of ZCTA land | primary={row["is_primary_land_county"]}')
03 / RESULTS
Analysis of the output
Area | ZCTA | Selected county | FIPS | County count
A1 | 02108 | Suffolk County, MA | 25025 | 1
A2 | 27513 | Wake County, NC | 37183 | 1
A3 | 27519 | Wake County, NC | 37183 | 2
All relationships for areas crossing county lines
27519 | Chatham County | 8.13% of ZCTA land | primary=0
27519 | Wake County | 91.87% of ZCTA land | primary=1
ZCTA 02108 is associated with Suffolk County, Massachusetts, and 27513 with Wake County, North Carolina. Their county names are ready to display; the five-digit county FIPS values can join to other county tables using compatible geography.
ZCTA 27519 overlaps Wake and Chatham counties. Wake contains about 91.87% of the ZCTA’s land, so it is the selected county under this rule. Chatham’s remaining land share is still visible in the detailed output. For coordination involving the whole area, both county relationships may matter.
zcta_land_share divides the overlap’s land area by the ZCTA’s land area. It is not the proportion of residents, customers or addresses in that county. The example assigns a planning label; it does not allocate population or other totals. The separate county_land_share column has the county’s land area as its denominator.
The ACD flag is_primary_land_county selects the greatest land overlap, with the lowest county FIPS as the tie-breaker. A missing lookup stays in results as not_in_reference; when using the sample, that describes only the sample’s coverage.
04 / YOUR DATA
Using the full dataset
Replace crosswalk.csv in the open(...) call with ActuallyCleanData_2020_ZCTA_to_County_Crosswalk.csv from the purchased ZIP. Replace the areas list with your records, or load your CSV:
with open("my-areas.csv", newline="", encoding="utf-8-sig") as f:
areas = list(csv.DictReader(f))
Use area_id and zcta columns, or adjust the names in the example. Insert the loading lines where the example list appears. If you need all county overlaps rather than one routing label, use county_relationships. Repeated ZCTAs in that table represent separate relationships; do not sum a ZCTA-level measure across them without an appropriate allocation method.
The full crosswalk includes 46,960 relationships for 33,791 ZCTAs and 3,232 counties or county equivalents. It includes the overlap areas, both land-share measures and the documented primary selection rule.
Sources and versions
The Census Bureau’s geographic relationship files supply the 2020 ZCTA/county boundaries and intersection areas. ACD adds shares and the primary flag. The package was produced on 2026-10-13; that date does not make the geography current. County identifiers must be used with the same vintage, including Connecticut’s former counties in this release. The state-reference source does not declare an edition.
Example checked October 15, 2026 against both these samples and the complete purchased files. Source and processing records, sample checksums · Expected result CSV. Independent preparation; no agency endorsement.