Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Example of usage of morpc-census for analysis

from morpc_census import (
    Endpoint, Group, CensusAPI, DimensionTable, RaceDimensionTable,
    RACE_TABLE_MAP, HIGHLEVEL_DESC_TO_ID,
    fetch_geos_from_scope_sumlevel,
)
import pandas as pd
import numpy as np
import itables

1. Variable discovery

Network required — the cells below make live calls to the Census API.

Start with an Endpoint and search endpoint.groups for tables related to the topic. We can think of an endpoint as specific survey and year/vintage.

The ACS 5-year survey has hundreds of groups; filtering by keyword narrows the field quickly.

ep = Endpoint('acs/acs5', 2024)

ep.search_groups('poverty')
{'B05010': 'Ratio of Income to Poverty Level in the Past 12 Months by Nativity of Children Under 18 Years in Families and Subfamilies by Living Arrangements and Nativity of Parents', 'B06012': 'Place of Birth by Poverty Status in the Past 12 Months in the United States', 'B06012PR': 'Place of Birth by Poverty Status in the Past 12 Months in Puerto Rico', 'B07012': 'Geographical Mobility in the Past Year by Poverty Status in the Past 12 Months for Current Residence in the United States', 'B07012PR': 'Geographical Mobility in the Past Year by Poverty Status in the Past 12 Months for Current Residence in Puerto Rico', 'B07412': 'Geographical Mobility in the Past Year by Poverty Status in the Past 12 Months for Residence 1 Year Ago in the United States', 'B07412PR': 'Geographical Mobility in the Past Year by Poverty Status in the Past 12 Months for Residence 1 Year Ago in Puerto Rico', 'B08122': 'Means of Transportation to Work by Poverty Status in the Past 12 Months', 'B08522': 'Means of Transportation to Work by Poverty Status in the Past 12 Months for Workplace Geography', 'B10059': 'Poverty Status in the Past 12 Months of Grandparents Living With Own Grandchildren Under 18 Years by Responsibility for Own Grandchildren and Age of Grandparent', 'B13010': 'Women 15 to 50 Years Who Had a Birth in the Past 12 Months by Marital Status and Poverty Status in the Past 12 Months', 'B14006': 'Poverty Status in the Past 12 Months by School Enrollment by Level of School for the Population 3 Years and Over', 'B16009': 'Poverty Status in the Past 12 Months by Age by Language Spoken at Home for the Population 5 Years and Over', 'B17001': 'Poverty Status in the Past 12 Months by Sex by Age', 'B17001A': 'Poverty Status in the Past 12 Months by Sex by Age (White Alone)', 'B17001B': 'Poverty Status in the Past 12 Months by Sex by Age (Black or African American Alone)', 'B17001C': 'Poverty Status in the Past 12 Months by Sex by Age (American Indian and Alaska Native Alone)', 'B17001D': 'Poverty Status in the Past 12 Months by Sex by Age (Asian Alone)', 'B17001E': 'Poverty Status in the Past 12 Months by Sex by Age (Native Hawaiian and Other Pacific Islander Alone)', 'B17001F': 'Poverty Status in the Past 12 Months by Sex by Age (Some Other Race Alone)', 'B17001G': 'Poverty Status in the Past 12 Months by Sex by Age (Two or More Races)', 'B17001H': 'Poverty Status in the Past 12 Months by Sex by Age (White Alone, Not Hispanic or Latino)', 'B17001I': 'Poverty Status in the Past 12 Months by Sex by Age (Hispanic or Latino)', 'B17003': 'Poverty Status in the Past 12 Months of Individuals by Sex by Educational Attainment', 'B17004': 'Poverty Status in the Past 12 Months of Individuals by Sex by Work Experience', 'B17005': 'Poverty Status in the Past 12 Months of Individuals by Sex by Employment Status', 'B17006': 'Poverty Status in the Past 12 Months of Related Children Under 18 Years by Family Type by Age of Related Children Under 18 Years', 'B17007': 'Poverty Status in the Past 12 Months of Unrelated Individuals 15 Years and Over by Sex by Age', 'B17009': 'Poverty Status by Work Experience of Unrelated Individuals by Householder Status', 'B17010': 'Poverty Status in the Past 12 Months of Families by Family Type by Presence of Related Children Under 18 Years by Age of Related Children', 'B17010A': 'Poverty Status in the Past 12 Months of Families by Family Type by Presence of Related Children Under 18 Years by Age of Related Children (White Alone Householder)', 'B17010B': 'Poverty Status in the Past 12 Months of Families by Family Type by Presence of Related Children Under 18 Years by Age of Related Children (Black or African American Alone Householder)', 'B17010C': 'Poverty Status in the Past 12 Months of Families by Family Type by Presence of Related Children Under 18 Years by Age of Related Children (American Indian and Alaska Native Alone)', 'B17010D': 'Poverty Status in the Past 12 Months of Families by Family Type by Presence of Related Children Under 18 Years by Age of Related Children (Asian Alone Householder)', 'B17010E': 'Poverty Status in the Past 12 Months of Families by Family Type by Presence of Related Children Under 18 Years by Age of Related Children (Native Hawaiian and Other Pacific Islander Alone Householder)', 'B17010F': 'Poverty Status in the Past 12 Months of Families by Family Type by Presence of Related Children Under 18 Years by Age of Related Children (Some Other Race Alone Householder)', 'B17010G': 'Poverty Status in the Past 12 Months of Families by Family Type by Presence of Related Children Under 18 Years by Age of Related Children (Two or More Races Householder)', 'B17010H': 'Poverty Status in the Past 12 Months of Families by Family Type by Presence of Related Children Under 18 Years by Age of Related Children (White Alone, Not Hispanic or Latino Householder)', 'B17010I': 'Poverty Status in the Past 12 Months of Families by Family Type by Presence of Related Children Under 18 Years by Age of Related Children (Hispanic or Latino)', 'B17012': 'Poverty Status in the Past 12 Months of Families by Household Type by Number of Related Children Under 18 Years', 'B17013': 'Poverty Status in the Past 12 Months of Families by Household Type by Number of Persons in Family', 'B17014': 'Poverty Status in the Past 12 Months of Families by Household Type by Number of Workers in Family', 'B17015': 'Poverty Status in the Past 12 Months of Families by Family Type by Social Security Income by Supplemental Security Income (SSI) and Cash Public Assistance Income', 'B17016': 'Poverty Status in the Past 12 Months of Families by Family Type by Work Experience of Householder and Spouse', 'B17017': 'Poverty Status in the Past 12 Months by Household Type by Age of Householder', 'B17018': 'Poverty Status in the Past 12 Months of Families by Household Type by Educational Attainment of Householder', 'B17019': 'Poverty Status in the Past 12 Months of Families by Household Type by Tenure', 'B17020': 'Poverty Status in the Past 12 Months by Age', 'B17020A': 'Poverty Status in the Past 12 Months by Age (White Alone)', 'B17020B': 'Poverty Status in the Past 12 Months by Age (Black or African American Alone)', 'B17020C': 'Poverty Status in the Past 12 Months by Age (American Indian and Alaska Native Alone)', 'B17020D': 'Poverty Status in the Past 12 Months by Age (Asian Alone)', 'B17020E': 'Poverty Status in the Past 12 Months by Age (Native Hawaiian and Other Pacific Islander Alone)', 'B17020F': 'Poverty Status in the Past 12 Months by Age (Some Other Race Alone)', 'B17020G': 'Poverty Status in the Past 12 Months by Age (Two or More Races)', 'B17020H': 'Poverty Status in the Past 12 Months by Age (White Alone, Not Hispanic or Latino)', 'B17020I': 'Poverty Status in the Past 12 Months by Age (Hispanic or Latino)', 'B17021': 'Poverty Status of Individuals in the Past 12 Months by Living Arrangement', 'B17022': 'Ratio of Income to Poverty Level in the Past 12 Months of Families by Family Type by Presence of Related Children Under 18 Years by Age of Related Children', 'B17023': 'Poverty Status in the Past 12 Months of Families by Household Type by Number of Own Children Under 18 Years', 'B17024': 'Age by Ratio of Income to Poverty Level in the Past 12 Months', 'B17025': 'Poverty Status in the Past 12 Months by Nativity', 'B17026': 'Ratio of Income to Poverty Level of Families in the Past 12 Months', 'B17101': 'Poverty Status in the Past 12 Months of People in Housing Units', 'B22003': 'Receipt of Food Stamps/SNAP in the Past 12 Months by Poverty Status in the Past 12 Months for Households', 'B23024': 'Poverty Status in the Past 12 Months by Disability Status by Employment Status for the Population 20 to 64 Years', 'B29003': 'Citizen, Voting-Age Population by Poverty Status', 'B99171': 'Allocation of Poverty Status in the Past 12 Months for Unrelated Individuals', 'B99172': 'Allocation of Poverty Status in the Past 12 Months for Families', 'C17002': 'Ratio of Income to Poverty Level in the Past 12 Months', 'C18130': 'Age by Disability Status by Poverty Status', 'C18131': 'Ratio of Income to Poverty Level in the Past 12 Months by Disability Status', 'C21007': 'Age by Veteran Status by Poverty Status in the Past 12 Months by Disability Status for the Civilian Population 18 Years and Over', 'C27016': 'Health Insurance Coverage Status by Ratio of Income to Poverty Level in the Past 12 Months by Age', 'C27017': 'Private Health Insurance by Ratio of Income to Poverty Level in the Past 12 Months by Age', 'C27018': 'Public Health Insurance by Ratio of Income to Poverty Level in the Past 12 Months by Age'}

