##        County CurrentPop EarlierPop       Region Change Percent_change
## 1    Davidson     715388     598184     Davidson 117204     0.19593302
## 2  Rutherford     360646     292425     Doughnut  68221     0.23329401
## 3  Williamson     260351     208242     Doughnut  52109     0.25023290
## 4      Sumner     204424     174773     Doughnut  29651     0.16965435
## 5      Wilson     158805     129918     Doughnut  28887     0.22234794
## 6       Maury     107791      88738 Non-doughnut  19053     0.21471072
## 7   Robertson      75539      67517     Doughnut   8022     0.11881452
## 8    Cheatham      41829      39087     Doughnut   2742     0.07015120
## 9     Dickson      55983      51608 Non-doughnut   4375     0.08477368
## 10  Trousdale      11957      10131 Non-doughnut   1826     0.18023887
## 11      Macon      26240      23261 Non-doughnut   2979     0.12806844
## 12      Smith      20389      19389 Non-doughnut   1000     0.05157564
## 13     Cannon      14818      13958 Non-doughnut    860     0.06161341
## 14    Hickman      25436      24561 Non-doughnut    875     0.03562559
##    Square_Miles    Density Density_Category
## 1           504 1419.42063     High Density
## 2           620  581.68710     High Density
## 3           583  446.57118   Medium Density
## 4           529  386.43478   Medium Density
## 5           571  278.11734   Medium Density
## 6           613  175.84176   Medium Density
## 7           476  158.69538   Medium Density
## 8           302  138.50662   Medium Density
## 9           490  114.25102   Medium Density
## 10          114  104.88596   Medium Density
## 11          307   85.47231      Low Density
## 12          314   64.93312      Low Density
## 13          266   55.70677      Low Density
## 14          612   41.56209      Low Density

Code

# ==============================================================
# Step 1: Install (if needed) and load the tidyverse, knitr, and
#         kableExtra packages
# ==============================================================
if (!requireNamespace("tidyverse", quietly = TRUE)) {
  install.packages("tidyverse")
}
if (!requireNamespace("knitr", quietly = TRUE)) {
  install.packages("knitr")
}
if (!requireNamespace("kableExtra", quietly = TRUE)) {
  install.packages("kableExtra")
}
library(tidyverse)
library(knitr)
library(kableExtra)

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

# ==============================================================
# Step 3: Create a vector of current population figures
#         (one value per county, in the same order as County)
# ==============================================================
CurrentPop <- c(14818, 41829, 715388, 55983, 25436, 26240, 107791, 75539, 360646,
                20389, 204424, 11957, 260351, 158805)

# ==============================================================
# Step 4: Create a vector of earlier population figures
#         (one value per county, in the same order as County)
# ==============================================================
EarlierPop <- c(13958, 39087, 598184, 51608, 24561, 23261, 88738, 67517, 292425,
                19389, 174773, 10131, 208242, 129918)

# ==============================================================
# Step 5: Create a vector classifying each county's region
#         (Doughnut, Non-doughnut, or Davidson)
# ==============================================================
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 single data frame
# ==============================================================
Population <- data.frame(
  County,
  CurrentPop,
  EarlierPop,
  Region
)

# ==============================================================
# Step 7: Display the resulting data frame
# ==============================================================
Population

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

# ==============================================================
# Step 9: Redisplay the Population data frame (now sorted)
# ==============================================================
Population

# ==============================================================
# Step 10: Use mutate() to add a "Change" variable, calculated
#          as CurrentPop minus EarlierPop
# ==============================================================
Population <- Population %>%
  mutate(Change = CurrentPop - EarlierPop)

# ==============================================================
# Step 11: Display the updated data frame
# ==============================================================
Population

# ==============================================================
# Step 12: Use select() to create a new data frame, Change_only,
#          containing just County and Change, then use arrange()
#          to sort it 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: Use filter() to create a new data frame, Doughnut,
#          keeping only rows where Region is "Davidson" or
#          "Doughnut", then use arrange() to sort it 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: Use group_by() and summarize() to create a Summary
#          data frame that totals CurrentPop, EarlierPop, and
#          Change from the Population data frame, grouped 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 Population into a new data frame, Population_v2,
#          use mutate() to add a Percent_change variable
#          (Change divided by EarlierPop), and sort the result
#          by Percent_change in descending order
# ==============================================================
Population_v2 <- Population %>%
  mutate(Percent_change = Change / EarlierPop) %>%
  arrange(desc(Percent_change))

# ==============================================================
# Step 19: Display the Population_v2 data frame
# ==============================================================
Population_v2

# ==============================================================
# Step 20: Create a kableExtra-formatted table for the
#          Population data frame, then display it
# ==============================================================
Population_table <- Population %>%
  kbl(caption = "Population") %>%
  kable_styling()
Population_table

# ==============================================================
# Step 21: Create a kableExtra-formatted table for the
#          Change_only data frame, then display it
# ==============================================================
Change_only_table <- Change_only %>%
  kbl(caption = "Change_only") %>%
  kable_styling()
Change_only_table

# ==============================================================
# Step 22: Create a kableExtra-formatted table for the
#          Doughnut data frame, then display it
# ==============================================================
Doughnut_table <- Doughnut %>%
  kbl(caption = "Doughnut") %>%
  kable_styling()
Doughnut_table

# ==============================================================
# Step 23: Create a kableExtra-formatted table for the
#          Summary data frame, then display it
# ==============================================================
Summary_table <- Summary %>%
  kbl(caption = "Summary") %>%
  kable_styling()
Summary_table

# ==============================================================
# Step 24: Create a kableExtra-formatted table for the
#          Population_v2 data frame, then display it
# ==============================================================
Population_v2_table <- Population_v2 %>%
  kbl(caption = "Population_v2") %>%
  kable_styling()
Population_v2_table

# ==============================================================
# Step 25: Download the Tennessee county gazetteer file 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 26: Create the Land_Area data frame, keeping county name
#          and land area in square miles; remove " County" from
#          county names and 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 27: Display the completed Land_Area data frame
# ==============================================================
Land_Area

# ==============================================================
# Step 28: Use left_join() to add Square_Miles from Land_Area
#          into Population_v2, matching on County and keeping
#          all rows from Population_v2
# ==============================================================
library(dplyr)

Population_v2 <- left_join(Population_v2, Land_Area, by = "County")

# ==============================================================
# Step 29: Use mutate() to add a Density variable (CurrentPop
#          divided by Square_Miles), then sort Population_v2 by
#          Density in descending order
# ==============================================================
library(dplyr)

Population_v2 <- Population_v2 %>%
  mutate(Density = CurrentPop / Square_Miles) %>%
  arrange(desc(Density))

# ==============================================================
# Step 30: Use case_when() to add a Density_Category variable,
#          classifying each county as "High Density" (Density
#          >= 500), "Medium Density" (100 <= Density <= 499), or
#          "Low Density" (Density < 100)
# ==============================================================
library(dplyr)

Population_v2 <- Population_v2 %>%
  mutate(Density_Category = case_when(
    Density >= 500 ~ "High Density",
    Density >= 100 & Density <= 499 ~ "Medium Density",
    Density < 100 ~ "Low Density"
  ))

# ==============================================================
# Step 31: Save Population_v2 to a CSV file, then read it back
#          in from an RDS file
# ==============================================================
write_csv(Population_v2, "Population_v2.csv")
Population_v2 <- readRDS("Population_v2.RDS")