Assignment 1: Census Data Quality for Policy Decisions

Evaluating Data Reliability for Algorithmic Decision-Making

Author

Sujan Kakumanu

Published

September 29, 2025

Assignment Overview

Scenario

You are a data analyst for the Ohio Department of Human Services. The department is considering implementing an algorithmic system to identify communities that should receive priority for social service funding and outreach programs. Your supervisor has asked you to evaluate the quality and reliability of available census data to inform this decision.

Drawing on our Week 2 discussion of algorithmic bias, you need to assess not just what the data shows, but how reliable it is and what communities might be affected by data quality issues.

Learning Objectives

  • Apply dplyr functions to real census data for policy analysis
  • Evaluate data quality using margins of error
  • Connect technical analysis to algorithmic decision-making
  • Identify potential equity implications of data reliability issues
  • Create professional documentation for policy stakeholders

Setup

library(tidyverse)
library(tidycensus)
library(knitr)

census_api_key(Sys.getenv("MY_CENSUS_API_KEY"))

my_state <- "OH"

State Selection: I have chosen Ohio for this analysis because it is my home state! I spent most of my life there and although I am excited for my new chapter in Philadelphia, the state of Ohio will always have a place in my heart.

Part 2: County-Level Resource Assessment

2.1 Data Retrieval

Data: - Geography: county level - Variables: median household income (B19013_001) and total population (B01003_001)
- Year: 2022 - Survey: acs5 - Output format: wide

county_data <- get_acs(
  geography = "county",
  variables = c(
    median_income = "B19013_001",   # Median household income
    total_pop = "B01003_001"       # Total population
  ),
  state = my_state,
  year = 2022,
  output = "wide"
)

# Clean the county names to remove state name and "County" 
words_to_remove <- c("County", "Ohio", ', ', ' ')
pattern <- paste0("\\b(", paste(words_to_remove, collapse = "|"), ")\\b")

county_data <- county_data %>%
  mutate(county_name = str_remove_all(NAME, pattern))

# Display the first few rows
glimpse(county_data)
Rows: 88
Columns: 7
$ GEOID          <chr> "39001", "39003", "39005", "39007", "39009", "39011", "…
$ NAME           <chr> "Adams County, Ohio", "Allen County, Ohio", "Ashland Co…
$ median_incomeE <dbl> 46234, 58976, 62254, 53663, 48750, 75231, 56943, 66677,…
$ median_incomeM <dbl> 4072, 2172, 2954, 2186, 3695, 2795, 2417, 3077, 1604, 3…
$ total_popE     <dbl> 27509, 102087, 52522, 97666, 61276, 46263, 66554, 43715…
$ total_popM     <dbl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
$ county_name    <chr> "Adams", "Allen", "Ashland", "Ashtabula", "Athens", "Au…

2.2 Data Quality Assessment

Your Task: Calculate margin of error percentages and create reliability categories.

Requirements: - Calculate MOE percentage: (margin of error / estimate) * 100 - Create reliability categories: - High Confidence: MOE < 5% - Moderate Confidence: MOE 5-10%
- Low Confidence: MOE > 10% - Create a flag for unreliable estimates (MOE > 10%)

Hint: Use mutate() with case_when() for the categories.

# Calculate MOE percentages
county_data <- county_data %>%
  mutate(
      median_income_moe_pct = (median_incomeM / median_incomeE) * 100,
  )

county_data <- county_data %>%
  mutate(
    median_income_moe_cat = case_when(
      median_income_moe_pct > 10 ~ "Low Confidence: MOE > 10%",
      median_income_moe_pct >= 5 & median_income_moe_pct <= 10 ~ "Moderate Confidence: MOE 5-10%",
      TRUE ~ "High Confidence: MOE < 5%"
    ),
  )

# Summary showing count of counties in each reliability category
moe_cat_summary_table <- county_data %>%
  count(median_income_moe_cat, name = "number_of_counties")

moe_cat_summary_table
# A tibble: 3 × 2
  median_income_moe_cat          number_of_counties
  <chr>                                       <int>
1 High Confidence: MOE < 5%                      57
2 Low Confidence: MOE > 10%                       2
3 Moderate Confidence: MOE 5-10%                 29

2.3 High Uncertainty Counties