B17001 - Poverty Status by Sex by Age is the standard person-level poverty table.

We can look at the groups description and universe by creating a Group object.

g = Group(ep, 'B17001')
print(f"Description : {g.description}")
print(f"Universe    : {g.universe}")
Description : Poverty Status in the Past 12 Months by Sex by Age
Universe    : Population for whom poverty status is determined

To look at the rate of poverty we can call DimensionTable.percent() which will calculate the percent of each row based on the total.

The .drop() method allows us to drop unwanted dimension in the data, in this case sex and age.

dim = DimensionTable(CensusAPI(ep, group=g, scope='region15').long).drop(['dim_2', 'dim_3']).percent()
itables.show(dim)
Loading...

B17001A-I - Poverty Status in the Past 12 Months by Sex by Age (by Race) are race tables which separate poverty status by race.

morpc-census allows us to call all of these table in the same format and concatenating them to compare across race.

race_groups = {value: Group(ep, f"B17001{key}") for key, value in RACE_TABLE_MAP.items()}
[v.description for k, v in race_groups.items()]
['Poverty Status in the Past 12 Months by Sex by Age (White Alone)', 'Poverty Status in the Past 12 Months by Sex by Age (Black or African American Alone)', 'Poverty Status in the Past 12 Months by Sex by Age (American Indian and Alaska Native Alone)', 'Poverty Status in the Past 12 Months by Sex by Age (Asian Alone)', 'Poverty Status in the Past 12 Months by Sex by Age (Native Hawaiian and Other Pacific Islander Alone)', 'Poverty Status in the Past 12 Months by Sex by Age (Some Other Race Alone)', 'Poverty Status in the Past 12 Months by Sex by Age (Two or More Races)', 'Poverty Status in the Past 12 Months by Sex by Age (White Alone, Not Hispanic or Latino)', 'Poverty Status in the Past 12 Months by Sex by Age (Hispanic or Latino)']

