Davidson County has a high population density, with about 1,419 residents per square mile. This means more people are concentrated within its land area, which can create different needs for housing, roads, and public services compared with less densely populated counties like Hickman and Cannon counties. Rutherford County is still considered high density because it has about 582 residents per square mile. Counties are classified as “High Density” if their density values are 500 or more, “Medium Density” if their density values are between 100 and 499, and “Low Density” if their density values are less than 100. To figure out Tennessee counties’ total land area, data was pulled from the U.S. Census Bureau’s Gazetteer Files website. Population data in this report came from the U.S. Census Bureau’s American Community Survey.

Population Data with Percent Change
County CurrentPop EarlierPop Region Change Percent_change Square_Miles Density Density_Category
Davidson 715388 598184 Davidson 117204 0.1959330 504 1419.42063 High Density
Rutherford 360646 292425 Doughnut 68221 0.2332940 620 581.68710 High Density
Williamson 260351 208242 Doughnut 52109 0.2502329 583 446.57118 Medium Density
Sumner 204424 174773 Doughnut 29651 0.1696544 529 386.43478 Medium Density
Wilson 158805 129918 Doughnut 28887 0.2223479 571 278.11734 Medium Density
Maury 107791 88738 Non-doughnut 19053 0.2147107 613 175.84176 Medium Density
Robertson 75539 67517 Doughnut 8022 0.1188145 476 158.69538 Medium Density
Cheatham 41829 39087 Doughnut 2742 0.0701512 302 138.50662 Medium Density
Dickson 55983 51608 Non-doughnut 4375 0.0847737 490 114.25102 Medium Density
Trousdale 11957 10131 Non-doughnut 1826 0.1802389 114 104.88596 Medium Density
Macon 26240 23261 Non-doughnut 2979 0.1280684 307 85.47231 Low Density
Smith 20389 19389 Non-doughnut 1000 0.0515756 314 64.93312 Low Density
Cannon 14818 13958 Non-doughnut 860 0.0616134 266 55.70677 Low Density
Hickman 25436 24561 Non-doughnut 875 0.0356256 612 41.56209 Low Density

CODE

############################################################
# Step 1: Install and load required packages
############################################################
required_packages <- c("tidyverse", "knitr", "kableExtra")

for (pkg in required_packages) {
  if (!require(pkg, character.only = TRUE)) {
    install.packages(pkg)
    library(pkg, character.only = TRUE)
  }
}

############################################################
# Step 2: Create a vector containing county names
############################################################
County <- c(
  "Cannon", "Cheatham", "Davidson", "Dickson", "Hickman",
  "Macon", "Maury", "Robertson", "Rutherford", "Smith",
  "Sumner", "Trousdale", "Williamson", "Wilson"
)

############################################################
# Step 3: Create a vector containing current population data
############################################################
CurrentPop <- c(
  14818, 41829, 715388, 55983, 25436, 26240, 107791,
  75539, 360646, 20389, 204424, 11957, 260351, 158805
)

############################################################
# Step 4: Create a vector containing earlier population data
############################################################
EarlierPop <- c(
  13958, 39087, 598184, 51608, 24561, 23261, 88738,
  67517, 292425, 19389, 174773, 10131, 208242, 129918
)

############################################################
# Step 5: Create a vector identifying each county's region
############################################################
Region <- c(
  "Non-doughnut", "Doughnut", "Davidson", "Non-doughnut",
  "Non-doughnut", "Non-doughnut", "Non-doughnut",
  "Doughnut", "Doughnut", "Non-doughnut", "Doughnut",
  "Non-doughnut", "Doughnut", "Doughnut"
)

############################################################
# Step 6: Combine all vectors into a population data frame
############################################################
Population <- data.frame(
  County,
  CurrentPop,
  EarlierPop,
  Region
)

############################################################
# Step 7: Display the original population data frame
############################################################
Population

############################################################
# Step 8: Sort the population data frame by current
# population in descending order
############################################################
Population <- Population %>%
  arrange(desc(CurrentPop))

############################################################
# Step 9: Display the sorted population data frame
############################################################
Population

############################################################
# Step 10: Add a Change variable showing population growth
############################################################
Population <- Population %>%
  mutate(Change = CurrentPop - EarlierPop)

############################################################
# Step 11: Display the updated data frame with Change
############################################################
Population

############################################################
# Step 12: Create a data frame containing only County
# and Change, sorted by Change in descending order
############################################################
Change_only <- Population %>%
  select(County, Change) %>%
  arrange(desc(Change))

############################################################
# Step 13: Display the Change_only data frame
############################################################
Change_only