Workflow: - Sort by MOE percentage (highest first) - Select the top 5 counties - Display: county name, median income, margin of error, MOE percentage, reliability category - Format as a professional table using kable()

# Create table of top 5 counties by MOE percentage
top_moe_counties <- county_data %>%
   slice_max(median_income_moe_pct, n = 5) %>%
    select(county_name, median_incomeE, median_incomeM, median_income_moe_pct, median_income_moe_cat)

kable(
  top_moe_counties,
  caption = "Top 5 Counties in Ohio by Median Income Margin of Error (MOE)",
  col.names = c("County Name", "Median Income Estimate $", "Median Income MOE $", "Median Income MOE %", "Median Income MOE Category")
)
Top 5 Counties in Ohio by Median Income Margin of Error (MOE)
County Name Median Income Estimate $ Median Income MOE $ Median Income MOE % Median Income MOE Category
Vinton 50967 6264 12.290306 Low Confidence: MOE > 10%
Noble 51547 5566 10.797913 Low Confidence: MOE > 10%
Monroe 55381 5415 9.777722 Moderate Confidence: MOE 5-10%
Adams 46234 4072 8.807371 Moderate Confidence: MOE 5-10%
Pike 50832 4258 8.376613 Moderate Confidence: MOE 5-10%

Data Quality Commentary:

These results are not surprising given that the counties are among the least populous in the state (Vinton, Noble, and Monroe have less than 15,000 people according to 2024 estimates). Since the ACS relies on sample data, rather than the total enumeration provided by the Census, small population groups will result in small sample sizes.

In this scenario: Any algorithmic system should account for the data inaccuracies arising from Ohio’s smaller counties.

Part 3: Neighborhood-Level Analysis

3.1 Focus Area Selection

Selection: Counties that represent different reliability levels (e.g., 1 high confidence, 1 moderate, 1 low confidence) to compare how data quality varies.

selected_counties <- county_data %>%
  filter(county_name %in% c("Cuyahoga", "Marion", "Vinton"))

# Display the selected counties with their key characteristics
# Show: county name, median income, MOE percentage, reliability category
kable(
  caption = "Selected Counties (1 in each reliability category)",
  selected_counties %>% select(county_name, median_incomeE, median_income_moe_pct, median_income_moe_cat),
  col.names = c("County Name", "Median Income Estimate $", "Median Income MOE %", "Median Income MOE Category")
)
Selected Counties (1 in each reliability category)
County Name Median Income Estimate $ Median Income MOE % Median Income MOE Category
Cuyahoga 60074 1.163565 High Confidence: MOE < 5%
Marion 55106 8.313069 Moderate Confidence: MOE 5-10%
Vinton 50967 12.290306 Low Confidence: MOE > 10%

Comment on the output: Cuyahoga county is one of the most populous counties in Ohio, so the sample sizes used lent to its low MOE of ~1.16%. Although this can not be extrapolated to all counties, it is interesting to see a decline of median income as the MOE increases among the selected counties.

3.2 Tract-Level Demographics

  • Geography: tract level
  • Variables: white alone (B03002_003), Black/African American (B03002_004), Hispanic/Latino (B03002_012), total population (B03002_001)
  • Use the same state and year as before
  • Output format: wide
race_vars <- c(
  white    = "B03002_003",
  black    = "B03002_004",
  hispanic = "B03002_012",
  total    = "B03002_001"
)

# Based on https://www.census.gov/programs-surveys/geography/guidance/geo-identifiers.html,
# GEOIDs for counties are made of two digit state codes and three digit county codes. This is visible
# in how GEOID in county_data stays constant for the first two digits, and is unique for the last 3.
# We can parse this and create a list:
selected_counties <- selected_counties %>%
  mutate(county_code = str_sub(GEOID, 3, 5))

county_codes <- selected_counties %>% pull(county_code)

tract_demo_selected_counties = get_acs(
  geography = "tract",
  state     = my_state,
  county    = county_codes,
  variables = race_vars,
  output    = "wide"
)

# Calculate percentage of each group using mutate()
# Create percentages for white, Black, and Hispanic populations
tract_demo_selected_counties <- tract_demo_selected_counties %>%
  mutate(
    pct_white    = 100 * whiteE    / totalE,
    pct_black    = 100 * blackE    / totalE,
    pct_hispanic = 100 * hispanicE / totalE
  )