2. Fetching data

For the poverty-by-race snapshot we need two variables from each racial iteration table:

CensusAPI allows us to call a list of variables across groups. We can use this to call the variables needed from each race table.

VariableMeaning
B17001X_001ETotal population for whom poverty status is determined
B17001X_002EPopulation with income below the poverty level

The Census API returns a margin of error (MOE) alongside every estimate. MOEs are reported at the 90% confidence level and appear in the moe column of .long.

race_vars = []
for code in RACE_TABLE_MAP:
    race_vars += [f'B17001{code}_001E', f'B17001{code}_001M', f'B17001{code}_002E', f'B17001{code}_002M']

race_api = CensusAPI(ep, 'region15', variables=race_vars)
itables.show(race_api.long)
Loading...

3. Poverty by race — 2024 snapshot

For each racial group, the poverty rate is the share of people in that group whose income falls below the federal poverty level:

morpc-census provides a DimensionTable and RaceDimensionTable to look at human-readable, wide format tables

wide = RaceDimensionTable(race_api.long).percent()
itables.show(wide, maxColumns=270)
Loading...
itables.show(wide.melt().pivot_table(index=['geoidfq', 'name'], columns=['race', 'value_type'], values='value'))
/tmp/ipykernel_478215/905136670.py:1: FutureWarning: The default value of observed=False is deprecated and will change to observed=True in a future version of pandas. Specify observed=False to silence this warning and retain the current behavior
Loading...

