---
title: "Case Study III: Retail Performance (KY, OH, PA, 2021–2023)"
author: "Group Members: <ADD NAMES HERE>"
date: "September 16, 2025"
output:
pdf_document:
number_sections: true
toc: true
toc_depth: 2
html_document:
df_print: paged
toc: true
toc_depth: 2
---
library(tidyverse)
library(janitor)
library(scales)
library(gt)
library(glue)
0) Reproducible Data Ingest & Cleaning
# ---- Configuration ----
states_of_interest <- c("KY", "OH", "PA")
years_of_interest <- 2021:2023
# If you have a single combined CSV, set its filename here; leave NULL to auto-read many files.
combined_file <- NULL
# ---- Helper ----
read_one <- function(path) readr::read_csv(path, show_col_types = FALSE) |> clean_names()
# ---- Read data ----
if (!is.null(combined_file) && file.exists(combined_file)) {
raw <- read_one(combined_file)
} else {
files <- list.files(pattern = "^(KY|OH|PA)_20.*\\.csv$", full.names = TRUE)
if (length(files) == 0L) stop("No input files found. Place KY/OH/PA CSVs (e.g., KY_2021.csv) in the working directory.")
raw <- purrr::map_dfr(files, read_one)
}
# Expected columns (case-insensitive handled by clean_names)
needed <- c("store_id","state","year","sales","customer_count","employees",
"store_area_sqft","profit_margin","store_type")
stopifnot(all(needed %in% names(raw)))
dat0 <- raw |>
mutate(state = toupper(state), year = as.integer(year)) |>
filter(state %in% states_of_interest, year %in% years_of_interest)
# Derived metrics (store-year)
dat <- dat0 |>
mutate(
sales_per_employee = if_else(employees > 0, sales / employees, NA_real_),
sales_per_sqft = if_else(store_area_sqft > 0, sales / store_area_sqft, NA_real_),
sales_per_customer = if_else(customer_count > 0, sales / customer_count, NA_real_)
)
# Quick snapshot
dat |> slice_head(n = 10) |> gt()
store_id |
state |
year |
sales |
customer_count |
employees |
store_area_sqft |
profit_margin |
store_type |
sales_per_employee |
sales_per_sqft |
sales_per_customer |
1 |
KY |
2021 |
60127 |
2064 |
39 |
1788 |
0.18184457 |
Urban |
1541.718 |
33.628076 |
29.13130 |
2 |
KY |
2021 |
63097 |
3526 |
45 |
2338 |
0.19847073 |
Urban |
1402.156 |
26.987596 |
17.89478 |
3 |
KY |
2021 |
192359 |
1791 |
41 |
5591 |
0.19066358 |
Rural |
4691.683 |
34.405115 |
107.40313 |
4 |
KY |
2021 |
153471 |
1239 |
26 |
5499 |
0.06346242 |
Suburban |
5902.731 |
27.908893 |
123.86683 |
5 |
KY |
2021 |
151949 |
4984 |
24 |
3459 |
0.24606024 |
Suburban |
6331.208 |
43.928592 |
30.48736 |
6 |
KY |
2021 |
193611 |
3492 |
8 |
1737 |
0.15368608 |
Urban |
24201.375 |
111.462867 |
55.44416 |
7 |
KY |
2021 |
98086 |
2025 |
14 |
1773 |
0.24574189 |
Suburban |
7006.143 |
55.322053 |
48.43753 |
8 |
KY |
2021 |
184969 |
1744 |
7 |
8492 |
0.12997104 |
Rural |
26424.143 |
21.781559 |
106.06021 |
9 |
KY |
2021 |
52043 |
1315 |
35 |
8152 |
0.20485211 |
Suburban |
1486.943 |
6.384078 |
39.57643 |
10 |
KY |
2021 |
130429 |
3930 |
45 |
9042 |
0.13154801 |
Suburban |
2898.422 |
14.424795 |
33.18804 |
1) Sales & Profit Trends (2021–2023)
w_avg_profit <- function(margin, weight) {
if (all(is.na(margin)) || all(is.na(weight)) || sum(weight, na.rm = TRUE) == 0) return(NA_real_)
sum(margin * weight, na.rm = TRUE) / sum(weight, na.rm = TRUE)
}
state_year <- dat |>
group_by(state, year) |>
summarize(
total_sales = sum(sales, na.rm = TRUE),
avg_profit_margin_w = w_avg_profit(profit_margin, sales),
total_area = sum(store_area_sqft, na.rm = TRUE),
sales_per_sqft_state = if_else(total_area > 0, total_sales / total_area, NA_real_),
.groups = "drop"
)
# Growth metrics: slope (trend) & CAGR (2021->2023)
state_year_wide <- state_year |>
select(state, year, total_sales) |>
pivot_wider(names_from = year, values_from = total_sales, names_prefix = "y")
growth_tbl <- state_year |>
group_by(state) |>
summarize(
sales_slope = coef(lm(total_sales ~ year))[2],
pm_slope = coef(lm(avg_profit_margin_w ~ year))[2],
.groups = "drop"
) |>
left_join(
state_year_wide |>
transmute(state, cagr = if_else(!is.na(y2021) & !is.na(y2023) & y2021 > 0,
(y2023 / y2021)^(1/2) - 1, NA_real_)),
by = "state"
) |>
arrange(desc(cagr))
growth_tbl |>
mutate(cagr_pct = percent(cagr, accuracy = 0.1), sales_slope = scales::comma(sales_slope)) |>
select(state, cagr_pct, sales_slope, pm_slope) |>
gt() |>
tab_header(title = "Sales Growth & Profit-Margin Trend by State (2021–2023)") |>
cols_label(
state = "State",
cagr_pct = "CAGR (2021→2023)",
sales_slope = "Sales Slope (per year)",
pm_slope = "Profit-Margin Slope (per year)"
)
Sales Growth & Profit-Margin Trend by State (2021–2023) |
State |
CAGR (2021→2023) |
Sales Slope (per year) |
Profit-Margin Slope (per year) |
OH |
12.3% |
164,891 |
0.008507483 |
PA |
10.1% |
123,691 |
0.010523623 |
KY |
-4.2% |
-52,369 |
0.020676987 |
p_sales <- state_year |>
ggplot(aes(x = year, y = total_sales, color = state)) +
geom_line(linewidth = 1) +
geom_point(size = 2) +
scale_y_continuous(labels = dollar) +
scale_x_continuous(breaks = sort(unique(state_year$year))) +
labs(title = "Total Annual Sales by State", x = NULL, y = "Total Sales") +
theme_minimal(base_size = 12) +
theme(legend.position = "top")
p_pm <- state_year |>
ggplot(aes(x = year, y = avg_profit_margin_w, color = state)) +
geom_line(linewidth = 1) +
geom_point(size = 2) +
scale_y_continuous(labels = percent_format(accuracy = 0.1), limits = c(0, NA)) +
scale_x_continuous(breaks = sort(unique(state_year$year))) +
labs(title = "Sales-Weighted Avg. Profit Margin by State", x = NULL, y = "Profit Margin") +
theme_minimal(base_size = 12) +
theme(legend.position = "none")
p_sales

p_pm

