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")