Jayden Khalifa Armand
Student ID: 114035109
This report analyzes instructor-provided financial transaction data to evaluate sales performance across product categories and order behavior. The goal is to demonstrate how a transactional database can be transformed into managerial insights through structured aggregation and reporting.
Key Objectives :
This analysis uses provided relational datasets representing bicycle sales transactions:
# Import Library
library(tidyverse)
library(lubridate)
library(scales)
library(readxl)
library(janitor)
# Load tables
orderlines <- read_excel("orderlines.xlsx") |> clean_names()
bikes <- read_excel("bikes.xlsx") |> clean_names()
bikeshops <- read_excel("bikeshops.xlsx") |> clean_names()
# Load Main Analysis Table that used
sales_raw <- read_csv("bike_orderlines.csv", show_col_types = FALSE) |> clean_names()
# Clean & prepare analysis fields
sales <- sales_raw |>
mutate(
order_date = parse_date_time(order_date, orders = c("Ymd HMS", "Ymd HM", "Ymd")),
total_price = readr::parse_number(as.character(total_price))
) |>
filter(!is.na(order_date), !is.na(total_price)) |>
mutate(
month = floor_date(order_date, "month"),
quarter = paste0(year(order_date), " Q", quarter(order_date))
)
glimpse(sales)
## Rows: 15,644
## Columns: 15
## $ order_date <dttm> 2011-01-07, 2011-01-07, 2011-01-10, 2011-01-10, 2011-0…
## $ order_id <dbl> 1, 1, 2, 2, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7…
## $ order_line <dbl> 1, 2, 1, 2, 1, 2, 3, 4, 5, 1, 1, 2, 3, 4, 1, 2, 3, 4, 1…
## $ quantity <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1…
## $ price <dbl> 6070, 5970, 2770, 5970, 10660, 3200, 12790, 5330, 1570,…
## $ total_price <dbl> 6070, 5970, 2770, 5970, 10660, 3200, 12790, 5330, 1570,…
## $ model <chr> "Jekyll Carbon 2", "Trigger Carbon 2", "Beast of the Ea…
## $ category_1 <chr> "Mountain", "Mountain", "Mountain", "Mountain", "Road",…
## $ category_2 <chr> "Over Mountain", "Over Mountain", "Trail", "Over Mounta…
## $ frame_material <chr> "Carbon", "Carbon", "Aluminum", "Carbon", "Carbon", "Ca…
## $ bikeshop_name <chr> "Ithaca Mountain Climbers", "Ithaca Mountain Climbers",…
## $ city <chr> "Ithaca", "Ithaca", "Kansas City", "Kansas City", "Loui…
## $ state <chr> "NY", "NY", "KS", "KS", "KY", "KY", "KY", "KY", "KY", "…
## $ month <dttm> 2011-01-01, 2011-01-01, 2011-01-01, 2011-01-01, 2011-0…
## $ quarter <chr> "2011 Q1", "2011 Q1", "2011 Q1", "2011 Q1", "2011 Q1", …
I also try to aggregate transactions into monthly and quarterly periods and compute key financial indicators:
#Monthly totals
monthly_totals <- sales |>
group_by(month) |>
summarise(
total_sales = sum(total_price, na.rm = TRUE),
orders = n_distinct(order_id),
units = sum(quantity, na.rm = TRUE),
.groups = "drop"
) |>
mutate(
aov = total_sales / orders,
units_per_order = units / orders
)
# Monthly by category (Road vs Mountain)
monthly_by_cat <- sales |>
group_by(month, category_1) |>
summarise(
total_sales = sum(total_price, na.rm = TRUE),
orders = n_distinct(order_id),
units = sum(quantity, na.rm = TRUE),
.groups = "drop"
) |>
mutate(
aov = total_sales / orders,
units_per_order = units / orders
)
# Quarterly summary
quarterly_totals <- sales |>
group_by(quarter) |>
summarise(
total_sales = sum(total_price, na.rm = TRUE),
orders = n_distinct(order_id),
.groups = "drop"
)
ggplot(monthly_totals, aes(month, total_sales)) +
geom_line(linewidth = 1.0, color = "#2c3e50") +
geom_point(size = 1.2, color = "#3498db", alpha = 0.85) +
scale_y_continuous(labels = scales::dollar) +
labs(
title = "Monthly Total Sales",
subtitle = "Overall revenue trend using monthly aggregation",
x = "Month",
y = "Total Sales (USD)"
) +
theme_minimal(base_size = 12)
ggplot(quarterly_totals, aes(quarter, total_sales, group = 1)) +
geom_line(linewidth = 1.0, color = "#2c3e50", alpha = 0.9) +
geom_point(size = 2.0, color = "#9b59b6", alpha = 0.9) +
scale_y_continuous(labels = scales::dollar) +
labs(
title = "Quarterly Total Sales",
subtitle = "Quarterly aggregation is commonly used for strategic evaluation",
x = "Quarter",
y = "Total Sales (USD)"
) +
theme_minimal(base_size = 12) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Instead of plotting both categories on one line, this report uses faceting to clearly compare patterns across categories.
ggplot(monthly_by_cat, aes(x = month, y = total_sales)) +
geom_line(linewidth = 1.0, color = "#e74c3c", alpha = 0.9) +
scale_y_continuous(labels = scales::dollar) +
facet_wrap(~ category_1, scales = "free_y") +
labs(
title = "Monthly Sales by Category (Faceted View)",
subtitle = "Faceting improves readability when categories have different magnitudes",
x = "Month",
y = "Sales (USD)"
) +
theme_minimal(base_size = 12)
ggplot(monthly_by_cat, aes(x = month, y = aov)) +
geom_line(linewidth = 1.0, color = "#2c3e50") +
facet_wrap(~ category_1, scales = "free_y") +
scale_y_continuous(labels = scales::dollar) +
labs(
title = "Average Order Value (AOV) by Category",
subtitle = "Shows differences in transaction value across categories",
x = "Month",
y = "AOV (USD)"
) +
theme_minimal(base_size = 12)
ggplot(monthly_by_cat, aes(x = month, y = units_per_order)) +
geom_line(linewidth = 1.0, color = "#3498db") +
facet_wrap(~ category_1) +
labs(
title = "Average Units per Order by Category",
subtitle = "Helps interpret whether customers buy more items per order by category",
x = "Month",
y = "Units per Order"
) +
theme_minimal(base_size = 12)