# Add readable tract and county name columns using str_extract() or similar
tract_demo_selected_counties <- tract_demo_selected_counties %>%
  mutate(
    tract_name  = str_extract(NAME, "^[^;]+"),
    county_name = str_extract(NAME, "(?<=;\\s).*")
  )

# We can clean up the county name as we did before:
words_to_remove <- c("County", "Ohio", '; ', ' ')
pattern <- paste0("\\b(", paste(words_to_remove, collapse = "|"), ")\\b")
tract_demo_selected_counties <- tract_demo_selected_counties %>%
  mutate(county_name = str_remove_all(county_name, pattern))

3.3 Demographic Analysis

# Find the tract with the highest percentage of Hispanic/Latino residents
# Hint: use arrange() and slice() to get the top tract
top_hispanic <- tract_demo_selected_counties %>%
  slice_max(order_by = pct_hispanic, n = 1)

top_hispanic %>%
  select(county_name, tract_name, pct_white, pct_black, pct_hispanic) %>%
  kable(
    caption = "Details of Census Tract with Highest Percentage Hispanic Residents",
    col.names = c("County Name", "Tract", "% White", "% Black", "% Hispanic")
  )
Details of Census Tract with Highest Percentage Hispanic Residents
County Name Tract % White % Black % Hispanic
Cuyahoga Census Tract 1029 18.89952 19.13876 61.48325
# Calculate average demographics by county using group_by() and summarize()
# Show: number of tracts, average percentage for each racial/ethnic group

selected_counties_summary <- tract_demo_selected_counties %>%
  group_by(county_name) %>%
  summarize(
    n_of_tracts      = n(),
    avg_pct_white = mean(pct_white,    na.rm = TRUE),
    avg_pct_black = mean(pct_black,    na.rm = TRUE),
    avg_pct_hisp  = mean(pct_hispanic, na.rm = TRUE),
  )

# Create a nicely formatted table of your results using kable()
selected_counties_summary %>%
  kable(
    caption = "Average Demographics for Selected Ohio Counties",
    digits = 2,
    col.names = c("County Name", "Number of Tracts", "Avg % White", "Avg % Black", "Avg % Hispanic")
  )
Average Demographics for Selected Ohio Counties
County Name Number of Tracts Avg % White Avg % Black Avg % Hispanic
Cuyahoga 428 51.12 34.98 6.77
Marion 18 86.13 4.75 3.50
Vinton 3 96.42 0.49 0.07

Part 4: Comprehensive Data Quality Evaluation

4.1 MOE Analysis for Demographic Variables

Workflow: - Calculate MOE percentages for each demographic variable - Flag tracts where any demographic variable has MOE > 15% - Create summary statistics

# I noticed some of the estimates or margins were 0s, or NaNs, so I'm accounting for that here as well:
tract_demo_selected_counties <- tract_demo_selected_counties %>%
  mutate(
    white_moe_pct    = if_else(whiteE    > 0, (whiteM    / whiteE), NA_real_) * 100,
    black_moe_pct    = if_else(blackE    > 0, (blackM    / blackE), NA_real_) * 100,
    hispanic_moe_pct = if_else(hispanicE > 0, (hispanicM / hispanicE), NA_real_) * 100
  )

# Create a flag for tracts with high MOE on any demographic variable
# Use logical operators (| for OR) in an ifelse() statement
tract_demo_selected_counties <- tract_demo_selected_counties %>%
  mutate(
    high_moe_flag = ifelse(
      white_moe_pct > 15 | black_moe_pct > 15 | hispanic_moe_pct > 15,
      TRUE,
      FALSE
    )
  )

# Create summary statistics showing how many tracts have data quality issues

tract_demo_selected_counties %>%
  group_by(county_name) %>%
  summarize(
    total_tracts    = sum(!is.na(high_moe_flag), na.rm = TRUE),
    high_moe_tracts = sum(high_moe_flag, na.rm = TRUE),
  ) %>%
  kable(
    caption = "Tract Data Quality by County",
    digits = 0,
    col.names = c("County Name", "Number of Tracts (excluding those without data)", "Tracts with Quality Issues (>15% MOE)")
  )
