The table below displays population, population growth, land area, population density, and density categories for selected Middle Tennessee counties. Davidson County emerged as the most densely populated county in the dataset, with at least 1,149 residents per square mile. Rutherford County ranked a distant second in density, and Williamson County ranked third. The results indicate that Davidson County remains the urban core of the Nashville Metropolitan Area. At the same time, surrounding counties such as Rutherford and Williamson continue to experience substantial population growth, but at much lower population densities than Davidson County.
| County | CurrentPop | EarlierPop | Region | Change | Percent_change | Square_Miles | Density | Density_Category |
|---|---|---|---|---|---|---|---|---|
| Davidson | 715388 | 598184 | Davidson | 117204 | 0.1959 | 504 | 1419.4206 | High Density |
| Rutherford | 360646 | 292425 | Doughnut | 68221 | 0.2333 | 620 | 581.6871 | High Density |
| Williamson | 260351 | 208242 | Doughnut | 52109 | 0.2502 | 583 | 446.5712 | Medium Density |
| Sumner | 204424 | 174773 | Doughnut | 29651 | 0.1697 | 529 | 386.4348 | Medium Density |
| Wilson | 158805 | 129918 | Doughnut | 28887 | 0.2223 | 571 | 278.1173 | Medium Density |
| Maury | 107791 | 88738 | Non-doughnut | 19053 | 0.2147 | 613 | 175.8418 | Medium Density |
| Robertson | 75539 | 67517 | Doughnut | 8022 | 0.1188 | 476 | 158.6954 | Medium Density |
| Cheatham | 41829 | 39087 | Doughnut | 2742 | 0.0702 | 302 | 138.5066 | Medium Density |
| Dickson | 55983 | 51608 | Non-doughnut | 4375 | 0.0848 | 490 | 114.2510 | Medium Density |
| Trousdale | 11957 | 10131 | Non-doughnut | 1826 | 0.1802 | 114 | 104.8860 | Medium Density |
| Macon | 26240 | 23261 | Non-doughnut | 2979 | 0.1281 | 307 | 85.4723 | Low Density |
| Smith | 20389 | 19389 | Non-doughnut | 1000 | 0.0516 | 314 | 64.9331 | Low Density |
| Cannon | 14818 | 13958 | Non-doughnut | 860 | 0.0616 | 266 | 55.7068 | Low Density |
| Hickman | 25436 | 24561 | Non-doughnut | 875 | 0.0356 | 612 | 41.5621 | Low Density |
Here is the code that produced the analysis, which relied on information from the U.S. Census Bureau’s 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 the 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 counts
# ============================================================
CurrentPop <- c(
14818, 41829, 715388, 55983, 25436, 26240,
107791, 75539, 360646, 20389, 204424, 11957,
260351, 158805
)
# ============================================================
# Step 4: Create a vector containing earlier population counts
# ============================================================
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 the vectors into a population data frame
# ============================================================
Population <- data.frame(
County,
CurrentPop,
EarlierPop,
Region
)
# ============================================================
# Step 7: Add a Change variable showing population increase
# ============================================================
Population <- Population %>%
mutate(Change = CurrentPop - EarlierPop)
# ============================================================
# Step 8: Sort the data frame by CurrentPop in descending order
# ============================================================
Population <- Population %>%
arrange(desc(CurrentPop))
# ============================================================
# Step 9: Display the updated and sorted population data frame
# ============================================================
Population
# ============================================================
# Step 10: Create a data frame containing only County and Change
# ============================================================
Change_only <- Population %>%
select(County, Change)
# ============================================================
# Step 11: Sort the Change_only data frame by Change descending
# ============================================================
Change_only <- Change_only %>%
arrange(desc(Change))
# ============================================================
# Step 12: Display the Change_only data frame
# ============================================================
Change_only
# ============================================================
# Step 13: Create a Doughnut data frame containing only
# Davidson and Doughnut-region counties
# ============================================================
Doughnut <- Population %>%
filter(Region %in% c("Davidson", "Doughnut"))
# ============================================================
# Step 14: Sort the Doughnut data frame by Change descending
# ============================================================
Doughnut <- Doughnut %>%
arrange(desc(Change))
# ============================================================
# Step 15: Display the Doughnut data frame
# ============================================================
Doughnut
# ============================================================
# Step 16: Create a Summary data frame containing totals by
# Region
# ============================================================
Summary <- Population %>%
group_by(Region) %>%
summarise(
TotalCurrentPop = sum(CurrentPop),
TotalEarlierPop = sum(EarlierPop),
TotalChange = sum(Change),
.groups = "drop"
)
# ============================================================
# 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
# ============================================================
Population_v2 <- Population_v2 %>%
mutate(Percent_change = Change / EarlierPop)
# ============================================================
# Step 20: Sort the data frame by Percent_change descending
# ============================================================
Population_v2 <- Population_v2 %>%
arrange(desc(Percent_change))
# ============================================================
# Step 21: Display the Population_v2 data frame
# ============================================================
Population_v2
# ============================================================
# Step 22: Create a formatted table for Population
# ============================================================
Population_tbl <- Population %>%
kbl(
caption = "Population Data",
digits = 4
) %>%
kable_styling(
bootstrap_options = c(
"striped",
"hover",
"condensed"
),
full_width = FALSE
)
# ============================================================
# Step 23: Display the Population formatted table
# ============================================================
Population_tbl
# ============================================================
# Step 24: Create a formatted table for Change_only
# ============================================================
Change_only_tbl <- Change_only %>%
kbl(
caption = "County Population Change",
digits = 4
) %>%
kable_styling(
bootstrap_options = c(
"striped",
"hover",
"condensed"
),
full_width = FALSE
)
# ============================================================
# Step 25: Display the Change_only formatted table
# ============================================================
Change_only_tbl
# ============================================================
# Step 26: Create a formatted table for Doughnut
# ============================================================
Doughnut_tbl <- Doughnut %>%
kbl(
caption = "Davidson and Doughnut Counties",
digits = 4
) %>%
kable_styling(
bootstrap_options = c(
"striped",
"hover",
"condensed"
),
full_width = FALSE
)
# ============================================================
# Step 27: Display the Doughnut formatted table
# ============================================================
Doughnut_tbl
# ============================================================
# Step 28: Create a formatted table for Summary
# ============================================================
Summary_tbl <- Summary %>%
kbl(
caption = "Regional Population Summary",
digits = 4
) %>%
kable_styling(
bootstrap_options = c(
"striped",
"hover",
"condensed"
),
full_width = FALSE
)
# ============================================================
# Step 29: Display the Summary formatted table
# ============================================================
Summary_tbl
# ============================================================
# Step 30: Create a formatted table for Population_v2
# ============================================================
Population_v2_tbl <- Population_v2 %>%
kbl(
caption = "Population Data with Percent Change",
digits = 4
) %>%
kable_styling(
bootstrap_options = c(
"striped",
"hover",
"condensed"
),
full_width = FALSE
)
# ============================================================
# Step 31: Display the Population_v2 formatted table
# ============================================================
Population_v2_tbl
# ============================================================
# Step 32: 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
)
# ============================================================
# Step 33: 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)
)
# ============================================================
# Step 34: Display Results
# ============================================================
# View the completed land-area data frame.
Land_Area
# ============================================================
# Step 35: Add Square_Miles from Land_Area using a left join
# ============================================================
Population_v2 <- Population_v2 %>%
left_join(
Land_Area %>%
select(County, Square_Miles),
by = "County"
)
# ============================================================
# Step 36: Display the updated Population_v2 data frame
# ============================================================
Population_v2
# ============================================================
# Step 37: Add Density and Density_Category variables
# ============================================================
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"
)
)
# ============================================================
# Step 38: Sort the Population_v2 data frame by Density
# in descending order
# ============================================================
Population_v2 <- Population_v2 %>%
arrange(desc(Density))
# ============================================================
# Step 39: Display the updated Population_v2 data frame
# ============================================================
Population_v2
# ============================================================
# Step 40: Create a formatted table for Population_v2
# ============================================================
Population_v2_tbl <- Population_v2 %>%
kbl(
caption = "Population Data with Density Measures",
digits = 4
) %>%
kable_styling(
bootstrap_options = c(
"striped",
"hover",
"condensed"
),
full_width = FALSE
)
# ============================================================
# Step 41: Display the Population_v2 formatted table
# ============================================================
Population_v2_tbl