library(tidyverse)
## Warning: package 'tidyverse' was built under R version 4.5.2
## Warning: package 'stringr' was built under R version 4.5.2
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.5
## ✔ forcats 1.0.1 ✔ stringr 1.6.0
## ✔ ggplot2 4.0.0 ✔ tibble 3.3.0
## ✔ lubridate 1.9.4 ✔ tidyr 1.3.1
## ✔ purrr 1.1.0
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(lubridate)
set.seed(123)
dates <- seq.Date(
from = as.Date("2010-01-01"),
to = as.Date("2015-12-31"),
by = "week"
)
sales_data <- tibble(
date = dates,
sales =
200000 +
seq_along(dates) * 800 +
40000 * sin(2 * pi * yday(dates) / 365) +
rnorm(length(dates), 0, 25000)
)
weekly_sales <- sales_data %>%
group_by(week = floor_date(date, "week")) %>%
summarise(total_sales = sum(sales), .groups = "drop")
monthly_sales <- sales_data %>%
group_by(month = floor_date(date, "month")) %>%
summarise(total_sales = sum(sales), .groups = "drop")
quarterly_sales <- sales_data %>%
group_by(quarter = floor_date(date, "quarter")) %>%
summarise(total_sales = sum(sales), .groups = "drop")
# Quarterly
ggplot(quarterly_sales, aes(quarter, total_sales)) +
geom_line(color = "blue", linewidth = 1) +
labs(title = "Total Sales", y = "Revenue (USD)") +
theme_minimal()

# Monthly
ggplot(monthly_sales, aes(month, total_sales)) +
geom_point(alpha = 0.7) +
geom_smooth(se = TRUE, color = "blue") +
labs(title = "Total Sales", y = "Revenue (USD)") +
theme_minimal()
## `geom_smooth()` using method = 'loess' and formula = 'y ~ x'

# Weekly
ggplot(weekly_sales, aes(week, total_sales)) +
geom_point(size = 1, alpha = 0.8) +
geom_smooth(se = TRUE, color = "blue") +
labs(title = "Total Sales", y = "Revenue (USD)") +
theme_minimal()
## `geom_smooth()` using method = 'loess' and formula = 'y ~ x'

# 1. Load Libraries
library(tidyverse)
library(tidyquant)
## Registered S3 method overwritten by 'quantmod':
## method from
## as.zoo.data.frame zoo
## ── Attaching core tidyquant packages ─────────────────────── tidyquant 1.0.11 ──
## ✔ PerformanceAnalytics 2.0.8 ✔ TTR 0.24.4
## ✔ quantmod 0.4.28 ✔ xts 0.14.1
## ── Conflicts ────────────────────────────────────────── tidyquant_conflicts() ──
## ✖ zoo::as.Date() masks base::as.Date()
## ✖ zoo::as.Date.numeric() masks base::as.Date.numeric()
## ✖ dplyr::filter() masks stats::filter()
## ✖ xts::first() masks dplyr::first()
## ✖ dplyr::lag() masks stats::lag()
## ✖ xts::last() masks dplyr::last()
## ✖ PerformanceAnalytics::legend() masks graphics::legend()
## ✖ quantmod::summary() masks base::summary()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(lubridate)
# 2. Setup Data (Replace this part with your 'read_csv' if you have the file)
set.seed(123)
dates <- seq(as.Date("2011-01-01"), as.Date("2015-12-31"), by="week")
road_cats <- c("Elite Road", "Endurance Road", "Triathlon", "Cyclocross")
mtn_cats <- c("Cross Country Race", "Trail", "Over Mountain", "Sport", "Fat Bike")
data <- expand.grid(order_date = dates, category_2 = c(road_cats, mtn_cats)) %>%
mutate(
sales = runif(n(), 10000, 50000) * (1 + sin(month(order_date)/2)),
category_1 = ifelse(category_2 %in% road_cats, "Road", "Mountain")
)
# 3. Improved Plotting Function
# Added 'scales = "free_y"' and 'ncol = 1' to ensure they stack vertically and zoom in
plot_sales <- function(df, title_text, subtitle_text) {
df %>%
ggplot(aes(order_date, sales, color = category_2)) +
geom_point(alpha = 0.5, size = 0.8, color = "#2c3e50") +
geom_smooth(method = "loess", span = 0.2, size = 1, fill = "lightgrey") +
facet_wrap(~ category_2, ncol = 1, scales = "free_y") +
scale_y_continuous(labels = scales::dollar_format(scale = 1e-3, suffix = "K")) +
scale_color_tq() +
theme_tq() +
labs(title = title_text, subtitle = subtitle_text, x = "", y = "") +
theme(
strip.background = element_rect(fill = "#2c3e50"),
strip.text = element_text(color = "white", face = "bold"),
legend.position = "none",
panel.spacing = unit(1, "lines") # Adds space between the charts
)
}
# 4. Generate the Chart
# Example: Mountain Monthly
mtn_monthly_plot <- data %>%
filter(category_1 == "Mountain") %>%
group_by(category_2, order_date = floor_date(order_date, "month")) %>%
summarise(sales = sum(sales)) %>%
plot_sales("Sales By Category 2", "Monthly")
## `summarise()` has grouped output by 'category_2'. You can override using the
## `.groups` argument.
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
# 5. THE FIX: SAVE TO PDF
# 'height = 14' ensures the charts are tall and not squashed
ggsave("Mountain_Sales_Clear.pdf",
plot = mtn_monthly_plot,
width = 8.5,
height = 14,
device = "pdf")
## `geom_smooth()` using formula = 'y ~ x'
# Print to RStudio
print(mtn_monthly_plot)
## `geom_smooth()` using formula = 'y ~ x'