Tract Data Quality by County
County Name Number of Tracts (excluding those without data) Tracts with Quality Issues (>15% MOE)
Cuyahoga 421 421
Marion 18 18
Vinton 2 2

I noticed that there were some tracts with no data, so the sum of tracts excludes those.

4.2 Pattern Analysis

# Group tracts by whether they have high MOE issues
# Calculate average characteristics for each group:
# - population size, demographic percentages

# Use group_by() and summarize() to create this comparison
# Create a professional table showing the patterns

tract_demo_selected_counties %>%
  group_by(high_moe_flag) %>%
  summarize(
    avg_pct_white  = mean(pct_white, na.rm = TRUE),
    avg_pct_black  = mean(pct_black, na.rm = TRUE),
    avg_pct_hisp   = mean(pct_hispanic, na.rm = TRUE),
    n_tracts       = n(),
    .groups = "drop"
  ) %>%
  kable(
    caption = "Average Demographics by High MOE Status",
    digits = 1,
    col.names = c(
      "High MOE Flag",
      "Avg % White",
      "Avg % Black",
      "Avg % Hispanic",
      "Number of Tracts"
    )
  )
Average Demographics by High MOE Status
High MOE Flag Avg % White Avg % Black Avg % Hispanic Number of Tracts
TRUE 52.6 33.7 6.6 441
NA 98.9 0.0 0.0 8

This summary was simple, but when I did a visual inspection of the data, I see there’s some patterns that this table analysis is obscuring. I see that the MOE for Hispanic residents is above 15% for all tracts! I’ll do a second summary table, this time showing

tract_demo_selected_counties %>%
  summarize(
    n_tracts          = n(),
    white_high_moe    = sum(white_moe_pct    > 15, na.rm = TRUE),
    black_high_moe    = sum(black_moe_pct    > 15, na.rm = TRUE),
    hispanic_high_moe = sum(hispanic_moe_pct > 15, na.rm = TRUE),
    pct_white_high    = 100 * white_high_moe    / n_tracts,
    pct_black_high    = 100 * black_high_moe    / n_tracts,
    pct_hispanic_high = 100 * hispanic_high_moe / n_tracts
  ) %>%
  kable(
    caption = "Tracts with MOE > 15% by Demographic Group",
    digits = 1,
    col.names = c(
      "Total Tracts",
      "White High MOE Tracts",
      "Black High MOE Tracts",
      "Hispanic High MOE Tracts",
      "% of Tracts with White High MOE",
      "% of Tracts with Black High MOE",
      "% of Tracts with Hispanic High MOE"
    )
  )
Tracts with MOE > 15% by Demographic Group
Total Tracts White High MOE Tracts Black High MOE Tracts Hispanic High MOE Tracts % of Tracts with White High MOE % of Tracts with Black High MOE % of Tracts with Hispanic High MOE
449 306 416 403 68.2 92.7 89.8
tract_demo_selected_counties %>%
  group_by(county_name) %>%
  summarize(
    n_tracts          = n(),
    white_high_moe    = sum(white_moe_pct    > 15, na.rm = TRUE),
    black_high_moe    = sum(black_moe_pct    > 15, na.rm = TRUE),
    hispanic_high_moe = sum(hispanic_moe_pct > 15, na.rm = TRUE),
    pct_white_high    = 100 * white_high_moe    / n_tracts,
    pct_black_high    = 100 * black_high_moe    / n_tracts,
    pct_hispanic_high = 100 * hispanic_high_moe / n_tracts,
    .groups = "drop"
  ) %>%
  kable(
    caption = "County-Level Breakdown of Tracts with MOE > 15%, by Demographic Group",
    digits = 1,
    col.names = c(
      "County",
      "Total Tracts",
      "White High MOE Tracts",
      "Black High MOE Tracts",
      "Hispanic High MOE Tracts",
      "% of Tracts with White High MOE",
      "% of Tracts with Black High MOE",
      "% of Tracts with Hispanic High MOE"
    )
  )
