Seen below is a table of Nashville area counties that includes each county’s land area in square miles, population density, and a classification of each county as having a “High Density,” “Medium Density,” or “Low Density” population. The data shown in this table was obtained from the U.S. Census Bureau’s 2025 2025 Gazetteer Files.
The table shows Davidson County at the top, with approximately 1,419 residents per square mile of land. Davidson County, which encompasses the popular city of Nashville, is more than twice as densely populated as Rutherford County, which ranks second with about 582 residents per square mile. Looking at the data, it appears counties such as Rutherford and Williamson are growing at faster rates because they simply have more room for new residents than Davidson County. As Davidson County becomes more densely populated, much of the region’s growth is occurring in surrounding counties where more land is available.
| 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 |
# ==================================================
# STEP 1: Install and Load Required Packages
# Installs and loads tidyverse, knitr, and kableExtra.
# ==================================================
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 County Name Vector
# Stores the names of all counties included in the analysis.
# ==================================================
County <- c("Cannon", "Cheatham", "Davidson", "Dickson", "Hickman", "Macon",
"Maury", "Robertson", "Rutherford", "Smith", "Sumner", "Trousdale",
"Williamson", "Wilson")
# ==================================================
# STEP 3: Create Current Population Vector
# Stores the most recent population values for each county.
# ==================================================
CurrentPop <- c(14818, 41829, 715388, 55983, 25436, 26240, 107791, 75539,
360646, 20389, 204424, 11957, 260351, 158805)
# ==================================================
# STEP 4: Create Earlier Population Vector
# Stores population values from the earlier census period.
# ==================================================
EarlierPop <- c(13958, 39087, 598184, 51608, 24561, 23261, 88738, 67517,
292425, 19389, 174773, 10131, 208242, 129918)
# ==================================================
# STEP 5: Create Regional Classification Vector
# Categorizes each county by geographic 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 Data Frame
# Creates a structured dataset containing all variables.
# ==================================================
Population <- data.frame(
County,
CurrentPop,
EarlierPop,
Region
)
# ==================================================
# STEP 7: Sort Data by Current Population
# Arranges counties from highest to lowest current population.
# ==================================================
Population <- Population %>%
arrange(desc(CurrentPop))
# ==================================================
# STEP 8: Add Population Change Variable
# Creates a new variable showing population growth.
# ==================================================
Population <- Population %>%
mutate(Change = CurrentPop - EarlierPop)
# ==================================================
# STEP 9: Display the Updated Population Data Frame
# Prints the sorted dataset with the Change variable.
# ==================================================
Population
# ==================================================
# STEP 10: Create Change_only Data Frame
# Selects only County and Change variables and
# sorts by Change in descending order.
# ==================================================
Change_only <- Population %>%
select(County, Change) %>%
arrange(desc(Change))
# ==================================================
# STEP 11: Display the Change_only Data Frame
# Prints counties ranked by population change.
# ==================================================
Change_only
# ==================================================
# STEP 12: Create Doughnut Data Frame
# Filters for Davidson and Doughnut counties and
# sorts them by Change in descending order.
# ==================================================
Doughnut <- Population %>%
filter(Region %in% c("Davidson", "Doughnut")) %>%
arrange(desc(Change))
# ==================================================
# STEP 13: Display the Doughnut Data Frame
# Prints the filtered and sorted Doughnut dataset.
# ==================================================
Doughnut
# ==================================================
# STEP 14: Create Summary Data Frame
# Groups counties by Region and totals population
# variables for each region.
# ==================================================
Summary <- Population %>%
group_by(Region) %>%
summarize(
CurrentPop = sum(CurrentPop),
EarlierPop = sum(EarlierPop),
Change = sum(Change)
)
# ==================================================
# STEP 15: Display the Summary Data Frame
# Prints regional population totals and change.
# ==================================================
Summary
# ==================================================
# STEP 16: Create Population_v2 Data Frame
# Copies the Population data frame into a new data frame.
# ==================================================
Population_v2 <- Population
# ==================================================
# STEP 17: Add Percent_change Variable
# Calculates percentage population change.
# ==================================================
Population_v2 <- Population_v2 %>%
mutate(Percent_change = Change / EarlierPop)
# ==================================================
# STEP 18: Sort Population_v2 by Percent Change
# Arranges counties from highest to lowest percent change.
# ==================================================
Population_v2 <- Population_v2 %>%
arrange(desc(Percent_change))
# ==================================================
# STEP 19: Display the Population_v2 Data Frame
# Prints the data frame with Percent_change included.
# ==================================================
Population_v2
# ==================================================
# STEP 20: Create Formatted Population Table
# Creates a kableExtra-formatted table for Population.
# ==================================================
Population_table <- Population %>%
kbl(caption = "Population Data") %>%
kable_styling(full_width = FALSE)
# ==================================================
# STEP 21: Display Population Table
# ==================================================
Population_table
# ==================================================
# STEP 22: Create Formatted Change_only Table
# Creates a kableExtra-formatted table for Change_only.
# ==================================================
Change_only_table <- Change_only %>%
kbl(caption = "County Population Change") %>%
kable_styling(full_width = FALSE)
# ==================================================
# STEP 23: Display Change_only Table
# ==================================================
Change_only_table
# ==================================================
# STEP 24: Create Formatted Doughnut Table
# Creates a kableExtra-formatted table for Doughnut.
# ==================================================
Doughnut_table <- Doughnut %>%
kbl(caption = "Davidson and Doughnut Counties") %>%
kable_styling(full_width = FALSE)
# ==================================================
# STEP 25: Display Doughnut Table
# ==================================================
Doughnut_table
# ==================================================
# STEP 26: Create Formatted Summary Table
# Creates a kableExtra-formatted table for Summary.
# ==================================================
Summary_table <- Summary %>%
kbl(caption = "Regional Population Summary") %>%
kable_styling(full_width = FALSE)
# ==================================================
# STEP 27: Display Summary Table
# ==================================================
Summary_table
# ==================================================
# STEP 28: Create Formatted Population_v2 Table
# Creates a kableExtra-formatted table for Population_v2.
# ==================================================
Population_v2_table <- Population_v2 %>%
kbl(caption = "Population Percent Change") %>%
kable_styling(full_width = FALSE)
# ==================================================
# STEP 29: Display Population_v2 Table
# ==================================================
Population_v2_table
# ============================================================
# 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 30: Add Square_Miles to Population_v2
# Uses a left join to add the Square_Miles variable
# from the Land_Area data frame, matching by County.
# ==================================================
Population_v2 <- Population_v2 %>%
left_join(
Land_Area %>% select(County, Square_Miles),
by = "County"
)
# ==================================================
# STEP 31: Display the Updated Population_v2 Data Frame
# Prints Population_v2 with the Square_Miles variable.
# ==================================================
Population_v2
# ==================================================
# STEP 32: Add Population Density Variable
# Calculates population density by dividing
# CurrentPop by Square_Miles.
# ==================================================
Population_v2 <- Population_v2 %>%
mutate(Density = CurrentPop / Square_Miles)
# ==================================================
# STEP 33: Sort Population_v2 by Density
# Arranges counties from highest to lowest density.
# ==================================================
Population_v2 <- Population_v2 %>%
arrange(desc(Density))
# ==================================================
# STEP 34: Display the Updated Population_v2 Data Frame
# Prints the data frame with Density included.
# ==================================================
Population_v2
# ==================================================
# STEP 35: Create Density_Category Variable
# Classifies counties 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 36: Display the Updated Population_v2 Data Frame
# Prints the data frame with Density_Category included.
# ==================================================
Population_v2
# ==================================================
# STEP 37: Create Updated Population_v2 Table
# Creates a formatted table including density data.
# ==================================================
Population_v2_table <- Population_v2 %>%
kbl(caption = "Population Data with Percent Change") %>%
kable_styling(full_width = FALSE)
# ==================================================
# STEP 38: Display Updated Population_v2 Table
# ==================================================
Population_v2_table
write_csv(Population_v2, "Population_v2.csv")
Population_v2 <- read.csv("Population_v2.csv")
saveRDS(Population_v2, "Population_v2.RDS")
Population_v2 <- readRDS("Population_v2.RDS")