Tutorial 4: Working with Geometries¶
cendat can fetch geographic boundaries alongside your data for mapping.
Goal: Get median household income data with geometries for Colorado block groups and create a choropleth map.
Setup¶
In [1]:
Copied!
import os
import matplotlib.pyplot as plt
from cendat import CenDatHelper
from dotenv import load_dotenv
load_dotenv()
cdh = CenDatHelper(key=os.getenv("CENSUS_API_KEY"))
import os
import matplotlib.pyplot as plt
from cendat import CenDatHelper
from dotenv import load_dotenv
load_dotenv()
cdh = CenDatHelper(key=os.getenv("CENSUS_API_KEY"))
✅ API key loaded successfully.
Step 1: Select Product¶
In [2]:
Copied!
cdh.list_products(years=[2023], patterns=r"acs/acs5\)")
cdh.set_products()
cdh.list_products(years=[2023], patterns=r"acs/acs5\)")
cdh.set_products()
✅ Product set: 'ACS 5-Year Detailed Tables (2023/acs/acs5)' (Vintage: [2023])
Step 2: Select Variables and Geography¶
In [3]:
Copied!
# Search for median household income groups
cdh.list_groups(patterns=r"^median household income")
# Search for median household income groups
cdh.list_groups(patterns=r"^median household income")
Out[3]:
[{'name': 'B29004',
'description': 'Median Household Income for Households With a Citizen, Voting-Age Householder (in 2023 Inflation-Adjusted Dollars)',
'product': 'ACS 5-Year Detailed Tables (2023/acs/acs5)',
'vintage': [2023],
'url': 'http://api.census.gov/data/2023/acs/acs5'},
{'name': 'B19013H',
'description': 'Median Household Income in the Past 12 Months (in 2023 Inflation-Adjusted Dollars) (White Alone, Not Hispanic or Latino Householder)',
'product': 'ACS 5-Year Detailed Tables (2023/acs/acs5)',
'vintage': [2023],
'url': 'http://api.census.gov/data/2023/acs/acs5'},
{'name': 'B19013I',
'description': 'Median Household Income in the Past 12 Months (in 2023 Inflation-Adjusted Dollars) (Hispanic or Latino Householder)',
'product': 'ACS 5-Year Detailed Tables (2023/acs/acs5)',
'vintage': [2023],
'url': 'http://api.census.gov/data/2023/acs/acs5'},
{'name': 'B19013B',
'description': 'Median Household Income in the Past 12 Months (in 2023 Inflation-Adjusted Dollars) (Black or African American Alone Householder)',
'product': 'ACS 5-Year Detailed Tables (2023/acs/acs5)',
'vintage': [2023],
'url': 'http://api.census.gov/data/2023/acs/acs5'},
{'name': 'B19013C',
'description': 'Median Household Income in the Past 12 Months (in 2023 Inflation-Adjusted Dollars) (American Indian and Alaska Native Alone Householder)',
'product': 'ACS 5-Year Detailed Tables (2023/acs/acs5)',
'vintage': [2023],
'url': 'http://api.census.gov/data/2023/acs/acs5'},
{'name': 'B19013A',
'description': 'Median Household Income in the Past 12 Months (in 2023 Inflation-Adjusted Dollars) (White Alone Householder)',
'product': 'ACS 5-Year Detailed Tables (2023/acs/acs5)',
'vintage': [2023],
'url': 'http://api.census.gov/data/2023/acs/acs5'},
{'name': 'B19013F',
'description': 'Median Household Income in the Past 12 Months (in 2023 Inflation-Adjusted Dollars) (Some Other Race Alone Householder)',
'product': 'ACS 5-Year Detailed Tables (2023/acs/acs5)',
'vintage': [2023],
'url': 'http://api.census.gov/data/2023/acs/acs5'},
{'name': 'B19013G',
'description': 'Median Household Income in the Past 12 Months (in 2023 Inflation-Adjusted Dollars) (Two or More Races Householder)',
'product': 'ACS 5-Year Detailed Tables (2023/acs/acs5)',
'vintage': [2023],
'url': 'http://api.census.gov/data/2023/acs/acs5'},
{'name': 'B19013D',
'description': 'Median Household Income in the Past 12 Months (in 2023 Inflation-Adjusted Dollars) (Asian Alone Householder)',
'product': 'ACS 5-Year Detailed Tables (2023/acs/acs5)',
'vintage': [2023],
'url': 'http://api.census.gov/data/2023/acs/acs5'},
{'name': 'B19013E',
'description': 'Median Household Income in the Past 12 Months (in 2023 Inflation-Adjusted Dollars) (Native Hawaiian and Other Pacific Islander Alone Householder)',
'product': 'ACS 5-Year Detailed Tables (2023/acs/acs5)',
'vintage': [2023],
'url': 'http://api.census.gov/data/2023/acs/acs5'},
{'name': 'B25119',
'description': 'Median Household Income in the Past 12 Months (in 2023 Inflation-Adjusted Dollars) by Tenure',
'product': 'ACS 5-Year Detailed Tables (2023/acs/acs5)',
'vintage': [2023],
'url': 'http://api.census.gov/data/2023/acs/acs5'},
{'name': 'B19019',
'description': 'Median Household Income in the Past 12 Months (in 2023 Inflation-Adjusted Dollars) by Household Size',
'product': 'ACS 5-Year Detailed Tables (2023/acs/acs5)',
'vintage': [2023],
'url': 'http://api.census.gov/data/2023/acs/acs5'},
{'name': 'B19013',
'description': 'Median Household Income in the Past 12 Months (in 2023 Inflation-Adjusted Dollars)',
'product': 'ACS 5-Year Detailed Tables (2023/acs/acs5)',
'vintage': [2023],
'url': 'http://api.census.gov/data/2023/acs/acs5'},
{'name': 'B22008',
'description': 'Median Household Income in the Past 12 Months (in 2023 Inflation-Adjusted Dollars) by Receipt of Food Stamps/SNAP in the Past 12 Months',
'product': 'ACS 5-Year Detailed Tables (2023/acs/acs5)',
'vintage': [2023],
'url': 'http://api.census.gov/data/2023/acs/acs5'},
{'name': 'B19049',
'description': 'Median Household Income in the Past 12 Months (in 2023 Inflation-Adjusted Dollars) by Age of Householder',
'product': 'ACS 5-Year Detailed Tables (2023/acs/acs5)',
'vintage': [2023],
'url': 'http://api.census.gov/data/2023/acs/acs5'}]
In [4]:
Copied!
cdh.set_groups(["B19013"])
cdh.describe_groups()
cdh.set_groups(["B19013"])
cdh.describe_groups()
✅ Groups set: B19013
--- Group: B19013 (Median Household Income in the Past 12 Months (in 2023 Inflation-Adjusted Dollars)) ---
Product: ACS 5-Year Detailed Tables (2023/acs/acs5) (Vintage: 2023)
B19013_001E: Median household income in the past 12 months (in 2023 inflation-adjusted dollars)
In [5]:
Copied!
# 150 = Block Groups
cdh.set_geos(["150"])
# 150 = Block Groups
cdh.set_geos(["150"])
✅ Geographies set: 'block group' (requires `within` for: county, state, tract)
Step 3: Get Data with Geometry¶
In [6]:
Copied!
# Fetch data for 3 Colorado counties with geometries
response = cdh.get_data(
include_names=True,
include_geometry=True, # This fetches boundaries from TIGERweb
within={
"state": ["08"], # Colorado
"county": ["069", "123", "013"], # Larimer, Weld, Boulder
},
)
# Fetch data for 3 Colorado counties with geometries
response = cdh.get_data(
include_names=True,
include_geometry=True, # This fetches boundaries from TIGERweb
within={
"state": ["08"], # Colorado
"county": ["069", "123", "013"], # Larimer, Weld, Boulder
},
)
✅ Variables set:
- Product: ACS 5-Year Detailed Tables (2023/acs/acs5) (Vintage: [2023])
Variables: B19013_001E
✅ Parameters created for 1 geo-variable/group combinations.
✅ Data fetching complete. Stacking results.
✅ Geometry fetching complete. Stacking results.
Step 4: Convert to GeoDataFrame and Map¶
In [7]:
Copied!
# to_gpd() returns a GeoDataFrame ready for mapping
gdf = response.to_gpd(destring=True, join_strategy="inner")
# Handle missing values (Census uses -666666666 for suppressed data)
gdf.loc[gdf["B19013_001E"] == -666666666, "B19013_001E"] = None
print(f"Block groups returned: {len(gdf)}")
gdf.head()
# to_gpd() returns a GeoDataFrame ready for mapping
gdf = response.to_gpd(destring=True, join_strategy="inner")
# Handle missing values (Census uses -666666666 for suppressed data)
gdf.loc[gdf["B19013_001E"] == -666666666, "B19013_001E"] = None
print(f"Block groups returned: {len(gdf)}")
gdf.head()
Block groups returned: 669
Out[7]:
| NAME | GEO_ID | B19013_001E | B19013_001EA | B19013_001M | B19013_001MA | state | county | tract | block group | GEOID | geometry | product | vintage | sumlev | desc | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Block Group 1; Census Tract 19.11; Weld County... | 1500000US081230019111 | NaN | - | -222222222 | ** | 08 | 123 | 001911 | 1 | 081230019111 | POLYGON ((-104.82197 40.00041, -104.82187 40.0... | ACS 5-Year Detailed Tables (2023/acs/acs5) | 2023 | 150 | block group |
| 1 | Block Group 1; Census Tract 7.01; Weld County;... | 1500000US081230007011 | 45000.0 | None | 9586 | None | 08 | 123 | 000701 | 1 | 081230007011 | POLYGON ((-104.68805 40.42252, -104.68772 40.4... | ACS 5-Year Detailed Tables (2023/acs/acs5) | 2023 | 150 | block group |
| 2 | Block Group 2; Census Tract 7.01; Weld County;... | 1500000US081230007012 | 55000.0 | None | 5225 | None | 08 | 123 | 000701 | 2 | 081230007012 | POLYGON ((-104.69123 40.44896, -104.69147 40.4... | ACS 5-Year Detailed Tables (2023/acs/acs5) | 2023 | 150 | block group |
| 3 | Block Group 1; Census Tract 20.13; Weld County... | 1500000US081230020131 | 139861.0 | None | 14646 | None | 08 | 123 | 002013 | 1 | 081230020131 | POLYGON ((-104.96099 40.10244, -104.96099 40.1... | ACS 5-Year Detailed Tables (2023/acs/acs5) | 2023 | 150 | block group |
| 4 | Block Group 2; Census Tract 20.13; Weld County... | 1500000US081230020132 | NaN | - | -222222222 | ** | 08 | 123 | 002013 | 2 | 081230020132 | POLYGON ((-104.9612 40.08816, -104.9612 40.087... | ACS 5-Year Detailed Tables (2023/acs/acs5) | 2023 | 150 | block group |
In [8]:
Copied!
# Create a polished choropleth map
fig, ax = plt.subplots(1, 1, figsize=(10, 5))
gdf.plot(
column="B19013_001E",
cmap="viridis",
linewidth=0.8,
ax=ax,
legend=True,
alpha=1.0,
legend_kwds={
"label": "Income",
"orientation": "horizontal",
"location": "bottom",
"shrink": 0.5,
"fraction": 0.1,
"format": "{x:,.0f}",
},
missing_kwds={
"color": "lightgrey",
"edgecolor": "grey",
"hatch": "////",
"label": "Missing values",
},
)
ax.set_title(
"Larimer, Weld, and Boulder County Med. HH Income by Block Group",
fontdict={"fontsize": "12", "fontweight": "3"},
)
ax.set_axis_off()
plt.tight_layout()
plt.show()
# Create a polished choropleth map
fig, ax = plt.subplots(1, 1, figsize=(10, 5))
gdf.plot(
column="B19013_001E",
cmap="viridis",
linewidth=0.8,
ax=ax,
legend=True,
alpha=1.0,
legend_kwds={
"label": "Income",
"orientation": "horizontal",
"location": "bottom",
"shrink": 0.5,
"fraction": 0.1,
"format": "{x:,.0f}",
},
missing_kwds={
"color": "lightgrey",
"edgecolor": "grey",
"hatch": "////",
"label": "Missing values",
},
)
ax.set_title(
"Larimer, Weld, and Boulder County Med. HH Income by Block Group",
fontdict={"fontsize": "12", "fontweight": "3"},
)
ax.set_axis_off()
plt.tight_layout()
plt.show()
Note - Supported Geographies
Geometry fetching currently supports: regions (020), divisions (030), states (040), counties (050), county subdivisions (060), census tracts (140), block groups (150), and places (160).