County-Level Breakdown of Tracts with MOE > 15%, by Demographic Group
County Total Tracts White High MOE Tracts Black High MOE Tracts Hispanic High MOE Tracts % of Tracts with White High MOE % of Tracts with Black High MOE % of Tracts with Hispanic High MOE
Cuyahoga 428 298 402 383 69.6 93.9 89.5
Marion 18 8 12 18 44.4 66.7 100.0
Vinton 3 0 2 2 0.0 66.7 66.7

Pattern Analysis: A larger share of census tracts flagged as High MOE (>15%) are driven primarily by high median income margins of error among Black or Hispanic populations. The last chart highlights that, with both communities having a higher MOE as a percentage of tracts being marked.

For Marion and Vinton counties, this is potentially explained by Black and Hispanic populations being a smaller percentage of the total. This would make the sample sizes for both communities fairly small in these counties. In Cuyahoga county, a visual inspection reveals tracts being flagged when they have small percentages of Hispanic, Black, or White population.

Part 5: Policy Recommendations

5.1 Analysis Integration and Professional Summary

Executive Summary:

Across the analyses, a pattern emerges: the MOE for demographic data is highly sensitive to both the population size of census tracts and the demographic composition within them. Small-population counties, such as Vinton County, show particularly large MOEs due to limited sample sizes in the ACS. Similarly, tracts with highly diverse populations tend to have inflated MOEs across multiple demographic groups, making nearly every group susceptible to high uncertainty in the estimates.

In examining equity implications, Black and Hispanic populations face the highest risk of unreliable data. While the largest MOE observed for White populations is around 200%, Black and Hispanic populations frequently have MOEs in the hundreds of percent and, in extreme cases, as high as 2000%. This suggests that algorithmic decisions relying on this demographic data can be highly unreliable for these communities.

We can classify the root causes of these data issues into two categories. First, small sample sizes in low-population tracts and counties amplify MOEs for all estimates. Second, demographic variability can take the form of both extremely small minority populations and highly heterogeneous tracts. This further inflates MOE. An example is Census Tract 1711.02, which is nearly 87% Black but flagged as “High MOE” because the MOE for the White population exceeds 40%.

To address these systematic issues, decision-makers should incorporate demographic composition into analyses, weighting MOEs appropriately when interpreting the demographic data. Underrepresented communities should be prioritized for follow-up investigations at the tract or county level to better understand them. By acknowledging and adjusting for these data limitations, we can improve the equity and reliability of policy decisions.

6.3 Specific Recommendations

# Create a summary table using your county reliability data
# Include: county name, median income, MOE percentage, reliability category

# Add a new column with algorithm recommendations using case_when():
# - High Confidence: "Safe for algorithmic decisions"
# - Moderate Confidence: "Use with caution - monitor outcomes"  
# - Low Confidence: "Requires manual review or additional data"

# Format as a professional table with kable()

county_data %>%
  mutate(
    algorithm_recommendation = case_when(
      median_income_moe_cat == "High Confidence: MOE < 5%"  ~ "Safe for algorithmic decisions",
      median_income_moe_cat == "Moderate Confidence: MOE 5-10%" ~ "Use with caution - monitor outcomes",
      median_income_moe_cat == "Low Confidence: MOE > 10%" ~ "Requires manual review or additional data",
      TRUE ~ "Check data"
    )
  ) %>%
  select(county_name, median_incomeE, median_income_moe_pct, median_income_moe_cat, algorithm_recommendation) %>%
  kable(
    caption = "County-Level Median Income Reliability and Algorithmic Recommendations",
    digits = 1,
    col.names = c(
      "County Name",
      "Median Income",
      "MOE % Median Income",
      "Reliability Category",
      "Algorithm Recommendation"
    )
  )