4. Non-white poverty over time (2014–2023)

To track change in poverty among non-white residents, subtract the White alone, not Hispanic or Latino population (B17001H) from the all-persons total (B17001):

non-white below poverty=B17001_002B17001H_002\text{non-white below poverty} = \text{B17001\_002} - \text{B17001H\_002}
non-white total=B17001_001B17001H_001\text{non-white total} = \text{B17001\_001} - \text{B17001H\_001}

Network required — the loop below fetches five ACS vintage years.

ts_vars = ['B17001_001E', 'B17001_002E', 'B17001H_001E', 'B17001H_002E']

long_frames = []
for year in [2014, 2019, 2024]:
    ep_yr = Endpoint('acs/acs5', year)
    api   = CensusAPI(ep_yr, 'region15', variables=ts_vars)
    long_frames.append(api.long)

long_ts = pd.concat(long_frames, ignore_index=True)
itables.show(long_ts)
Loading...
est_ts = long_ts.pivot_table(
    index=['geoidfq', 'name', 'reference_period'],
    columns='variable', values='estimate', aggfunc='first',
).reset_index()

nw_below = est_ts['B17001_002'] - est_ts['B17001H_002']
nw_total = est_ts['B17001_001'] - est_ts['B17001H_001']
est_ts['nw_rate'] = (nw_below / nw_total * 100).round(2)

rate_ts     = nw_below / nw_total

est_ts.pivot_table(
    index='reference_period', columns='name', values='nw_rate', aggfunc='first'
)

change = est_ts[['geoidfq', 'name', 'reference_period', 'nw_rate']].pivot(index=['geoidfq', 'name'], columns='reference_period', values='nw_rate')
itables.show(change)
Loading...

5. Mapping the change

fetch_geos_from_scope_sumlevel returns a GeoDataFrame with county boundaries. Merging it with the computed poverty change gives a ready-to-plot choropleth.

A diverging colormap (red = increase, blue = decrease) makes the direction of change immediately readable.

change.columns = [f'rate_{yr}' for yr in change.columns]
change['change_pp'] = (change['rate_2024'] - change['rate_2014']).round(2)
change = change.reset_index()
change
Loading...
geos = fetch_geos_from_scope_sumlevel('region15')
geos = geos[['GEOIDFQ', 'geometry']].merge(change, left_on='GEOIDFQ', right_on='geoidfq', how='left')[['geoidfq', 'name', 'change_pp', 'geometry']].set_index('geoidfq')

Downloading: 100%|██████████████████████████| 1/1 [00:01<00:00, 1.02 requests/s]
geos
Loading...
import matplotlib.pyplot as plt

max_abs = geos['change_pp'].abs().max()

fig, ax = plt.subplots(figsize=(10, 8))
geos.plot(
    column='change_pp',
    ax=ax,
    cmap='RdBu_r',
    vmin=-max_abs,
    vmax=max_abs,
    legend=True,
    legend_kwds={
        'label': 'Change in non-white poverty rate (%)',
        'orientation': 'horizontal',
        'shrink': 0.6,
        'pad': 0.02,
    },
    edgecolor='white',
    linewidth=0.5,
)

for _, row in geos.iterrows():
    cx = row.geometry.centroid.x
    cy = row.geometry.centroid.y
    label = row['name'].replace(', Ohio', '').replace(' County', '')
    val   = row['change_pp']
    ax.annotate(
        f"{label}\n{val:+.1f}%",
        (cx, cy),
        ha='center', va='center', fontsize=6.5, color='black',
    )

ax.set_title(
    'Change in Non-White Poverty Rate\nMORPC 15-County Region — 2014 to 2023 ACS 5-Year',
    fontsize=13,
)
ax.axis('off')
plt.tight_layout()
plt.show()
<Figure size 1000x800 with 2 Axes>