best_cagr <- growth_tbl |> slice_max(cagr, n = 1, with_ties = FALSE) |> pull(state)
best_slope <- growth_tbl |> slice_max(sales_slope, n = 1, with_ties = FALSE) |> pull(state)
pm_moves <- state_year |>
group_by(state) |>
summarize(
pm_2021 = avg_profit_margin_w[year == 2021],
pm_2023 = avg_profit_margin_w[year == 2023],
moved_same_dir_as_sales =
sign(pm_2023 - pm_2021) == sign(total_sales[year == 2023] - total_sales[year == 2021]),
.groups = "drop"
) |>
transmute(state, direction = if_else(moved_same_dir_as_sales, "same", "different"))
cat(glue("
Key findings (auto-generated):
- Strongest sales growth by CAGR: {best_cagr}.
- Steepest linear sales slope: {best_slope}.
- Profit margins moved in the same direction as sales for: {toString(pm_moves$state[pm_moves$direction=='same'])}.
- Profit margins moved in a different direction for: {toString(pm_moves$state[pm_moves$direction=='different'])}.
"))
## Key findings (auto-generated):
## - Strongest sales growth by CAGR: OH.
## - Steepest linear sales slope: OH.
## - Profit margins moved in the same direction as sales for: OH, PA.
## - Profit margins moved in a different direction for: KY.
2) Customer Traffic vs. Revenue Conversion
# Regression-based conversion: slope of Sales ~ Customers by state-year
lm_slopes <- dat |>
nest_by(state, year) |>
mutate(
model = list(lm(sales ~ customer_count, data = data)),
conversion_slope = as.numeric(coef(model)[["customer_count"]])
) |>
ungroup() |>
select(state, year, conversion_slope)
# Median Sales/Customer as a robust conversion proxy
conv_state_year <- dat |>
group_by(state, year) |>
summarize(median_sales_per_customer = median(sales_per_customer, na.rm = TRUE), .groups = "drop") |>
left_join(lm_slopes, by = c("state","year"))
# High vs Low conversion by comparing each state's median (across years) to global median
conv_class <- conv_state_year |>
group_by(state) |>
summarize(
median_conv = median(median_sales_per_customer, na.rm = TRUE),
mean_slope = mean(conversion_slope, na.rm = TRUE),
.groups = "drop"
)
global_median <- median(conv_state_year$median_sales_per_customer, na.rm = TRUE)
conv_class <- conv_class |>
mutate(conversion_class = if_else(median_conv >= global_median, "High-conversion", "Low-conversion")) |>
arrange(desc(median_conv))
conv_class |>
mutate(median_conv = dollar(median_conv), mean_slope = dollar(mean_slope)) |>
gt() |>
tab_header(title = "Conversion by State (Median Sales/Customer & OLS Slope)") |>
cols_label(
state = "State", median_conv = "Median Sales per Customer",
mean_slope = "OLS Slope (Sales ~ Customers)", conversion_class = "Class"
)
Conversion by State (Median Sales/Customer & OLS Slope) |
State |
Median Sales per Customer |
OLS Slope (Sales ~ Customers) |
Class |
OH |
$49.42 |
$5.56 |
High-conversion |
KY |
$41.16 |
$5.21 |
High-conversion |
PA |
$34.39 |
-$11.79 |
Low-conversion |
dat |>
ggplot(aes(x = customer_count, y = sales)) +
geom_point(alpha = 0.5) +
geom_smooth(method = "lm", se = FALSE, linewidth = 1) +
scale_y_continuous(labels = dollar) +
labs(title = "Store-Level: Customers vs Sales", x = "Customer Count", y = "Sales") +
facet_wrap(~ state, scales = "free") +
theme_minimal(base_size = 12)

3) Store Efficiency: Sales/Employee & Sales/SqFt
store_year <- dat |>
select(store_id, state, year, store_type, sales, profit_margin,
sales_per_employee, sales_per_sqft)
# Rank within each year
ranked <- store_year |>
group_by(year) |>
mutate(
rank_spe = dense_rank(desc(sales_per_employee)),
rank_spsf = dense_rank(desc(sales_per_sqft))
) |>
ungroup()
top_spe <- ranked |>
group_by(year) |>
slice_min(rank_spe, n = 10, with_ties = FALSE) |>
ungroup() |>
select(year, store_id, state, store_type, sales_per_employee, rank_spe)
top_spsf <- ranked |>
group_by(year) |>
slice_min(rank_spsf, n = 10, with_ties = FALSE) |>
ungroup() |>
select(year, store_id, state, store_type, sales_per_sqft, rank_spsf)
# Consistency (>=2/3 years)
consistent_top_spe <- top_spe |> count(store_id, sort = TRUE) |> filter(n >= 2) |>
left_join(distinct(store_year, store_id, state, store_type), by = "store_id")
consistent_top_spsf <- top_spsf |> count(store_id, sort = TRUE) |> filter(n >= 2) |>
left_join(distinct(store_year, store_id, state, store_type), by = "store_id")
# Underperformers: bottom quartile on BOTH Sales/SqFt and Profit Margin (>=2 years)
thresholds <- store_year |>
group_by(year) |>
summarize(
q25_spsf = quantile(sales_per_sqft, 0.25, na.rm = TRUE),
q25_pm = quantile(profit_margin, 0.25, na.rm = TRUE),
.groups = "drop"
)
underperf <- store_year |>
left_join(thresholds, by = "year") |>
mutate(
low_spsf = sales_per_sqft <= q25_spsf,
low_pm = profit_margin <= q25_pm,
under_both = low_spsf & low_pm
) |>
filter(under_both) |>
count(store_id, sort = TRUE, name = "underperf_years") |>
filter(underperf_years >= 2) |>
left_join(distinct(store_year, store_id, state, store_type), by = "store_id")
list(
"Top-10 Sales/Employee (by year)" = top_spe,
"Top-10 Sales/SqFt (by year)" = top_spsf,
"Consistent Top (>=2 yrs) — Sales/Employee" = consistent_top_spe,
"Consistent Top (>=2 yrs) — Sales/SqFt" = consistent_top_spsf,
"Consistently Underperform (>=2 yrs) on PM & Sales/SqFt" = underperf
)
## $`Top-10 Sales/Employee (by year)`
## # A tibble: 30 × 6
## year store_id state store_type sales_per_employee rank_spe
## <int> <dbl> <chr> <chr> <dbl> <int>
## 1 2021 8 KY Rural 26424. 1
## 2 2021 8 OH Rural 26236. 2
## 3 2021 6 KY Urban 24201. 3
## 4 2021 6 OH Urban 24134. 4
## 5 2021 7 PA Rural 17818. 5
## 6 2021 2 PA Rural 17326 6
## 7 2021 7 KY Suburban 7006. 7
## 8 2021 7 OH Suburban 6970. 8
## 9 2021 1 PA Suburban 6605. 9
## 10 2021 5 KY Suburban 6331. 10
## # ℹ 20 more rows
##
## $`Top-10 Sales/SqFt (by year)`
## # A tibble: 30 × 6
## year store_id state store_type sales_per_sqft rank_spsf
## <int> <dbl> <chr> <chr> <dbl> <int>
## 1 2021 6 KY Urban 111. 1
## 2 2021 6 OH Urban 111. 2
## 3 2021 1 PA Suburban 84.6 3
## 4 2021 7 KY Suburban 55.3 4
## 5 2021 7 OH Suburban 55.0 5
## 6 2021 9 PA Urban 46.6 6
## 7 2021 5 KY Suburban 43.9 7
## 8 2021 5 OH Suburban 43.9 8
## 9 2021 3 KY Rural 34.4 9
## 10 2021 3 OH Rural 34.3 10
## # ℹ 20 more rows
##
## $`Consistent Top (>=2 yrs) — Sales/Employee`
## # A tibble: 18 × 4
## store_id n state store_type
## <dbl> <int> <chr> <chr>
## 1 7 8 KY Suburban
## 2 7 8 OH Suburban
## 3 7 8 PA Rural
## 4 6 6 KY Urban
## 5 6 6 OH Urban
## 6 6 6 PA Urban
## 7 8 6 KY Rural
## 8 8 6 OH Rural
## 9 8 6 PA Urban
## 10 1 3 KY Urban
## 11 1 3 OH Urban
## 12 1 3 PA Suburban
## 13 2 3 KY Urban
## 14 2 3 OH Urban
## 15 2 3 PA Rural
## 16 5 3 KY Suburban
## 17 5 3 OH Suburban
## 18 5 3 PA Urban
##
## $`Consistent Top (>=2 yrs) — Sales/SqFt`
## # A tibble: 18 × 4
## store_id n state store_type
## <dbl> <int> <chr> <chr>
## 1 5 6 KY Suburban
## 2 5 6 OH Suburban
## 3 5 6 PA Urban
## 4 6 6 KY Urban
## 5 6 6 OH Urban
## 6 6 6 PA Urban
## 7 7 6 KY Suburban
## 8 7 6 OH Suburban
## 9 7 6 PA Rural
## 10 1 5 KY Urban
## 11 1 5 OH Urban
## 12 1 5 PA Suburban
## 13 3 4 KY Rural
## 14 3 4 OH Rural
## 15 3 4 PA Urban
## 16 9 3 KY Suburban
## 17 9 3 OH Suburban
## 18 9 3 PA Urban
##
## $`Consistently Underperform (>=2 yrs) on PM & Sales/SqFt`
## # A tibble: 3 × 4
## store_id underperf_years state store_type
## <dbl> <int> <chr> <chr>
## 1 10 6 KY Suburban
## 2 10 6 OH Suburban
## 3 10 6 PA Rural
store_year |>
pivot_longer(c(sales_per_employee, sales_per_sqft), names_to = "metric", values_to = "value") |>
ggplot(aes(x = value, fill = metric)) +
geom_histogram(bins = 30, alpha = 0.6, position = "identity") +
scale_x_continuous(labels = dollar) +
labs(title = "Store Efficiency Distributions", x = "Value", y = "Count") +
facet_wrap(~ metric, scales = "free") +
theme_minimal(base_size = 12) +
theme(legend.position = "none")