County-Level Median Income Reliability and Algorithmic Recommendations
County Name Median Income MOE % Median Income Reliability Category Algorithm Recommendation
Adams 46234 8.8 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Allen 58976 3.7 High Confidence: MOE < 5% Safe for algorithmic decisions
Ashland 62254 4.7 High Confidence: MOE < 5% Safe for algorithmic decisions
Ashtabula 53663 4.1 High Confidence: MOE < 5% Safe for algorithmic decisions
Athens 48750 7.6 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Auglaize 75231 3.7 High Confidence: MOE < 5% Safe for algorithmic decisions
Belmont 56943 4.2 High Confidence: MOE < 5% Safe for algorithmic decisions
Brown 66677 4.6 High Confidence: MOE < 5% Safe for algorithmic decisions
Butler 77062 2.1 High Confidence: MOE < 5% Safe for algorithmic decisions
Carroll 59872 5.9 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Champaign 70486 5.5 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Clark 58954 2.6 High Confidence: MOE < 5% Safe for algorithmic decisions
Clermont 79573 2.9 High Confidence: MOE < 5% Safe for algorithmic decisions
Clinton 64210 7.1 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Columbiana 55473 4.0 High Confidence: MOE < 5% Safe for algorithmic decisions
Coshocton 52048 5.9 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Crawford 52486 4.2 High Confidence: MOE < 5% Safe for algorithmic decisions
Cuyahoga 60074 1.2 High Confidence: MOE < 5% Safe for algorithmic decisions
Darke 60237 6.5 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Defiance 69302 4.8 High Confidence: MOE < 5% Safe for algorithmic decisions
Delaware 123995 1.9 High Confidence: MOE < 5% Safe for algorithmic decisions
Erie 65171 4.4 High Confidence: MOE < 5% Safe for algorithmic decisions
Fairfield 82969 3.9 High Confidence: MOE < 5% Safe for algorithmic decisions
Fayette 56773 6.2 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Franklin 71070 1.3 High Confidence: MOE < 5% Safe for algorithmic decisions
Fulton 71453 4.5 High Confidence: MOE < 5% Safe for algorithmic decisions
Gallia 55533 6.4 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Geauga 97162 3.9 High Confidence: MOE < 5% Safe for algorithmic decisions
Greene 81243 3.0 High Confidence: MOE < 5% Safe for algorithmic decisions
Guernsey 53901 7.7 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Hamilton 68249 1.6 High Confidence: MOE < 5% Safe for algorithmic decisions
Hancock 67006 4.6 High Confidence: MOE < 5% Safe for algorithmic decisions
Hardin 55876 5.1 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Harrison 54056 6.7 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Henry 71616 5.9 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Highland 60522 5.0 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Hocking 59007 4.9 High Confidence: MOE < 5% Safe for algorithmic decisions
Holmes 72987 4.1 High Confidence: MOE < 5% Safe for algorithmic decisions
Huron 64144 4.1 High Confidence: MOE < 5% Safe for algorithmic decisions
Jackson 56549 7.8 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Jefferson 53124 4.4 High Confidence: MOE < 5% Safe for algorithmic decisions
Knox 71246 5.2 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Lake 76835 1.7 High Confidence: MOE < 5% Safe for algorithmic decisions
Lawrence 51846 5.2 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Licking 78505 2.8 High Confidence: MOE < 5% Safe for algorithmic decisions
Logan 69125 4.8 High Confidence: MOE < 5% Safe for algorithmic decisions
Lorain 67272 1.7 High Confidence: MOE < 5% Safe for algorithmic decisions
Lucas 57265 1.7 High Confidence: MOE < 5% Safe for algorithmic decisions
Madison 77062 4.7 High Confidence: MOE < 5% Safe for algorithmic decisions
Mahoning 54279 2.0 High Confidence: MOE < 5% Safe for algorithmic decisions
Marion 55106 8.3 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Medina 89968 2.7 High Confidence: MOE < 5% Safe for algorithmic decisions
Meigs 46255 6.9 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Mercer 73278 4.5 High Confidence: MOE < 5% Safe for algorithmic decisions
Miami 71440 3.2 High Confidence: MOE < 5% Safe for algorithmic decisions
Monroe 55381 9.8 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Montgomery 61942 1.6 High Confidence: MOE < 5% Safe for algorithmic decisions
Morgan 51056 7.8 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Morrow 70412 6.4 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Muskingum 56810 4.9 High Confidence: MOE < 5% Safe for algorithmic decisions
Noble 51547 10.8 Low Confidence: MOE > 10% Requires manual review or additional data
Ottawa 69515 5.0 High Confidence: MOE < 5% Safe for algorithmic decisions
Paulding 65331 5.7 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Perry 62899 7.3 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Pickaway 67600 4.7 High Confidence: MOE < 5% Safe for algorithmic decisions
Pike 50832 8.4 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Portage 69796 3.2 High Confidence: MOE < 5% Safe for algorithmic decisions
Preble 66355 5.9 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Putnam 79453 4.0 High Confidence: MOE < 5% Safe for algorithmic decisions
Richland 56557 3.5 High Confidence: MOE < 5% Safe for algorithmic decisions
Ross 58048 4.1 High Confidence: MOE < 5% Safe for algorithmic decisions
Sandusky 60814 5.0 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Scioto 46360 3.6 High Confidence: MOE < 5% Safe for algorithmic decisions
Seneca 62476 3.9 High Confidence: MOE < 5% Safe for algorithmic decisions
Shelby 73502 4.6 High Confidence: MOE < 5% Safe for algorithmic decisions
Stark 63130 1.8 High Confidence: MOE < 5% Safe for algorithmic decisions
Summit 68360 1.3 High Confidence: MOE < 5% Safe for algorithmic decisions
Trumbull 53537 3.6 High Confidence: MOE < 5% Safe for algorithmic decisions
Tuscarawas 61953 3.7 High Confidence: MOE < 5% Safe for algorithmic decisions
Union 104496 2.9 High Confidence: MOE < 5% Safe for algorithmic decisions
VanWert 64841 5.4 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes
Vinton 50967 12.3 Low Confidence: MOE > 10% Requires manual review or additional data
Warren 103128 2.3 High Confidence: MOE < 5% Safe for algorithmic decisions
Washington 59053 4.2 High Confidence: MOE < 5% Safe for algorithmic decisions
Wayne 70320 2.7 High Confidence: MOE < 5% Safe for algorithmic decisions
Williams 60632 3.6 High Confidence: MOE < 5% Safe for algorithmic decisions
Wood 70537 3.2 High Confidence: MOE < 5% Safe for algorithmic decisions
Wyandot 68552 7.6 Moderate Confidence: MOE 5-10% Use with caution - monitor outcomes

