Working with Decennial Census Data¶
The Decennial Census is a complete count of the United States population conducted every ten years. Unlike the ACS, decennial data has no margins of error because it aims to enumerate every person rather than sample a subset. PyPUMS provides get_decennial() to pull decennial summary tables from the Census API.
Function signature¶
pypums.get_decennial(
geography,
variables=None,
table=None,
state=None,
county=None,
year=2020,
output="tidy",
pop_group=None,
geometry=False,
keep_geo_vars=False,
cache_table=False,
key=None,
)
API key required
All Census API calls require a key. Request one for free at
https://api.census.gov/data/key_signup.html, then set the
CENSUS_API_KEY environment variable or pass key="YOUR_KEY" to
every call.
Default datasets by year¶
PyPUMS automatically selects the correct dataset for each census year:
| Year | Default dataset | Description |
|---|---|---|
| 2020 | dec/dhc |
Demographic and Housing Characteristics |
| 2010 | dec/sf1 |
Summary File 1 |
| 2000 | dec/sf1 |
Summary File 1 |
When you use the pop_group parameter, the dataset switches to
dec/dhc-a (Demographic and Housing Characteristics – A), which
provides disaggregated race and ethnicity data.
Getting started¶
Total population by state¶
Variable P1_001N is total population in the 2020 DHC file:
GEOID NAME variable value 0 09 Connecticut P1_001N 3605944 1 10 Delaware P1_001N 989948 2 11 District of Columbia P1_001N 689545 3 12 Florida P1_001N 21538187 4 13 Georgia P1_001N 10711908
Variable naming convention
Decennial 2020 DHC variables use a different naming pattern than
the ACS. Population variables start with P (e.g., P1_001N),
housing variables with H (e.g., H1_001N). The suffix N means
the numeric value. There are no separate estimate/MOE pairs because
decennial data is a complete count.
Multiple variables¶
Pull both total population and housing units:
GEOID NAME variable value 0 48239 Jackson County, Texas P1_001N 14988 1 48233 Hutchinson County, Texas P1_001N 20617 2 48235 Irion County, Texas P1_001N 1513 3 48237 Jack County, Texas P1_001N 8472
Full table¶
Pass a table ID to pull all variables in a group:
python exec="on" source="tabbed-left" session="decennial"
race_table = pypums.get_decennial(
geography="state",
table="P1",
state="CA",
year=2020,
)
print(race_table.shape)
Tidy vs. wide output¶
Each row is one geography-variable combination. The value column is
named value (not estimate, since decennial data has no MOE).
df_tidy = pypums.get_decennial(
geography="county",
variables=["P1_001N", "H1_001N"],
state="NY",
year=2020,
output="tidy",
)
| GEOID | NAME | variable | value |
|---|---|---|---|
| 36001 | Albany County, New York | P1_001N | 314848 |
| 36001 | Albany County, New York | H1_001N | 140465 |
Columns: GEOID, NAME, variable, value
Population groups (DHC-A)¶
The 2020 Census introduced the DHC-A file, which provides population
counts disaggregated by detailed race and ethnicity categories. Use the
pop_group parameter to access this data.
Discovering population groups¶
Use get_pop_groups() to see available codes:
code label
0 1 Total population
1 2 Hispanic or Latino
2 3 Not Hispanic or Latino
3 4 White alone
4 5 Black or African American alone
...
You can also filter by state:
Querying with a population group¶
```python exec=”on” source=”tabbed-left” session=”decennial”
Total population for the Hispanic or Latino group, by state¶
hispanic_pop = pypums.get_decennial( geography=”state”, variables=[“P1_001N”], year=2020, pop_group=”2”, ) print(hispanic_pop.head()) ```
DHC-A availability
The pop_group parameter is only available for the 2020 Census.
Earlier census years (2010, 2000) use different data structures and
do not support population group queries through this parameter.
Geometry support¶
Set geometry=True to return a GeoDataFrame with cartographic boundary shapes:
GEOID geometry … variable value 0 17023 POLYGON ((-88.01394 39.48079, -88.00762 39.480… … P1_001N 15455 1 17017 POLYGON ((-90.58353 39.87675, -90.58064 39.881… … P1_001N 13042 2 17019 POLYGON ((-88.46355 40.22327, -88.46072 40.223… … P1_001N 205865
[3 rows x 5 columns]
import altair as alt
alt.Chart(county_geo).mark_geoshape(stroke="white", strokeWidth=0.5).encode(
color=alt.Color("value:Q", legend=alt.Legend(title="Population")),
tooltip=["NAME:N", alt.Tooltip("value:Q", format=",")],
).project("albersUsa").properties(width=500, height=400)
Interactive preview — state-level 2020 Census population
The code above fetches county-level data for Illinois. The chart below
uses state-level data to demonstrate what geometry=True + Altair
looks like at a broader geographic scale.
Optional dependency
geometry=True requires geopandas. Install with
uv add "pypums[spatial]".
Preserving raw FIPS columns¶
Set keep_geo_vars=True to retain the individual FIPS columns
alongside the composite GEOID:
Caching¶
Cache API responses to disk for 24 hours with cache_table=True:
df = pypums.get_decennial(
geography="tract",
variables=["P1_001N"],
state="CA",
county="037",
year=2020,
cache_table=True,
)
Discovering available datasets¶
Use summary_files() to list all available decennial census datasets
for a given year:
dataset_name ... description
0 dec/responserate … Daily Decennial Self-Response Rates 1 dec/pl … Public Law 94-171, enacted in 1975, directs th… 2 dec/pes … Post-Enumeration Survey estimates of net cover… 3 dec/dpas … The U.S. Census Bureau conducts the Island Are… 4 dec/dpgu … The U.S. Census Bureau conducts the Island Are… 5 dec/dpmp … The U.S. Census Bureau conducts the Island Are… 6 dec/dpvi … The U.S. Census Bureau conducts the Island Are… 7 dec/dhc … This product will include topics such as age, … 8 dec/dp … This product will include topics such as age, … 9 dec/dhcas … This product will include some of the demograp… 10 dec/dhcgu … This product will include some of the demograp… 11 dec/dhcmp … This product will include some of the demograp… 12 dec/dhcvi … This product will include some of the demograp… 13 dec/cd118 … The Congressional District Summary File (118th… 14 dec/ddhca … This product provides the population counts an… 15 dec/crosstabas … This product will include key socio-demographi… 16 dec/crosstabgu … This product will include…. 17 dec/crosstabmp … This product will include key socio-demographi… 18 dec/crosstabvi … This product will include key socio-demographi… 19 dec/selfresponserate … The 2020 Census Self-Response and Return Rates… 20 dec/ddhcb … This data product provides household type and … 21 dec/sdhc … This product includes counts of people living … 22 dec/cd119 … The Congressional District Summary File (119th…
[23 rows x 3 columns]
This is useful for discovering what data products are available beyond the default DHC file.
Differences from ACS¶
Understanding when to use get_decennial() versus get_acs() is
critical for sound analysis.
| Feature | Decennial (get_decennial) |
ACS (get_acs) |
|---|---|---|
| Frequency | Every 10 years | Annual (1-year or 5-year) |
| Type | Complete count | Sample-based survey |
| Margins of error | None (complete count) | Always present |
| Output columns | variable, value |
variable, estimate, moe |
| Variable names | P1_001N, H1_001N |
B01001_001, B19013_001 |
| Subject coverage | Basic demographics and housing | Detailed socioeconomic topics |
| Smallest geography | Block | Block group (5-year) |
When to use decennial data¶
- You need block-level counts (the smallest geography available)
- You want a complete count without sampling uncertainty
- You are doing redistricting or apportionment analysis
- You need a population baseline for a census year
When to use ACS data¶
- You need recent data between decennial years
- You need socioeconomic detail (income, education, commuting, etc.)
- You need annual time series
- Your analysis requires tract-level data with rich subject detail
Common patterns¶
Race and ethnicity by county¶
race = pypums.get_decennial(
geography="county",
variables=[
"P1_003N", # Population of one race: White alone
"P1_004N", # Black or African American alone
"P1_005N", # American Indian and Alaska Native alone
"P1_006N", # Asian alone
"P1_007N", # Native Hawaiian and Other Pacific Islander alone
"P1_008N", # Some Other Race alone
"P1_009N", # Population of two or more races
],
state="CA",
year=2020,
output="wide",
)
print(race.head(3))
```python exec=”on” session=”decennial”
markdown-exec: hide¶
race = pypums.get_decennial( geography=”county”, variables=[ “P1_003N”, “P1_004N”, “P1_005N”, “P1_006N”, “P1_007N”, “P1_008N”, “P1_009N”, ], state=”CA”, year=2020, output=”wide”, ) print(race.head(3)) ```
Occupied vs. vacant housing units¶
housing = pypums.get_decennial(
geography="county",
variables=[
"H1_001N", # Total housing units
"H1_002N", # Occupied
"H1_003N", # Vacant
],
state="FL",
year=2020,
)
housing_wide = pypums.get_decennial(
geography="county",
variables=["H1_001N", "H1_002N", "H1_003N"],
state="FL",
year=2020,
output="wide",
)
housing_wide["vacancy_rate"] = housing_wide["H1_003N"] / housing_wide["H1_001N"]
print(housing_wide.nlargest(5, "vacancy_rate")[["NAME", "H1_001N", "H1_003N", "vacancy_rate"]])
```python exec=”on” session=”decennial”
markdown-exec: hide¶
housing = pypums.get_decennial( geography=”county”, variables=[ “H1_001N”, “H1_002N”, “H1_003N”, ], state=”FL”, year=2020, ) housing_wide = pypums.get_decennial( geography=”county”, variables=[“H1_001N”, “H1_002N”, “H1_003N”], state=”FL”, year=2020, output=”wide”, ) housing_wide[“vacancy_rate”] = housing_wide[“H1_003N”] / housing_wide[“H1_001N”] print(housing_wide.nlargest(5, “vacancy_rate”)[[“NAME”, “H1_001N”, “H1_003N”, “vacancy_rate”]]) ```
Tract-level population map¶
pop_map = pypums.get_decennial(
geography="tract",
variables=["P1_001N"],
state="NY",
county="061", # New York County (Manhattan)
year=2020,
geometry=True,
)
import altair as alt
alt.Chart(pop_map).mark_geoshape(stroke="white", strokeWidth=0.3).encode(
color=alt.Color("value:Q", scale=alt.Scale(scheme="yelloworangered")),
tooltip=["NAME:N", alt.Tooltip("value:Q", format=",")],
).project("albersUsa").properties(width=500, height=400)
Comparing 2010 and 2020¶
2010 states: 52, 2020 states: 52
Variable names differ across decades
The 2020 DHC uses variables like P1_001N. The 2010 SF1 uses
P001001. The 2000 SF1 uses P001001 as well, but the available
tables differ. Always check load_variables() for the correct
variable names for each year.
# Look up 2010 variable names
vars_2010 = pypums.load_variables(2010, "sf1", cache=True)
pop_vars = vars_2010[vars_2010["label"].str.contains("Total", na=False)]
print(pop_vars[["name", "label"]].head(5))
name label
0 P001001 Total population
1 P003001 Total population in races tallied
2 P006001 Total races tallied (total pop.)
3 P010001 Total population in households
4 H001001 Total housing units
Troubleshooting¶
“error: unknown variable” for 2020 queries
: The 2020 Decennial uses the DHC dataset, not SF1. Variable names changed
(e.g., P1_001N instead of P001001). Use
load_variables(2020, "dhc") to find the correct codes.
pop_group parameter has no effect
: Population groups are only available in the 2020 DHC-A detailed tables.
They do not apply to the 2010 or 2000 SF1 datasets.
Missing data for small geographies : The Census Bureau suppresses block-level data for very small populations to protect privacy. Try a broader geography (block group or tract).
“error: unsupported year”
: get_decennial() supports 2000, 2010, and 2020. Other years are not
available through the Census API’s decennial endpoint.
See Also¶
- API Reference — Full
get_decennial()function signature - Geography & FIPS — Understanding geography levels and FIPS code lookups
- Spatial Data — Attaching geometry to query results
- ACS Data — For when you need margins of error and more recent annual data