############################################################
# Step 14: Create a Doughnut data frame containing only
# Davidson and Doughnut region counties, sorted by Change
############################################################
Doughnut <- Population %>%
  filter(Region == "Davidson" | Region == "Doughnut") %>%
  arrange(desc(Change))

############################################################
# Step 15: Display the Doughnut data frame
############################################################
Doughnut

############################################################
# Step 16: Create a Summary data frame that totals
# populations and population change by region
############################################################
Summary <- Population %>%
  group_by(Region) %>%
  summarize(
    CurrentPop = sum(CurrentPop),
    EarlierPop = sum(EarlierPop),
    Change = sum(Change)
  )

############################################################
# Step 17: Display the Summary data frame
############################################################
Summary

############################################################
# Step 18: Copy the Population data frame to a new
# data frame named Population_v2
############################################################
Population_v2 <- Population

############################################################
# Step 19: Create a Percent_change variable that measures
# population growth relative to earlier population
############################################################
Population_v2 <- Population_v2 %>%
  mutate(Percent_change = Change / EarlierPop)

############################################################
# Step 20: Sort Population_v2 by Percent_change in
# descending order
############################################################
Population_v2 <- Population_v2 %>%
  arrange(desc(Percent_change))

############################################################
# Step 21: Display the Population_v2 data frame
############################################################
Population_v2

############################################################
# Step 22: Create and display a formatted table for
# the Population data frame
############################################################
Population_tbl <- Population %>%
  kbl(caption = "Population Data") %>%
  kable_styling(full_width = FALSE)

Population_tbl

############################################################
# Step 23: Create and display a formatted table for
# the Change_only data frame
############################################################
Change_only_tbl <- Change_only %>%
  kbl(caption = "County Population Change") %>%
  kable_styling(full_width = FALSE)

Change_only_tbl

############################################################
# Step 24: Create and display a formatted table for
# the Doughnut data frame
############################################################
Doughnut_tbl <- Doughnut %>%
  kbl(caption = "Davidson and Doughnut Counties") %>%
  kable_styling(full_width = FALSE)

Doughnut_tbl

############################################################
# Step 25: Create and display a formatted table for
# the Summary data frame
############################################################
Summary_tbl <- Summary %>%
  kbl(caption = "Population Summary by Region") %>%
  kable_styling(full_width = FALSE)

Summary_tbl

############################################################
# Step 26: Create and display a formatted table for
# the Population_v2 data frame
############################################################
Population_v2_tbl <- Population_v2 %>%
  kbl(caption = "Population Growth Rates") %>%
  kable_styling(full_width = FALSE)

Population_v2_tbl
# ============================================================
# Download Tennessee County Gazetteer File
# ============================================================
# Import county land-area data from the U.S. Census Bureau.

TN_Counties <- read_delim(
  "https://www2.census.gov/geo/docs/maps-data/data/gazetteer/2025_Gazetteer/2025_gaz_counties_47.txt",
  delim = "|",
  show_col_types = FALSE
)

# ============================================================
# Create Land_Area Data Frame
# ============================================================
# Keep county name and land area in square miles.
# Remove " County" from county names.
# Round land area to the nearest whole square mile.

Land_Area <- TN_Counties %>%
  transmute(
    County = str_remove(NAME, " County"),
    Square_Miles = round(ALAND_SQMI)
  )

# ============================================================
# Display Results
# ============================================================
# View the completed land-area data frame.

Land_Area
############################################################
# Step 27: Add Square_Miles from Land_Area to Population_v2
# using County as the matching variable
############################################################
Population_v2 <- Population_v2 %>%
  left_join(
    Land_Area %>% select(County, Square_Miles),
    by = "County"
  )

############################################################
# Step 28: Display the updated Population_v2 data frame
############################################################
Population_v2
############################################################
# Step 29: Create a Density variable and sort by Density
############################################################
Population_v2 <- Population_v2 %>%
  mutate(Density = CurrentPop / Square_Miles) %>%
  arrange(desc(Density))

############################################################
# Step 30: Display the updated Population_v2 data frame
############################################################
Population_v2
############################################################
# Step 31: Create a Density_Category variable based on
# population density
############################################################
Population_v2 <- Population_v2 %>%
  mutate(
    Density_Category = case_when(
      Density >= 500 ~ "High Density",
      Density >= 100 & Density < 500 ~ "Medium Density",
      Density < 100 ~ "Low Density"
    )
  )

############################################################
# Step 32: Display the updated Population_v2 data frame
############################################################
Population_v2
############################################################
# Step 33: Create an updated formatted table for
# Population_v2
############################################################
Population_v2_tbl <- Population_v2 %>%
  kbl(caption = "Population Data with Percent Change") %>%
  kable_styling(full_width = FALSE)

############################################################
# Step 34: Display the updated table
############################################################
Population_v2_tbl