Key Recommendations:

  1. Counties suitable for immediate algorithmic implementation: Most Ohio counties are appropriate for immediate algorithmic use; however, some caveats apply. In counties with small or underrepresented populations, certain communities may face higher margins of error, and in highly diverse counties, careful attention or additional algorithmic analysis is needed.

  2. Counties requiring additional oversight: Adams, Athens, Carroll, Champaign, Clinton, Coshocton, Darke, Fayette, Gallia, Guernsey, Hardin, Harrison, Henry, Highland, Jackson, Knox, Lawrence, Marion, Meigs, Monroe, Morgan, Morrow, Paulding, Perry, Pike, Preble, Sandusky, Van Wert, Wyandot.

  • These counties can be further categorized based on population and demographic characteristics. In counties with very small populations, targeted local outreach may be sufficient to understand community needs. In larger or more diverse counties, more granular analysis at sub-county levels may be necessary to ensure accurate insights and equitable decision-making.
  1. Counties needing alternative approaches: Noble, Vinton.
  • These counties have some of the lowest populations in Ohio, making traditional ACS estimates highly uncertain. Given the small population size, targeted local outreach—allowing for larger effective sample sizes than the ACS—may be the most feasible approach to accurately assess community needs.

Questions for Further Investigation

The relationship between demographics and population size in determining MOE warrants further investigation. The current analysis, which applies MOE cutoffs based on either demographic or county-level data, involves tradeoffs and may exclude or underrepresent certain populations. Developing a cutoff that incorporates both population size and demographic characteristics could provide a more accurate and equitable assessment, rather than relying single, uniform metrics.

Technical Notes

Data Sources: - U.S. Census Bureau, American Community Survey 2018-2022 5-Year Estimates - Retrieved via tidycensus R package in September of 2025 (final run conducted on September 28th 2025)

Reproducibility: - All analysis conducted in R version 4.5.1 (2025-06-13) - Census API key required for replication - Complete code and documentation available at: https://musa-5080-fall-2025.github.io/portfolio-setup-kakumanus/

Methodology Notes: Nothing of note in this analysis.

Limitations: For algorithmic decision-making at the state government level, this analysis serves as an initial assessment, providing key considerations rather than providing a final solution. Further exploration is needed, particularly because relying on ACS-based estimates can inadvertently exclude populations—especially in counties where demographic characteristics make sampling approaches less reliable.