In this table below, it summarizes population, population change, and population density for fourteen Tennessee counties. Davidson County has the largest current population at 715,388 residents and also has the highest population density at approximately 1,419 people per square mile, placing it in the High Density category.
The data reveal important differences between population size and population density. For example, Williamson County has the third-largest population (260,351) but is classified as Medium Density because its population is spread across a relatively large land area. In contrast, counties such as Hickman, Cannon, and Smith have much smaller populations and lower densities, resulting in Low Density classifications.
| 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 |
Here is the code that produced the analysis. The analysis relied on data from U.S Census Bureau American Community Survey.
#=========================================================
# Step 1: Install and load required packages
#=========================================================
required_packages <- c("tidyverse", "knitr", "kableExtra")
for (pkg in required_packages) {
if (!requireNamespace(pkg, quietly = TRUE)) {
install.packages(pkg)
}
}
library(tidyverse)
library(knitr)
library(kableExtra)
#=========================================================
# 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 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 the difference
# between current and earlier population values
#=========================================================
Population <- Population %>%
mutate(Change = CurrentPop - EarlierPop)
#=========================================================
# Step 11: Display the updated population data frame
# with the new Change variable
#=========================================================
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 in descending order
#=========================================================
Doughnut <- Population %>%
filter(Region %in% c("Davidson", "Doughnut")) %>%
arrange(desc(Change))
#=========================================================
# Step 15: Display the Doughnut data frame
#=========================================================
Doughnut
#=========================================================
# Step 16: Create a Summary data frame that totals
# population values 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: Create a copy of the Population data frame
#=========================================================
Population_v2 <- Population
#=========================================================
# Step 19: Add a Percent_change variable to
# Population_v2
#=========================================================
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(CurrentPop))
#=========================================================
# Step 21: Display the Population_v2 data frame
#=========================================================
Population_v2
#=========================================================
# Step 22: Create and display a formatted table for
# Population
#=========================================================
Population_tbl <- Population %>%
kbl(caption = "Population Data") %>%
kable_styling(full_width = FALSE)
Population_tbl
#=========================================================
# Step 23: Create and display a formatted table for
# Change_only
#=========================================================
Change_only_tbl <- Change_only %>%
kbl(caption = "Population Change by County") %>%
kable_styling(full_width = FALSE)
Change_only_tbl
#=========================================================
# Step 24: Create and display a formatted table for
# Doughnut
#=========================================================
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
# Summary
#=========================================================
Summary_tbl <- Summary %>%
kbl(caption = "Regional Population Summary") %>%
kable_styling(full_width = FALSE)
Summary_tbl
#=========================================================
# Step 26: Create and display a formatted table for
# Population_v2
#=========================================================
Population_v2_tbl <- Population_v2 %>%
kbl(caption = "Population Data with Percent Change") %>%
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
# ==========================================================
Population_v2 <- Population_v2 %>%
left_join(
Land_Area %>%
select(County, Square_Miles),
by = "County"
)
# Display the updated data frame
Population_v2
#=========================================================
# Step 28: Add a Density variable to Population_v2
#=========================================================
Population_v2 <- Population_v2 %>%
mutate(Density = CurrentPop / Square_Miles)
#=========================================================
# Step 29: Display the updated Population_v2 data frame
# with the new Density variable
#=========================================================
Population_v2
#=========================================================
# Step 30: Create a Density_Category variable based on
# population density thresholds
#=========================================================
Population_v2 <- Population_v2 %>%
mutate(
Density_Category = case_when(
Density >= 500 ~ "High Density",
Density >= 100 & Density < 500 ~ "Medium Density",
Density < 100 ~ "Low Density"
)
)
#=========================================================
# Step 31: Sort Population_v2 by Density in descending
# order (highest density to lowest density)
#=========================================================
Population_v2 <- Population_v2 %>%
arrange(desc(Density))
#=========================================================
# Step 32: Create and display the final formatted table
#=========================================================
Population_v2_tbl <- Population_v2 %>%
kbl(
caption = "Population Data with Percent Change, Density, and Density Category"
) %>%
kable_styling(full_width = FALSE)
Population_v2_tbl
#=========================================================
# Step 33: Export the sorted data frame to CSV
#=========================================================
write_csv(Population_v2, "Population_v2.csv")
Population_v2 <- read.csv("Population_v2.csv")
#=========================================================
# Step 34: Add Density and Density_Category variables
# to Population_v2
#=========================================================
Population_v2 <- Population_v2 %>%
mutate(
Density = CurrentPop / Square_Miles,
Density_Category = case_when(
Density >= 500 ~ "High Density",
Density >= 100 & Density < 500 ~ "Medium Density",
Density < 100 ~ "Low Density"
)
)