5) State Rankings Over Time
state_rank <- dat |>
group_by(state, year) |>
summarize(
total_sales = sum(sales, na.rm = TRUE),
avg_profit_margin_w = w_avg_profit(profit_margin, sales),
total_area = sum(store_area_sqft, na.rm = TRUE),
sales_per_sqft_state = if_else(total_area > 0, total_sales / total_area, NA_real_),
.groups = "drop"
) |>
group_by(year) |>
mutate(
rk_sales = dense_rank(desc(total_sales)),
rk_pm = dense_rank(desc(avg_profit_margin_w)),
rk_spsf = dense_rank(desc(sales_per_sqft_state))
) |>
ungroup()
# One GT per year
split(state_rank, state_rank$year) |>
purrr::imap(~ {
.x |>
arrange(rk_sales) |>
mutate(
total_sales = dollar(total_sales),
avg_profit_margin_w = percent(avg_profit_margin_w, accuracy = 0.1),
sales_per_sqft_state = dollar(sales_per_sqft_state)
) |>
select(state, rk_sales, total_sales, rk_pm, avg_profit_margin_w, rk_spsf, sales_per_sqft_state) |>
gt() |>
tab_header(title = md(glue("State Rankings — {.y}"))) |>
cols_label(
state = "State",
rk_sales = "Rank: Total Sales",
total_sales = "Total Sales",
rk_pm = "Rank: Avg Profit Margin",
avg_profit_margin_w = "Avg Profit Margin",
rk_spsf = "Rank: Sales/SqFt",
sales_per_sqft_state = "Sales/SqFt"
)
})
## $`2021`
## <div id="foufhbxvgn" style="padding-left:0px;padding-right:0px;padding-top:10px;padding-bottom:10px;overflow-x:auto;overflow-y:auto;width:auto;height:auto;">
## <style>#foufhbxvgn table {
## font-family: system-ui, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
## -webkit-font-smoothing: antialiased;
## -moz-osx-font-smoothing: grayscale;
## }
##
## #foufhbxvgn thead, #foufhbxvgn tbody, #foufhbxvgn tfoot, #foufhbxvgn tr, #foufhbxvgn td, #foufhbxvgn th {
## border-style: none;
## }
##
## #foufhbxvgn p {
## margin: 0;
## padding: 0;
## }
##
## #foufhbxvgn .gt_table {
## display: table;
## border-collapse: collapse;
## line-height: normal;
## margin-left: auto;
## margin-right: auto;
## color: #333333;
## font-size: 16px;
## font-weight: normal;
## font-style: normal;
## background-color: #FFFFFF;
## width: auto;
## border-top-style: solid;
## border-top-width: 2px;
## border-top-color: #A8A8A8;
## border-right-style: none;
## border-right-width: 2px;
## border-right-color: #D3D3D3;
## border-bottom-style: solid;
## border-bottom-width: 2px;
## border-bottom-color: #A8A8A8;
## border-left-style: none;
## border-left-width: 2px;
## border-left-color: #D3D3D3;
## }
##
## #foufhbxvgn .gt_caption {
## padding-top: 4px;
## padding-bottom: 4px;
## }
##
## #foufhbxvgn .gt_title {
## color: #333333;
## font-size: 125%;
## font-weight: initial;
## padding-top: 4px;
## padding-bottom: 4px;
## padding-left: 5px;
## padding-right: 5px;
## border-bottom-color: #FFFFFF;
## border-bottom-width: 0;
## }
##
## #foufhbxvgn .gt_subtitle {
## color: #333333;
## font-size: 85%;
## font-weight: initial;
## padding-top: 3px;
## padding-bottom: 5px;
## padding-left: 5px;
## padding-right: 5px;
## border-top-color: #FFFFFF;
## border-top-width: 0;
## }
##
## #foufhbxvgn .gt_heading {
## background-color: #FFFFFF;
## text-align: center;
## border-bottom-color: #FFFFFF;
## border-left-style: none;
## border-left-width: 1px;
## border-left-color: #D3D3D3;
## border-right-style: none;
## border-right-width: 1px;
## border-right-color: #D3D3D3;
## }
##
## #foufhbxvgn .gt_bottom_border {
## border-bottom-style: solid;
## border-bottom-width: 2px;
## border-bottom-color: #D3D3D3;
## }
##
## #foufhbxvgn .gt_col_headings {
## border-top-style: solid;
## border-top-width: 2px;
## border-top-color: #D3D3D3;
## border-bottom-style: solid;
## border-bottom-width: 2px;
## border-bottom-color: #D3D3D3;
## border-left-style: none;
## border-left-width: 1px;
## border-left-color: #D3D3D3;
## border-right-style: none;
## border-right-width: 1px;
## border-right-color: #D3D3D3;
## }
##
## #foufhbxvgn .gt_col_heading {
## color: #333333;
## background-color: #FFFFFF;
## font-size: 100%;
## font-weight: normal;
## text-transform: inherit;
## border-left-style: none;
## border-left-width: 1px;
## border-left-color: #D3D3D3;
## border-right-style: none;
## border-right-width: 1px;
## border-right-color: #D3D3D3;
## vertical-align: bottom;
## padding-top: 5px;
## padding-bottom: 6px;
## padding-left: 5px;
## padding-right: 5px;
## overflow-x: hidden;
## }
##
## #foufhbxvgn .gt_column_spanner_outer {
## color: #333333;
## background-color: #FFFFFF;
## font-size: 100%;
## font-weight: normal;
## text-transform: inherit;
## padding-top: 0;
## padding-bottom: 0;
## padding-left: 4px;
## padding-right: 4px;
## }
##
## #foufhbxvgn .gt_column_spanner_outer:first-child {
## padding-left: 0;
## }
##
## #foufhbxvgn .gt_column_spanner_outer:last-child {
## padding-right: 0;
## }
##
## #foufhbxvgn .gt_column_spanner {
## border-bottom-style: solid;
## border-bottom-width: 2px;
## border-bottom-color: #D3D3D3;
## vertical-align: bottom;
## padding-top: 5px;
## padding-bottom: 5px;
## overflow-x: hidden;
## display: inline-block;
## width: 100%;
## }
##
## #foufhbxvgn .gt_spanner_row {
## border-bottom-style: hidden;
## }
##
## #foufhbxvgn .gt_group_heading {
## padding-top: 8px;
## padding-bottom: 8px;
## padding-left: 5px;
## padding-right: 5px;
## color: #333333;
## background-color: #FFFFFF;
## font-size: 100%;
## font-weight: initial;
## text-transform: inherit;
## border-top-style: solid;
## border-top-width: 2px;
## border-top-color: #D3D3D3;
## border-bottom-style: solid;
## border-bottom-width: 2px;
## border-bottom-color: #D3D3D3;
## border-left-style: none;
## border-left-width: 1px;
## border-left-color: #D3D3D3;
## border-right-style: none;
## border-right-width: 1px;
## border-right-color: #D3D3D3;
## vertical-align: middle;
## text-align: left;
## }
##
## #foufhbxvgn .gt_empty_group_heading {
## padding: 0.5px;
## color: #333333;
## background-color: #FFFFFF;
## font-size: 100%;
## font-weight: initial;
## border-top-style: solid;
## border-top-width: 2px;
## border-top-color: #D3D3D3;
## border-bottom-style: solid;
## border-bottom-width: 2px;
## border-bottom-color: #D3D3D3;
## vertical-align: middle;
## }
##
## #foufhbxvgn .gt_from_md > :first-child {
## margin-top: 0;
## }
##
## #foufhbxvgn .gt_from_md > :last-child {
## margin-bottom: 0;
## }
##
## #foufhbxvgn .gt_row {
## padding-top: 8px;
## padding-bottom: 8px;
## padding-left: 5px;
## padding-right: 5px;
## margin: 10px;
## border-top-style: solid;
## border-top-width: 1px;
## border-top-color: #D3D3D3;
## border-left-style: none;
## border-left-width: 1px;
## border-left-color: #D3D3D3;
## border-right-style: none;
## border-right-width: 1px;
## border-right-color: #D3D3D3;
## vertical-align: middle;
## overflow-x: hidden;
## }
##
## #foufhbxvgn .gt_stub {
## color: #333333;
## background-color: #FFFFFF;
## font-size: 100%;
## font-weight: initial;
## text-transform: inherit;
## border-right-style: solid;
## border-right-width: 2px;
## border-right-color: #D3D3D3;
## padding-left: 5px;
## padding-right: 5px;
## }
##
## #foufhbxvgn .gt_stub_row_group {
## color: #333333;
## background-color: #FFFFFF;
## font-size: 100%;
## font-weight: initial;
## text-transform: inherit;
## border-right-style: solid;
## border-right-width: 2px;
## border-right-color: #D3D3D3;
## padding-left: 5px;
## padding-right: 5px;
## vertical-align: top;
## }
##
## #foufhbxvgn .gt_row_group_first td {
## border-top-width: 2px;
## }
##
## #foufhbxvgn .gt_row_group_first th {
## border-top-width: 2px;
## }
##
## #foufhbxvgn .gt_summary_row {
## color: #333333;
## background-color: #FFFFFF;
## text-transform: inherit;
## padding-top: 8px;
## padding-bottom: 8px;
## padding-left: 5px;
## padding-right: 5px;
## }
##
## #foufhbxvgn .gt_first_summary_row {
## border-top-style: solid;
## border-top-color: #D3D3D3;
## }
##
## #foufhbxvgn .gt_first_summary_row.thick {
## border-top-width: 2px;
## }
##
## #foufhbxvgn .gt_last_summary_row {
## padding-top: 8px;
## padding-bottom: 8px;
## padding-left: 5px;
## padding-right: 5px;
## border-bottom-style: solid;
## border-bottom-width: 2px;
## border-bottom-color: #D3D3D3;
## }
##
## #foufhbxvgn .gt_grand_summary_row {
## color: #333333;
## background-color: #FFFFFF;
## text-transform: inherit;
## padding-top: 8px;
## padding-bottom: 8px;
## padding-left: 5px;
## padding-right: 5px;
## }
##
## #foufhbxvgn .gt_first_grand_summary_row {
## padding-top: 8px;
## padding-bottom: 8px;
## padding-left: 5px;
## padding-right: 5px;
## border-top-style: double;
## border-top-width: 6px;
## border-top-color: #D3D3D3;
## }
##
## #foufhbxvgn .gt_last_grand_summary_row_top {
## padding-top: 8px;
## padding-bottom: 8px;
## padding-left: 5px;
## padding-right: 5px;
## border-bottom-style: double;
## border-bottom-width: 6px;
## border-bottom-color: #D3D3D3;
## }
##
## #foufhbxvgn .gt_striped {
## background-color: rgba(128, 128, 128, 0.05);
## }
##
## #foufhbxvgn .gt_table_body {
## border-top-style: solid;
## border-top-width: 2px;
## border-top-color: #D3D3D3;
## border-bottom-style: solid;
## border-bottom-width: 2px;
## border-bottom-color: #D3D3D3;
## }
##
## #foufhbxvgn .gt_footnotes {
## color: #333333;
## background-color: #FFFFFF;
## border-bottom-style: none;
## border-bottom-width: 2px;
## border-bottom-color: #D3D3D3;
## border-left-style: none;
## border-left-width: 2px;
## border-left-color: #D3D3D3;
## border-right-style: none;
## border-right-width: 2px;
## border-right-color: #D3D3D3;
## }
##
## #foufhbxvgn .gt_footnote {
## margin: 0px;
## font-size: 90%;
## padding-top: 4px;
## padding-bottom: 4px;
## padding-left: 5px;
## padding-right: 5px;
## }
##
## #foufhbxvgn .gt_sourcenotes {
## color: #333333;
## background-color: #FFFFFF;
## border-bottom-style: none;
## border-bottom-width: 2px;
## border-bottom-color: #D3D3D3;
## border-left-style: none;
## border-left-width: 2px;
## border-left-color: #D3D3D3;
## border-right-style: none;
## border-right-width: 2px;
## border-right-color: #D3D3D3;
## }
##
## #foufhbxvgn .gt_sourcenote {
## font-size: 90%;
## padding-top: 4px;
## padding-bottom: 4px;
## padding-left: 5px;
## padding-right: 5px;
## }
##
## #foufhbxvgn .gt_left {
## text-align: left;
## }
##
## #foufhbxvgn .gt_center {
## text-align: center;
## }
##
## #foufhbxvgn .gt_right {
## text-align: right;
## font-variant-numeric: tabular-nums;
## }
##
## #foufhbxvgn .gt_font_normal {
## font-weight: normal;
## }
##
## #foufhbxvgn .gt_font_bold {
## font-weight: bold;
## }
##
## #foufhbxvgn .gt_font_italic {
## font-style: italic;
## }
##
## #foufhbxvgn .gt_super {
## font-size: 65%;
## }
##
## #foufhbxvgn .gt_footnote_marks {
## font-size: 75%;
## vertical-align: 0.4em;
## position: initial;
## }
##
## #foufhbxvgn .gt_asterisk {
## font-size: 100%;
## vertical-align: 0;
## }
##
## #foufhbxvgn .gt_indent_1 {
## text-indent: 5px;
## }
##
## #foufhbxvgn .gt_indent_2 {
## text-indent: 10px;
## }
##
## #foufhbxvgn .gt_indent_3 {
## text-indent: 15px;
## }
##
## #foufhbxvgn .gt_indent_4 {
## text-indent: 20px;
## }
##
## #foufhbxvgn .gt_indent_5 {
## text-indent: 25px;
## }
##
## #foufhbxvgn .katex-display {
## display: inline-flex !important;
## margin-bottom: 0.75em !important;
## }
##
## #foufhbxvgn div.Reactable > div.rt-table > div.rt-thead > div.rt-tr.rt-tr-group-header > div.rt-th-group:after {
## height: 0px !important;
## }
## </style>
## <table class="gt_table" data-quarto-disable-processing="false" data-quarto-bootstrap="false">
## <thead>
## <tr class="gt_heading">
## <td colspan="7" class="gt_heading gt_title gt_font_normal gt_bottom_border" style><span class='gt_from_md'>State Rankings — 2021</span></td>
## </tr>
##
## <tr class="gt_col_headings">
## <th class="gt_col_heading gt_columns_bottom_border gt_left" rowspan="1" colspan="1" scope="col" id="state">State</th>
## <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" scope="col" id="rk_sales">Rank: Total Sales</th>
## <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" scope="col" id="total_sales">Total Sales</th>
## <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" scope="col" id="rk_pm">Rank: Avg Profit Margin</th>
## <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" scope="col" id="avg_profit_margin_w">Avg Profit Margin</th>
## <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" scope="col" id="rk_spsf">Rank: Sales/SqFt</th>
## <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" scope="col" id="sales_per_sqft_state">Sales/SqFt</th>
## </tr>
## </thead>
## <tbody class="gt_table_body">
## <tr><td headers="state" class="gt_row gt_left">KY</td>
## <td headers="rk_sales" class="gt_row gt_right">1</td>
## <td headers="total_sales" class="gt_row gt_right">$1,280,141</td>
## <td headers="rk_pm" class="gt_row gt_right">2</td>
## <td headers="avg_profit_margin_w" class="gt_row gt_right">16.6%</td>
## <td headers="rk_spsf" class="gt_row gt_right">1</td>
## <td headers="sales_per_sqft_state" class="gt_row gt_right">$26.74</td></tr>
## <tr><td headers="state" class="gt_row gt_left">OH</td>
## <td headers="rk_sales" class="gt_row gt_right">2</td>
## <td headers="total_sales" class="gt_row gt_right">$1,266,415</td>
## <td headers="rk_pm" class="gt_row gt_right">3</td>
## <td headers="avg_profit_margin_w" class="gt_row gt_right">16.2%</td>
## <td headers="rk_spsf" class="gt_row gt_right">2</td>
## <td headers="sales_per_sqft_state" class="gt_row gt_right">$26.45</td></tr>
## <tr><td headers="state" class="gt_row gt_left">PA</td>
## <td headers="rk_sales" class="gt_row gt_right">3</td>
## <td headers="total_sales" class="gt_row gt_right">$1,168,255</td>
## <td headers="rk_pm" class="gt_row gt_right">1</td>
## <td headers="avg_profit_margin_w" class="gt_row gt_right">16.9%</td>
## <td headers="rk_spsf" class="gt_row gt_right">3</td>
## <td headers="sales_per_sqft_state" class="gt_row gt_right">$23.90</td></tr>
## </tbody>
##
##
## </table>
## </div>
##
## $`2022`
## <div id="nsgwsbmzlq" style="padding-left:0px;padding-right:0px;padding-top:10px;padding-bottom:10px;overflow-x:auto;overflow-y:auto;width:auto;height:auto;">
## <style>#nsgwsbmzlq table {
## font-family: system-ui, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
## -webkit-font-smoothing: antialiased;
## -moz-osx-font-smoothing: grayscale;
## }
##
## #nsgwsbmzlq thead, #nsgwsbmzlq tbody, #nsgwsbmzlq tfoot, #nsgwsbmzlq tr, #nsgwsbmzlq td, #nsgwsbmzlq th {
## border-style: none;
## }
##
## #nsgwsbmzlq p {
## margin: 0;
## padding: 0;
## }
##
## #nsgwsbmzlq .gt_table {
## display: table;
## border-collapse: collapse;
## line-height: normal;
## margin-left: auto;
## margin-right: auto;
## color: #333333;
## font-size: 16px;
## font-weight: normal;
## font-style: normal;
## background-color: #FFFFFF;
## width: auto;
## border-top-style: solid;
## border-top-width: 2px;
## border-top-color: #A8A8A8;
## border-right-style: none;
## border-right-width: 2px;
## border-right-color: #D3D3D3;
## border-bottom-style: solid;
## border-bottom-width: 2px;
## border-bottom-color: #A8A8A8;
## border-left-style: none;
## border-left-width: 2px;
## border-left-color: #D3D3D3;
## }
##
## #nsgwsbmzlq .gt_caption {
## padding-top: 4px;
## padding-bottom: 4px;
## }
##
## #nsgwsbmzlq .gt_title {
## color: #333333;
## font-size: 125%;
## font-weight: initial;
## padding-top: 4px;
## padding-bottom: 4px;
## padding-left: 5px;
## padding-right: 5px;
## border-bottom-color: #FFFFFF;
## border-bottom-width: 0;
## }
##
## #nsgwsbmzlq .gt_subtitle {
## color: #333333;
## font-size: 85%;
## font-weight: initial;
## padding-top: 3px;
## padding-bottom: 5px;
## padding-left: 5px;
## padding-right: 5px;
## border-top-color: #FFFFFF;
## border-top-width: 0;
## }
##
## #nsgwsbmzlq .gt_heading {
## background-color: #FFFFFF;
## text-align: center;
## border-bottom-color: #FFFFFF;
## border-left-style: none;
## border-left-width: 1px;
## border-left-color: #D3D3D3;
## border-right-style: none;
## border-right-width: 1px;
## border-right-color: #D3D3D3;
## }
##
## #nsgwsbmzlq .gt_bottom_border {
## border-bottom-style: solid;
## border-bottom-width: 2px;
## border-bottom-color: #D3D3D3;
## }
##
## #nsgwsbmzlq .gt_col_headings {
## border-top-style: solid;
## border-top-width: 2px;
## border-top-color: #D3D3D3;
## border-bottom-style: solid;
## border-bottom-width: 2px;
## border-bottom-color: #D3D3D3;
## border-left-style: none;
## border-left-width: 1px;
## border-left-color: #D3D3D3;
## border-right-style: none;
## border-right-width: 1px;
## border-right-color: #D3D3D3;
## }
##
## #nsgwsbmzlq .gt_col_heading {
## color: #333333;
## background-color: #FFFFFF;
## font-size: 100%;
## font-weight: normal;
## text-transform: inherit;
## border-left-style: none;
## border-left-width: 1px;
## border-left-color: #D3D3D3;
## border-right-style: none;
## border-right-width: 1px;
## border-right-color: #D3D3D3;
## vertical-align: bottom;
## padding-top: 5px;
## padding-bottom: 6px;
## padding-left: 5px;
## padding-right: 5px;
## overflow-x: hidden;
## }
##
## #nsgwsbmzlq .gt_column_spanner_outer {
## color: #333333;
## background-color: #FFFFFF;
## font-size: 100%;
## font-weight: normal;
## text-transform: inherit;
## padding-top: 0;
## padding-bottom: 0;
## padding-left: 4px;
## padding-right: 4px;
## }
##
## #nsgwsbmzlq .gt_column_spanner_outer:first-child {
## padding-left: 0;
## }
##
## #nsgwsbmzlq .gt_column_spanner_outer:last-child {
## padding-right: 0;
## }
##
## #nsgwsbmzlq .gt_column_spanner {
## border-bottom-style: solid;
## border-bottom-width: 2px;
## border-bottom-color: #D3D3D3;
## vertical-align: bottom;
## padding-top: 5px;
## padding-bottom: 5px;
## overflow-x: hidden;
## display: inline-block;
## width: 100%;
## }
##
## #nsgwsbmzlq .gt_spanner_row {
## border-bottom-style: hidden;
## }
##
## #nsgwsbmzlq .gt_group_heading {
## padding-top: 8px;
## padding-bottom: 8px;
## padding-left: 5px;
## padding-right: 5px;
## color: #333333;
## background-color: #FFFFFF;
## font-size: 100%;
## font-weight: initial;
## text-transform: inherit;
## border-top-style: solid;
## border-top-width: 2px;
## border-top-color: #D3D3D3;
## border-bottom-style: solid;
## border-bottom-width: 2px;
## border-bottom-color: #D3D3D3;
## border-left-style: none;
## border-left-width: 1px;
## border-left-color: #D3D3D3;
## border-right-style: none;
## border-right-width: 1px;
## border-right-color: #D3D3D3;
## vertical-align: middle;
## text-align: left;
## }
##
## #nsgwsbmzlq .gt_empty_group_heading {
## padding: 0.5px;
## color: #333333;
## background-color: #FFFFFF;
## font-size: 100%;
## font-weight: initial;
## border-top-style: solid;
## border-top-width: 2px;
## border-top-color: #D3D3D3;
## border-bottom-style: solid;
## border-bottom-width: 2px;
## border-bottom-color: #D3D3D3;
## vertical-align: middle;
## }
##
## #nsgwsbmzlq .gt_from_md > :first-child {
## margin-top: 0;
## }
##
## #nsgwsbmzlq .gt_from_md > :last-child {
## margin-bottom: 0;
## }
##
## #nsgwsbmzlq .gt_row {
## padding-top: 8px;
## padding-bottom: 8px;
## padding-left: 5px;
## padding-right: 5px;
## margin: 10px;
## border-top-style: solid;
## border-top-width: 1px;
## border-top-color: #D3D3D3;
## border-left-style: none;
## border-left-width: 1px;
## border-left-color: #D3D3D3;
## border-right-style: none;
## border-right-width: 1px;
## border-right-color: #D3D3D3;
## vertical-align: middle;
## overflow-x: hidden;
## }
##
## #nsgwsbmzlq .gt_stub {
## color: #333333;
## background-color: #FFFFFF;
## font-size: 100%;
## font-weight: initial;
## text-transform: inherit;
## border-right-style: solid;
## border-right-width: 2px;
## border-right-color: #D3D3D3;
## padding-left: 5px;
## padding-right: 5px;
## }
##
## #nsgwsbmzlq .gt_stub_row_group {
## color: #333333;
## background-color: #FFFFFF;
## font-size: 100%;
## font-weight: initial;
## text-transform: inherit;
## border-right-style: solid;
## border-right-width: 2px;
## border-right-color: #D3D3D3;
## padding-left: 5px;
## padding-right: 5px;
## vertical-align: top;
## }
##
## #nsgwsbmzlq .gt_row_group_first td {
## border-top-width: 2px;
## }
##
## #nsgwsbmzlq .gt_row_group_first th {
## border-top-width: 2px;
## }
##
## #nsgwsbmzlq .gt_summary_row {
## color: #333333;
## background-color: #FFFFFF;
## text-transform: inherit;
## padding-top: 8px;
## padding-bottom: 8px;
## padding-left: 5px;
## padding-right: 5px;
## }
##
## #nsgwsbmzlq .gt_first_summary_row {
## border-top-style: solid;
## border-top-color: #D3D3D3;
## }
##
## #nsgwsbmzlq .gt_first_summary_row.thick {
## border-top-width: 2px;
## }
##
## #nsgwsbmzlq .gt_last_summary_row {
## padding-top: 8px;
## padding-bottom: 8px;
## padding-left: 5px;
## padding-right: 5px;
## border-bottom-style: solid;
## border-bottom-width: 2px;
## border-bottom-color: #D3D3D3;
## }
##
## #nsgwsbmzlq .gt_grand_summary_row {
## color: #333333;
## background-color: #FFFFFF;
## text-transform: inherit;
## padding-top: 8px;
## padding-bottom: 8px;
## padding-left: 5px;
## padding-right: 5px;
## }
##
## #nsgwsbmzlq .gt_first_grand_summary_row {
## padding-top: 8px;
## padding-bottom: 8px;
## padding-left: 5px;
## padding-right: 5px;
## border-top-style: double;
## border-top-width: 6px;
## border-top-color: #D3D3D3;
## }
##
## #nsgwsbmzlq .gt_last_grand_summary_row_top {
## padding-top: 8px;
## padding-bottom: 8px;
## padding-left: 5px;
## padding-right: 5px;
## border-bottom-style: double;
## border-bottom-width: 6px;
## border-bottom-color: #D3D3D3;
## }
##
## #nsgwsbmzlq .gt_striped {
## background-color: rgba(128, 128, 128, 0.05);
## }
##
## #nsgwsbmzlq .gt_table_body {
## border-top-style: solid;
## border-top-width: 2px;
## border-top-color: #D3D3D3;
## border-bottom-style: solid;
## border-bottom-width: 2px;
## border-bottom-color: #D3D3D3;
## }
##
## #nsgwsbmzlq .gt_footnotes {
## color: #333333;
## background-color: #FFFFFF;
## border-bottom-style: none;
## border-bottom-width: 2px;
## border-bottom-color: #D3D3D3;
## border-left-style: none;
## border-left-width: 2px;
## border-left-color: #D3D3D3;
## border-right-style: none;
## border-right-width: 2px;
## border-right-color: #D3D3D3;
## }
##
## #nsgwsbmzlq .gt_footnote {
## margin: 0px;
## font-size: 90%;
## padding-top: 4px;
## padding-bottom: 4px;
## padding-left: 5px;
## padding-right: 5px;
## }
##
## #nsgwsbmzlq .gt_sourcenotes {
## color: #333333;
## background-color: #FFFFFF;
## border-bottom-style: none;
## border-bottom-width: 2px;
## border-bottom-color: #D3D3D3;
## border-left-style: none;
## border-left-width: 2px;
## border-left-color: #D3D3D3;
## border-right-style: none;
## border-right-width: 2px;
## border-right-color: #D3D3D3;
## }
##
## #nsgwsbmzlq .gt_sourcenote {
## font-size: 90%;
## padding-top: 4px;
## padding-bottom: 4px;
## padding-left: 5px;
## padding-right: 5px;
## }
##
## #nsgwsbmzlq .gt_left {
## text-align: left;
## }
##
## #nsgwsbmzlq .gt_center {
## text-align: center;
## }
##
## #nsgwsbmzlq .gt_right {
## text-align: right;
## font-variant-numeric: tabular-nums;
## }
##
## #nsgwsbmzlq .gt_font_normal {
## font-weight: normal;
## }
##
## #nsgwsbmzlq .gt_font_bold {
## font-weight: bold;
## }
##
## #nsgwsbmzlq .gt_font_italic {
## font-style: italic;
## }
##
## #nsgwsbmzlq .gt_super {
## font-size: 65%;
## }
##
## #nsgwsbmzlq .gt_footnote_marks {
## font-size: 75%;
## vertical-align: 0.4em;
## position: initial;
## }
##
## #nsgwsbmzlq .gt_asterisk {
## font-size: 100%;
## vertical-align: 0;
## }
##
## #nsgwsbmzlq .gt_indent_1 {
## text-indent: 5px;
## }
##
## #nsgwsbmzlq .gt_indent_2 {
## text-indent: 10px;
## }
##
## #nsgwsbmzlq .gt_indent_3 {
## text-indent: 15px;
## }
##
## #nsgwsbmzlq .gt_indent_4 {
## text-indent: 20px;
## }
##
## #nsgwsbmzlq .gt_indent_5 {
## text-indent: 25px;
## }
##
## #nsgwsbmzlq .katex-display {
## display: inline-flex !important;
## margin-bottom: 0.75em !important;
## }
##
## #nsgwsbmzlq div.Reactable > div.rt-table > div.rt-thead > div.rt-tr.rt-tr-group-header > div.rt-th-group:after {
## height: 0px !important;
## }
## </style>
## <table class="gt_table" data-quarto-disable-processing="false" data-quarto-bootstrap="false">
## <thead>
## <tr class="gt_heading">
## <td colspan="7" class="gt_heading gt_title gt_font_normal gt_bottom_border" style><span class='gt_from_md'>State Rankings — 2022</span></td>
## </tr>
##
## <tr class="gt_col_headings">
## <th class="gt_col_heading gt_columns_bottom_border gt_left" rowspan="1" colspan="1" scope="col" id="state">State</th>
## <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" scope="col" id="rk_sales">Rank: Total Sales</th>
## <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" scope="col" id="total_sales">Total Sales</th>
## <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" scope="col" id="rk_pm">Rank: Avg Profit Margin</th>
## <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" scope="col" id="avg_profit_margin_w">Avg Profit Margin</th>
## <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" scope="col" id="rk_spsf">Rank: Sales/SqFt</th>
## <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" scope="col" id="sales_per_sqft_state">Sales/SqFt</th>
## </tr>
## </thead>
## <tbody class="gt_table_body">
## <tr><td headers="state" class="gt_row gt_left">OH</td>
## <td headers="rk_sales" class="gt_row gt_right">1</td>
## <td headers="total_sales" class="gt_row gt_right">$1,416,733</td>
## <td headers="rk_pm" class="gt_row gt_right">3</td>
## <td headers="avg_profit_margin_w" class="gt_row gt_right">17.1%</td>
## <td headers="rk_spsf" class="gt_row gt_right">1</td>
## <td headers="sales_per_sqft_state" class="gt_row gt_right">$29.59</td></tr>
## <tr><td headers="state" class="gt_row gt_left">PA</td>
## <td headers="rk_sales" class="gt_row gt_right">2</td>
## <td headers="total_sales" class="gt_row gt_right">$1,270,717</td>
## <td headers="rk_pm" class="gt_row gt_right">2</td>
## <td headers="avg_profit_margin_w" class="gt_row gt_right">17.9%</td>
## <td headers="rk_spsf" class="gt_row gt_right">2</td>
## <td headers="sales_per_sqft_state" class="gt_row gt_right">$26.00</td></tr>
## <tr><td headers="state" class="gt_row gt_left">KY</td>
## <td headers="rk_sales" class="gt_row gt_right">3</td>
## <td headers="total_sales" class="gt_row gt_right">$1,226,768</td>
## <td headers="rk_pm" class="gt_row gt_right">1</td>
## <td headers="avg_profit_margin_w" class="gt_row gt_right">18.4%</td>
## <td headers="rk_spsf" class="gt_row gt_right">3</td>
## <td headers="sales_per_sqft_state" class="gt_row gt_right">$25.63</td></tr>
## </tbody>
##
##
## </table>
## </div>
##
## $`2023`
## <div id="gzoqsfisni" style="padding-left:0px;padding-right:0px;padding-top:10px;padding-bottom:10px;overflow-x:auto;overflow-y:auto;width:auto;height:auto;">
## <style>#gzoqsfisni table {
## font-family: system-ui, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
## -webkit-font-smoothing: antialiased;
## -moz-osx-font-smoothing: grayscale;
## }
##
## #gzoqsfisni thead, #gzoqsfisni tbody, #gzoqsfisni tfoot, #gzoqsfisni tr, #gzoqsfisni td, #gzoqsfisni th {
## border-style: none;
## }
##
## #gzoqsfisni p {
## margin: 0;
## padding: 0;
## }
##
## #gzoqsfisni .gt_table {
## display: table;
## border-collapse: collapse;
## line-height: normal;
## margin-left: auto;
## margin-right: auto;
## color: #333333;
## font-size: 16px;
## font-weight: normal;
## font-style: normal;
## background-color: #FFFFFF;
## width: auto;
## border-top-style: solid;
## border-top-width: 2px;
## border-top-color: #A8A8A8;
## border-right-style: none;
## border-right-width: 2px;
## border-right-color: #D3D3D3;
## border-bottom-style: solid;
## border-bottom-width: 2px;
## border-bottom-color: #A8A8A8;
## border-left-style: none;
## border-left-width: 2px;
## border-left-color: #D3D3D3;
## }
##
## #gzoqsfisni .gt_caption {
## padding-top: 4px;
## padding-bottom: 4px;
## }
##
## #gzoqsfisni .gt_title {
## color: #333333;
## font-size: 125%;
## font-weight: initial;
## padding-top: 4px;
## padding-bottom: 4px;
## padding-left: 5px;
## padding-right: 5px;
## border-bottom-color: #FFFFFF;
## border-bottom-width: 0;
## }
##
## #gzoqsfisni .gt_subtitle {
## color: #333333;
## font-size: 85%;
## font-weight: initial;
## padding-top: 3px;
## padding-bottom: 5px;
## padding-left: 5px;
## padding-right: 5px;
## border-top-color: #FFFFFF;
## border-top-width: 0;
## }
##
## #gzoqsfisni .gt_heading {
## background-color: #FFFFFF;
## text-align: center;
## border-bottom-color: #FFFFFF;
## border-left-style: none;
## border-left-width: 1px;
## border-left-color: #D3D3D3;
## border-right-style: none;
## border-right-width: 1px;
## border-right-color: #D3D3D3;
## }
##
## #gzoqsfisni .gt_bottom_border {
## border-bottom-style: solid;
## border-bottom-width: 2px;
## border-bottom-color: #D3D3D3;
## }
##
## #gzoqsfisni .gt_col_headings {
## border-top-style: solid;
## border-top-width: 2px;
## border-top-color: #D3D3D3;
## border-bottom-style: solid;
## border-bottom-width: 2px;
## border-bottom-color: #D3D3D3;
## border-left-style: none;
## border-left-width: 1px;
## border-left-color: #D3D3D3;
## border-right-style: none;
## border-right-width: 1px;
## border-right-color: #D3D3D3;
## }
##
## #gzoqsfisni .gt_col_heading {
## color: #333333;
## background-color: #FFFFFF;
## font-size: 100%;
## font-weight: normal;
## text-transform: inherit;
## border-left-style: none;
## border-left-width: 1px;
## border-left-color: #D3D3D3;
## border-right-style: none;
## border-right-width: 1px;
## border-right-color: #D3D3D3;
## vertical-align: bottom;
## padding-top: 5px;
## padding-bottom: 6px;
## padding-left: 5px;
## padding-right: 5px;
## overflow-x: hidden;
## }
##
## #gzoqsfisni .gt_column_spanner_outer {
## color: #333333;
## background-color: #FFFFFF;
## font-size: 100%;
## font-weight: normal;
## text-transform: inherit;
## padding-top: 0;
## padding-bottom: 0;
## padding-left: 4px;
## padding-right: 4px;
## }
##
## #gzoqsfisni .gt_column_spanner_outer:first-child {
## padding-left: 0;
## }
##
## #gzoqsfisni .gt_column_spanner_outer:last-child {
## padding-right: 0;
## }
##
## #gzoqsfisni .gt_column_spanner {
## border-bottom-style: solid;
## border-bottom-width: 2px;
## border-bottom-color: #D3D3D3;
## vertical-align: bottom;
## padding-top: 5px;
## padding-bottom: 5px;
## overflow-x: hidden;
## display: inline-block;
## width: 100%;
## }
##
## #gzoqsfisni .gt_spanner_row {
## border-bottom-style: hidden;
## }
##
## #gzoqsfisni .gt_group_heading {
## padding-top: 8px;
## padding-bottom: 8px;
## padding-left: 5px;
## padding-right: 5px;
## color: #333333;
## background-color: #FFFFFF;
## font-size: 100%;
## font-weight: initial;
## text-transform: inherit;
## border-top-style: solid;
## border-top-width: 2px;
## border-top-color: #D3D3D3;
## border-bottom-style: solid;
## border-bottom-width: 2px;
## border-bottom-color: #D3D3D3;
## border-left-style: none;
## border-left-width: 1px;
## border-left-color: #D3D3D3;
## border-right-style: none;
## border-right-width: 1px;
## border-right-color: #D3D3D3;
## vertical-align: middle;
## text-align: left;
## }
##
## #gzoqsfisni .gt_empty_group_heading {
## padding: 0.5px;
## color: #333333;
## background-color: #FFFFFF;
## font-size: 100%;
## font-weight: initial;
## border-top-style: solid;
## border-top-width: 2px;
## border-top-color: #D3D3D3;
## border-bottom-style: solid;
## border-bottom-width: 2px;
## border-bottom-color: #D3D3D3;
## vertical-align: middle;
## }
##
## #gzoqsfisni .gt_from_md > :first-child {
## margin-top: 0;
## }
##
## #gzoqsfisni .gt_from_md > :last-child {
## margin-bottom: 0;
## }
##
## #gzoqsfisni .gt_row {
## padding-top: 8px;
## padding-bottom: 8px;
## padding-left: 5px;
## padding-right: 5px;
## margin: 10px;
## border-top-style: solid;
## border-top-width: 1px;
## border-top-color: #D3D3D3;
## border-left-style: none;
## border-left-width: 1px;
## border-left-color: #D3D3D3;
## border-right-style: none;
## border-right-width: 1px;
## border-right-color: #D3D3D3;
## vertical-align: middle;
## overflow-x: hidden;
## }
##
## #gzoqsfisni .gt_stub {
## color: #333333;
## background-color: #FFFFFF;
## font-size: 100%;
## font-weight: initial;
## text-transform: inherit;
## border-right-style: solid;
## border-right-width: 2px;
## border-right-color: #D3D3D3;
## padding-left: 5px;
## padding-right: 5px;
## }
##
## #gzoqsfisni .gt_stub_row_group {
## color: #333333;
## background-color: #FFFFFF;
## font-size: 100%;
## font-weight: initial;
## text-transform: inherit;
## border-right-style: solid;
## border-right-width: 2px;
## border-right-color: #D3D3D3;
## padding-left: 5px;
## padding-right: 5px;
## vertical-align: top;
## }
##
## #gzoqsfisni .gt_row_group_first td {
## border-top-width: 2px;
## }
##
## #gzoqsfisni .gt_row_group_first th {
## border-top-width: 2px;
## }
##
## #gzoqsfisni .gt_summary_row {
## color: #333333;
## background-color: #FFFFFF;
## text-transform: inherit;
## padding-top: 8px;
## padding-bottom: 8px;
## padding-left: 5px;
## padding-right: 5px;
## }
##
## #gzoqsfisni .gt_first_summary_row {
## border-top-style: solid;
## border-top-color: #D3D3D3;
## }
##
## #gzoqsfisni .gt_first_summary_row.thick {
## border-top-width: 2px;
## }
##
## #gzoqsfisni .gt_last_summary_row {
## padding-top: 8px;
## padding-bottom: 8px;
## padding-left: 5px;
## padding-right: 5px;
## border-bottom-style: solid;
## border-bottom-width: 2px;
## border-bottom-color: #D3D3D3;
## }
##
## #gzoqsfisni .gt_grand_summary_row {
## color: #333333;
## background-color: #FFFFFF;
## text-transform: inherit;
## padding-top: 8px;
## padding-bottom: 8px;
## padding-left: 5px;
## padding-right: 5px;
## }
##
## #gzoqsfisni .gt_first_grand_summary_row {
## padding-top: 8px;
## padding-bottom: 8px;
## padding-left: 5px;
## padding-right: 5px;
## border-top-style: double;
## border-top-width: 6px;
## border-top-color: #D3D3D3;
## }
##
## #gzoqsfisni .gt_last_grand_summary_row_top {
## padding-top: 8px;
## padding-bottom: 8px;
## padding-left: 5px;
## padding-right: 5px;
## border-bottom-style: double;
## border-bottom-width: 6px;
## border-bottom-color: #D3D3D3;
## }
##
## #gzoqsfisni .gt_striped {
## background-color: rgba(128, 128, 128, 0.05);
## }
##
## #gzoqsfisni .gt_table_body {
## border-top-style: solid;
## border-top-width: 2px;
## border-top-color: #D3D3D3;
## border-bottom-style: solid;
## border-bottom-width: 2px;
## border-bottom-color: #D3D3D3;
## }
##
## #gzoqsfisni .gt_footnotes {
## color: #333333;
## background-color: #FFFFFF;
## border-bottom-style: none;
## border-bottom-width: 2px;
## border-bottom-color: #D3D3D3;
## border-left-style: none;
## border-left-width: 2px;
## border-left-color: #D3D3D3;
## border-right-style: none;
## border-right-width: 2px;
## border-right-color: #D3D3D3;
## }
##
## #gzoqsfisni .gt_footnote {
## margin: 0px;
## font-size: 90%;
## padding-top: 4px;
## padding-bottom: 4px;
## padding-left: 5px;
## padding-right: 5px;
## }
##
## #gzoqsfisni .gt_sourcenotes {
## color: #333333;
## background-color: #FFFFFF;
## border-bottom-style: none;
## border-bottom-width: 2px;
## border-bottom-color: #D3D3D3;
## border-left-style: none;
## border-left-width: 2px;
## border-left-color: #D3D3D3;
## border-right-style: none;
## border-right-width: 2px;
## border-right-color: #D3D3D3;
## }
##
## #gzoqsfisni .gt_sourcenote {
## font-size: 90%;
## padding-top: 4px;
## padding-bottom: 4px;
## padding-left: 5px;
## padding-right: 5px;
## }
##
## #gzoqsfisni .gt_left {
## text-align: left;
## }
##
## #gzoqsfisni .gt_center {
## text-align: center;
## }
##
## #gzoqsfisni .gt_right {
## text-align: right;
## font-variant-numeric: tabular-nums;
## }
##
## #gzoqsfisni .gt_font_normal {
## font-weight: normal;
## }
##
## #gzoqsfisni .gt_font_bold {
## font-weight: bold;
## }
##
## #gzoqsfisni .gt_font_italic {
## font-style: italic;
## }
##
## #gzoqsfisni .gt_super {
## font-size: 65%;
## }
##
## #gzoqsfisni .gt_footnote_marks {
## font-size: 75%;
## vertical-align: 0.4em;
## position: initial;
## }
##
## #gzoqsfisni .gt_asterisk {
## font-size: 100%;
## vertical-align: 0;
## }
##
## #gzoqsfisni .gt_indent_1 {
## text-indent: 5px;
## }
##
## #gzoqsfisni .gt_indent_2 {
## text-indent: 10px;
## }
##
## #gzoqsfisni .gt_indent_3 {
## text-indent: 15px;
## }
##
## #gzoqsfisni .gt_indent_4 {
## text-indent: 20px;
## }
##
## #gzoqsfisni .gt_indent_5 {
## text-indent: 25px;
## }
##
## #gzoqsfisni .katex-display {
## display: inline-flex !important;
## margin-bottom: 0.75em !important;
## }
##
## #gzoqsfisni div.Reactable > div.rt-table > div.rt-thead > div.rt-tr.rt-tr-group-header > div.rt-th-group:after {
## height: 0px !important;
## }
## </style>
## <table class="gt_table" data-quarto-disable-processing="false" data-quarto-bootstrap="false">
## <thead>
## <tr class="gt_heading">
## <td colspan="7" class="gt_heading gt_title gt_font_normal gt_bottom_border" style><span class='gt_from_md'>State Rankings — 2023</span></td>
## </tr>
##
## <tr class="gt_col_headings">
## <th class="gt_col_heading gt_columns_bottom_border gt_left" rowspan="1" colspan="1" scope="col" id="state">State</th>
## <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" scope="col" id="rk_sales">Rank: Total Sales</th>
## <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" scope="col" id="total_sales">Total Sales</th>
## <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" scope="col" id="rk_pm">Rank: Avg Profit Margin</th>
## <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" scope="col" id="avg_profit_margin_w">Avg Profit Margin</th>
## <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" scope="col" id="rk_spsf">Rank: Sales/SqFt</th>
## <th class="gt_col_heading gt_columns_bottom_border gt_right" rowspan="1" colspan="1" scope="col" id="sales_per_sqft_state">Sales/SqFt</th>
## </tr>
## </thead>
## <tbody class="gt_table_body">
## <tr><td headers="state" class="gt_row gt_left">OH</td>
## <td headers="rk_sales" class="gt_row gt_right">1</td>
## <td headers="total_sales" class="gt_row gt_right">$1,596,196</td>
## <td headers="rk_pm" class="gt_row gt_right">3</td>
## <td headers="avg_profit_margin_w" class="gt_row gt_right">17.9%</td>
## <td headers="rk_spsf" class="gt_row gt_right">1</td>
## <td headers="sales_per_sqft_state" class="gt_row gt_right">$33.34</td></tr>
## <tr><td headers="state" class="gt_row gt_left">PA</td>
## <td headers="rk_sales" class="gt_row gt_right">2</td>
## <td headers="total_sales" class="gt_row gt_right">$1,415,636</td>
## <td headers="rk_pm" class="gt_row gt_right">2</td>
## <td headers="avg_profit_margin_w" class="gt_row gt_right">19.0%</td>
## <td headers="rk_spsf" class="gt_row gt_right">2</td>
## <td headers="sales_per_sqft_state" class="gt_row gt_right">$28.96</td></tr>
## <tr><td headers="state" class="gt_row gt_left">KY</td>
## <td headers="rk_sales" class="gt_row gt_right">3</td>
## <td headers="total_sales" class="gt_row gt_right">$1,175,403</td>
## <td headers="rk_pm" class="gt_row gt_right">1</td>
## <td headers="avg_profit_margin_w" class="gt_row gt_right">20.8%</td>
## <td headers="rk_spsf" class="gt_row gt_right">3</td>
## <td headers="sales_per_sqft_state" class="gt_row gt_right">$24.55</td></tr>
## </tbody>
##
##
## </table>
## </div>
Conclusions
best_by_year <- state_rank |>
group_by(year) |>
summarize(
sales_winner = state[which.min(rk_sales)],
pm_winner = state[which.min(rk_pm)],
spsf_winner = state[which.min(rk_spsf)]
)
conv_summary <- conv_class |>
arrange(desc(median_conv)) |>
mutate(median_conv = dollar(median_conv))
cat("### Executive Summary\n")
## ### Executive Summary
cat("- Over 2021–2023, ", growth_tbl$state[which.max(growth_tbl$cagr)], " shows the strongest sales growth (CAGR).\n", sep = "")
## - Over 2021–2023, OH shows the strongest sales growth (CAGR).
cat("- Profit margins tended to move in the *",
ifelse(mean(growth_tbl$pm_slope, na.rm = TRUE) >= 0, "same/upward", "different/downward"),
"* direction as sales overall, with state-level exceptions.\n", sep = "")
## - Profit margins tended to move in the *same/upward* direction as sales overall, with state-level exceptions.
cat("- Conversion leaders (median sales/customer): ",
paste0(conv_summary$state, " (", conv_summary$median_conv, ")", collapse = "; "), ".\n")
## - Conversion leaders (median sales/customer): OH ($49.42); KY ($41.16); PA ($34.39) .
cat("- Efficiency: See Top-10 tables for Sales/Employee & Sales/SqFt (consistency noted).\n")
## - Efficiency: See Top-10 tables for Sales/Employee & Sales/SqFt (consistency noted).
if (nrow(underperf) > 0) {
cat("- Attention: Some stores consistently underperform on both Profit Margin and Sales/SqFt (≥2 years). Prioritize diagnostics.\n")
} else {
cat("- No stores consistently underperform on both Profit Margin and Sales/SqFt across multiple years.\n")
}
## - Attention: Some stores consistently underperform on both Profit Margin and Sales/SqFt (≥2 years). Prioritize diagnostics.
cat("- Rankings: Winners by metric each year:\n")
## - Rankings: Winners by metric each year:
print(best_by_year)
## # A tibble: 3 × 4
## year sales_winner pm_winner spsf_winner
## <int> <chr> <chr> <chr>
## 1 2021 KY PA KY
## 2 2022 OH KY OH
## 3 2023 OH KY OH
Tip: If you don’t have a LaTeX engine installed, knit to HTML first
(or install TinyTeX via tinytex::install_tinytex()
).