This table shows the count and proportion of county residents within each 2026 district, as extrapolated from 2020 Census tract data. For counties that are split across two or more districts, the count and percentage are shown for each district.
| County | Status | District | Population | Percent of County |
|---|---|---|---|---|
| Anderson | Whole | District 3 | 77,094 | 100.0% |
| Bedford | Whole | District 9 | 50,231 | 100.0% |
| Benton | Whole | District 5 | 15,863 | 100.0% |
| Bledsoe | Whole | District 4 | 14,911 | 100.0% |
| Blount | Whole | District 2 | 135,273 | 100.0% |
| Bradley | Whole | District 3 | 108,616 | 100.0% |
| Cannon | Whole | District 4 | 14,505 | 100.0% |
| Carroll | Whole | District 8 | 28,438 | 100.0% |
| Carter | Whole | District 1 | 56,346 | 100.0% |
| Cheatham | Whole | District 7 | 41,071 | 100.0% |
| Chester | Whole | District 8 | 17,339 | 100.0% |
| Claiborne | Whole | District 2 | 32,040 | 100.0% |
| Clay | Whole | District 6 | 7,577 | 100.0% |
| Cocke | Whole | District 1 | 35,991 | 100.0% |
| Coffee | Whole | District 4 | 57,886 | 100.0% |
| Crockett | Whole | District 8 | 13,910 | 100.0% |
| Cumberland | Whole | District 6 | 61,143 | 100.0% |
| DeKalb | Whole | District 6 | 20,078 | 100.0% |
| Decatur | Whole | District 8 | 11,434 | 100.0% |
| Dickson | Whole | District 7 | 54,306 | 100.0% |
| Dyer | Whole | District 5 | 36,795 | 100.0% |
| Fentress | Whole | District 6 | 18,489 | 100.0% |
| Franklin | Whole | District 4 | 42,770 | 100.0% |
| Gibson | Whole | District 8 | 50,424 | 100.0% |
| Giles | Whole | District 9 | 30,343 | 100.0% |
| Grainger | Whole | District 2 | 23,509 | 99.9% |
| Greene | Whole | District 1 | 70,138 | 100.0% |
| Grundy | Whole | District 4 | 13,529 | 100.0% |
| Hamblen | Whole | District 1 | 64,482 | 100.0% |
| Hamilton | Whole | District 3 | 366,094 | 100.0% |
| Hancock | Whole | District 1 | 6,661 | 100.0% |
| Hardeman | Whole | District 9 | 25,459 | 100.0% |
| Hardin | Whole | District 9 | 26,829 | 100.0% |
| Hawkins | Whole | District 1 | 56,703 | 100.0% |
| Haywood | Whole | District 8 | 17,864 | 100.0% |
| Henderson | Whole | District 8 | 27,842 | 100.0% |
| Henry | Whole | District 5 | 32,198 | 100.0% |
| Hickman | Whole | District 5 | 24,924 | 100.0% |
| Houston | Whole | District 5 | 8,283 | 100.0% |
| Humphreys | Whole | District 5 | 18,988 | 100.0% |
| Jackson | Whole | District 6 | 11,617 | 100.0% |
| Johnson | Whole | District 1 | 17,931 | 100.0% |
| Knox | Whole | District 2 | 478,913 | 100.0% |
| Lake | Whole | District 5 | 7,000 | 100.0% |
| Lauderdale | Whole | District 5 | 25,132 | 100.0% |
| Lawrence | Whole | District 9 | 44,158 | 100.0% |
| Lewis | Whole | District 5 | 12,579 | 100.0% |
| Lincoln | Whole | District 9 | 35,314 | 100.0% |
| Loudon | Whole | District 2 | 54,871 | 100.0% |
| Macon | Whole | District 7 | 25,209 | 100.0% |
| Madison | Whole | District 8 | 98,823 | 100.0% |
| Marion | Whole | District 4 | 28,820 | 100.0% |
| Marshall | Whole | District 9 | 34,316 | 100.0% |
| McMinn | Whole | District 3 | 53,275 | 100.0% |
| McNairy | Whole | District 9 | 25,864 | 100.0% |
| Meigs | Whole | District 3 | 12,758 | 100.0% |
| Monroe | Whole | District 3 | 46,233 | 100.0% |
| Moore | Whole | District 9 | 6,455 | 99.9% |
| Morgan | Whole | District 6 | 21,030 | 100.0% |
| Obion | Whole | District 5 | 30,783 | 100.0% |
| Overton | Whole | District 6 | 22,511 | 100.0% |
| Perry | Whole | District 8 | 8,364 | 100.0% |
| Pickett | Whole | District 6 | 4,997 | 100.0% |
| Polk | Whole | District 3 | 17,537 | 100.0% |
| Putnam | Whole | District 6 | 79,854 | 100.0% |
| Roane | Whole | District 3 | 53,384 | 100.0% |
| Robertson | Whole | District 7 | 72,795 | 100.0% |
| Scott | Whole | District 6 | 21,846 | 100.0% |
| Sequatchie | Whole | District 4 | 15,825 | 100.0% |
| Sevier | Whole | District 1 | 98,349 | 100.0% |
| Smith | Whole | District 6 | 19,903 | 100.0% |
| Stewart | Whole | District 5 | 13,651 | 100.0% |
| Sullivan | Whole | District 1 | 158,122 | 100.0% |
| Trousdale | Whole | District 7 | 11,612 | 100.0% |
| Unicoi | Whole | District 1 | 17,909 | 100.0% |
| Union | Whole | District 2 | 19,802 | 100.0% |
| Van Buren | Whole | District 4 | 6,167 | 100.0% |
| Warren | Whole | District 4 | 40,951 | 100.0% |
| Washington | Whole | District 1 | 133,001 | 100.0% |
| Wayne | Whole | District 9 | 16,230 | 100.0% |
| Weakley | Whole | District 5 | 32,894 | 100.0% |
| White | Whole | District 6 | 27,348 | 100.0% |
| Wilson | Whole | District 6 | 147,723 | 100.0% |
| Campbell | Split | District 2 | 21,775 | 55.5% |
| Campbell | Split | District 6 | 17,484 | 44.5% |
| Davidson | Split | District 4 | 229,036 | 32.0% |
| Davidson | Split | District 6 | 266,692 | 37.3% |
| Davidson | Split | District 7 | 220,133 | 30.7% |
| Fayette | Split | District 8 | 19,544 | 46.5% |
| Fayette | Split | District 9 | 22,445 | 53.5% |
| Jefferson | Split | District 1 | 53,264 | 97.4% |
| Jefferson | Split | District 2 | 1,419 | 2.6% |
| Maury | Split | District 5 | 70,449 | 69.8% |
| Maury | Split | District 9 | 30,525 | 30.2% |
| Montgomery | Split | District 5 | 54,622 | 24.8% |
| Montgomery | Split | District 7 | 165,406 | 75.2% |
| Rhea | Split | District 3 | 32,362 | 98.5% |
| Rhea | Split | District 6 | 504 | 1.5% |
| Rutherford | Split | District 4 | 301,393 | 88.3% |
| Rutherford | Split | District 9 | 40,090 | 11.7% |
| Shelby | Split | District 5 | 224,669 | 24.2% |
| Shelby | Split | District 8 | 434,844 | 46.8% |
| Shelby | Split | District 9 | 269,787 | 29.0% |
| Sumner | Split | District 6 | 21,691 | 11.1% |
| Sumner | Split | District 7 | 174,585 | 88.9% |
| Tipton | Split | District 5 | 25,957 | 42.6% |
| Tipton | Split | District 8 | 35,004 | 57.4% |
| Williamson | Split | District 5 | 139,596 | 56.4% |
| Williamson | Split | District 9 | 108,106 | 43.6% |
Code:
# ============================================================
# Step 0. INSTALL AND LOAD REQUIRED PACKAGES
# ============================================================
if (!require("tidyverse")) install.packages("tidyverse")
if (!require("tidycensus")) install.packages("tidycensus")
if (!require("sf")) install.packages("sf")
if (!require("tigris")) install.packages("tigris")
if (!require("lwgeom")) install.packages("lwgeom", type = "binary")
if (!require("kableExtra")) install.packages("kableExtra")
library(tidyverse)
library(tidycensus)
library(sf)
library(tigris)
library(lwgeom)
library(kableExtra)
options(tigris_use_cache = TRUE)
# ============================================================
# Step 1. FETCH TRACT-LEVEL TOTAL POPULATION
# ============================================================
tract_data <- get_decennial(
geography = "tract",
state = "TN",
variables = "P2_001N",
year = 2020,
geometry = TRUE
) %>%
rename(TotalPop = value) %>%
st_transform(4326)
# ============================================================
# Step 2. ADD COUNTY INFO (ROBUST)
# ============================================================
tract_data <- tract_data %>%
mutate(county_fips = substr(GEOID, 1, 5))
counties_df <- tigris::counties(state = "TN", cb = TRUE) %>%
st_drop_geometry() %>%
transmute(
county_fips = GEOID,
county_name = NAME
)
tract_data <- tract_data %>%
left_join(counties_df, by = "county_fips") %>%
select(GEOID, county_name, TotalPop, geometry)
# ============================================================
# Step 3. LOAD 2026 DISTRICT SHAPEFILE
# ============================================================
NewDistricts <- st_read(
"NewCongressional26.shp",
quiet = TRUE
) %>%
st_transform(4326) %>%
st_make_valid() %>%
mutate(
district_num = as.integer(DISTRICT),
cd_name = paste0("District ", district_num)
) %>%
select(cd_name, district_num, geometry)
# ============================================================
# Step 4. AREA-WEIGHTED INTERSECTION
# ============================================================
sf::sf_use_s2(FALSE)
intersections <- st_intersection(
tract_data,
NewDistricts
)
intersections <- intersections %>%
mutate(int_area = as.numeric(st_area(geometry)))
tract_areas <- tract_data %>%
mutate(tract_area = as.numeric(st_area(geometry))) %>%
st_drop_geometry() %>%
select(GEOID, tract_area)
intersections <- intersections %>%
left_join(tract_areas, by = "GEOID") %>%
mutate(
weight = int_area / tract_area,
alloc_pop = TotalPop * weight
)
# ============================================================
# Step 5. COUNTY × DISTRICT SUMS
# ============================================================
county_district_2026 <- intersections %>%
st_drop_geometry() %>%
group_by(county_name, cd_name, district_num) %>%
summarise(
population = sum(alloc_pop, na.rm = TRUE),
.groups = "drop"
)
# ============================================================
# Step 6. COUNTY TOTALS + PERCENTAGES
# ============================================================
county_totals <- county_district_2026 %>%
group_by(county_name) %>%
summarise(
county_total = sum(population),
.groups = "drop"
)
county_district_2026 <- county_district_2026 %>%
left_join(county_totals, by = "county_name") %>%
mutate(
percent_of_county = 100 * population / county_total
)
# ============================================================
# ✅ Step 7. APPLY "MEANINGFUL SPLIT" THRESHOLD
# ============================================================
# Flag meaningful shares (≥ 1%)
county_district_2026 <- county_district_2026 %>%
mutate(
meaningful = percent_of_county >= 1
)
# Recompute split status using threshold
county_split_flags <- county_district_2026 %>%
group_by(county_name) %>%
summarise(
num_meaningful_districts = sum(meaningful),
.groups = "drop"
) %>%
mutate(
Status = if_else(num_meaningful_districts > 1, "Split", "Whole")
)
# Join back
county_district_2026 <- county_district_2026 %>%
left_join(county_split_flags, by = "county_name")
sf::sf_use_s2(TRUE)
# ============================================================
# ✅ Step 8. FINAL TABLE (FILTERING OUT SLIVERS)
# ============================================================
Table_All_Counties_2026 <- county_district_2026 %>%
# Keep only meaningful portions
filter(percent_of_county >= 1) %>%
mutate(
Population = scales::comma(round(population)),
`Percent of County` = sprintf("%.1f%%", percent_of_county)
) %>%
arrange(desc(Status), county_name, district_num) %>%
select(
County = county_name,
Status,
District = cd_name,
Population,
`Percent of County`
) %>%
kbl(
format = "html",
caption = "Tennessee Counties by 2026 Congressional District (Meaningful Population Splits)"
) %>%
kable_styling(full_width = FALSE)
Table_All_Counties_2026
# ============================================================
# Step 9. EXPORT TABLE AS STAND-ALONE HTML
# ============================================================
save_kable(
Table_All_Counties_2026,
file = "County_District_Splits_2026.html",
self_contained = TRUE
)