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.


Tennessee Counties by 2026 Congressional District (Meaningful Population Splits)
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
)