1 Pendahuluan

Sebuah perusahaan ritel yang beroperasi di Amerika Serikat ingin mengevaluasi kinerja penjualannya selama periode 2015–2018. Perusahaan ini menjual berbagai produk yang dikategorikan ke dalam Furniture, Office Supplies, dan Technology, dengan pelanggan yang tersebar di empat wilayah utama: East, West, Central, dan South. Data transaksi harian yang dikumpulkan meliputi informasi pesanan, pelanggan, produk, dan nilai penjualan. Manajemen memerlukan analisis mendalam untuk memahami pola distribusi penjualan, kontribusi antar kategori produk, performa regional, serta tren musiman yang terjadi selama empat tahun terakhir. Hasil analisis akan digunakan untuk menyusun strategi pengelolaan stok, optimalisasi pemasaran, dan rencana ekspansi pasar. Dataset yang digunakan dalam analisis ini adalah Superstore Sales Dataset, sebuah dataset transaksi penjualan dari sebuah perusahaan retail di Amerika Serikat yang mencakup periode 2015 hingga 2018. Dataset ini umum digunakan dalam pembelajaran analisis data dan visualisasi karena kekayaan variabel yang dimilikinya.

Analisis ini mencakup tiga bagian utama:

  1. Eksplorasi Awal Data : memahami struktur, variabel, dan kualitas data
  2. Visualisasi Data : empat jenis visualisasi (kategorik, kontinu, bivariat, time series)
  3. Analisis & Insight : temuan pola, hubungan, dan tren dari hasil visualisasi

2 Library & Import Data

2.1 Load Library

library(readxl)
library(ggplot2)
library(dplyr)
library(scales)
library(lubridate)
library(patchwork)
library(forcats)
library(viridis)
library(knitr)
library(kableExtra)

2.2 Import Data

library(readxl)

data <- read_excel("data.visdat.project.xlsx")

3 Eksplorasi Awal Data

3.1 Penjelasan Dataset

Dataset Superstore Sales berisi catatan transaksi penjualan produk dari berbagai kategori kepada pelanggan di seluruh wilayah Amerika Serikat. Data ini sangat kaya informasi mulai dari detail pelanggan, metode pengiriman, kategori produk, hingga nilai penjualan per transaksi.

head(data)

3.2 Penjelasan Variabel

Berikut adalah kamus variabel yang terdapat dalam dataset:

No Variabel Tipe Deskripsi
1 Row ID Numerik ID unik untuk setiap baris data
2 Order ID Karakter ID unik untuk setiap order/pesanan
3 Order Date Tanggal Tanggal order dibuat oleh pelanggan
4 Ship Date Tanggal Tanggal produk dikirim ke pelanggan
5 Ship Mode Kategorik Metode pengiriman (4 kelas: Same Day, First Class, Second Class, Standard Class)
6 Customer ID Karakter ID unik pelanggan
7 Customer Name Karakter Nama lengkap pelanggan
8 Segment Kategorik Segmen pelanggan: Consumer, Corporate, atau Home Office
9 Country Karakter Negara tujuan pengiriman (seluruhnya USA)
10 City Karakter Kota tujuan pengiriman
11 State Karakter Negara bagian tujuan pengiriman
12 Postal Code Numerik Kode pos tujuan pengiriman
13 Region Kategorik Wilayah: East, West, Central, atau South
14 Product ID Karakter ID unik produk
15 Category Kategorik Kategori produk: Furniture, Office Supplies, atau Technology
16 Sub-Category Kategorik Sub-kategori produk (17 sub-kategori)
17 Product Name Karakter Nama lengkap produk
18 Sales Numerik Nilai penjualan dalam USD (variabel target utama)

3.3 Struktur Data

str(data)
## tibble [9,800 × 18] (S3: tbl_df/tbl/data.frame)
##  $ Row ID       : num [1:9800] 1 2 3 4 5 6 7 8 9 10 ...
##  $ Order ID     : chr [1:9800] "CA-2017-152156" "CA-2017-152156" "CA-2017-138688" "US-2016-108966" ...
##  $ Order Date   : POSIXct[1:9800], format: "2017-11-08" "2017-11-08" ...
##  $ Ship Date    : POSIXct[1:9800], format: "2017-11-11" "2017-11-11" ...
##  $ Ship Mode    : chr [1:9800] "Second Class" "Second Class" "Second Class" "Standard Class" ...
##  $ Customer ID  : chr [1:9800] "CG-12520" "CG-12520" "DV-13045" "SO-20335" ...
##  $ Customer Name: chr [1:9800] "Claire Gute" "Claire Gute" "Darrin Van Huff" "Sean O'Donnell" ...
##  $ Segment      : chr [1:9800] "Consumer" "Consumer" "Corporate" "Consumer" ...
##  $ Country      : chr [1:9800] "United States" "United States" "United States" "United States" ...
##  $ City         : chr [1:9800] "Henderson" "Henderson" "Los Angeles" "Fort Lauderdale" ...
##  $ State        : chr [1:9800] "Kentucky" "Kentucky" "California" "Florida" ...
##  $ Postal Code  : num [1:9800] 42420 42420 90036 33311 33311 ...
##  $ Region       : chr [1:9800] "South" "South" "West" "South" ...
##  $ Product ID   : chr [1:9800] "FUR-BO-10001798" "FUR-CH-10000454" "OFF-LA-10000240" "FUR-TA-10000577" ...
##  $ Category     : chr [1:9800] "Furniture" "Furniture" "Office Supplies" "Furniture" ...
##  $ Sub-Category : chr [1:9800] "Bookcases" "Chairs" "Labels" "Tables" ...
##  $ Product Name : chr [1:9800] "Bush Somerset Collection Bookcase" "Hon Deluxe Fabric Upholstered Stacking Chairs, Rounded Back" "Self-Adhesive Address Labels for Typewriters by Universal" "Bretford CR4500 Series Slim Rectangular Table" ...
##  $ Sales        : num [1:9800] 262 731.9 14.6 957.6 22.4 ...

3.4 Ringkasan Statistik

summary(data)
##      Row ID       Order ID           Order Date                    
##  Min.   :   1   Length:9800        Min.   :2015-01-03 00:00:00.00  
##  1st Qu.:2451   Class :character   1st Qu.:2016-05-24 00:00:00.00  
##  Median :4900   Mode  :character   Median :2017-06-26 00:00:00.00  
##  Mean   :4900                      Mean   :2017-05-01 05:13:51.67  
##  3rd Qu.:7350                      3rd Qu.:2018-05-15 00:00:00.00  
##  Max.   :9800                      Max.   :2018-12-30 00:00:00.00  
##                                                                    
##    Ship Date                       Ship Mode         Customer ID       
##  Min.   :2015-01-07 00:00:00.00   Length:9800        Length:9800       
##  1st Qu.:2016-05-27 18:00:00.00   Class :character   Class :character  
##  Median :2017-06-29 00:00:00.00   Mode  :character   Mode  :character  
##  Mean   :2017-05-05 04:17:52.65                                        
##  3rd Qu.:2018-05-19 00:00:00.00                                        
##  Max.   :2019-01-05 00:00:00.00                                        
##                                                                        
##  Customer Name        Segment            Country              City          
##  Length:9800        Length:9800        Length:9800        Length:9800       
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##                                                                             
##     State            Postal Code       Region           Product ID       
##  Length:9800        Min.   : 1040   Length:9800        Length:9800       
##  Class :character   1st Qu.:23223   Class :character   Class :character  
##  Mode  :character   Median :58103   Mode  :character   Mode  :character  
##                     Mean   :55273                                        
##                     3rd Qu.:90008                                        
##                     Max.   :99301                                        
##                     NA's   :11                                           
##    Category         Sub-Category       Product Name           Sales          
##  Length:9800        Length:9800        Length:9800        Min.   :    0.444  
##  Class :character   Class :character   Class :character   1st Qu.:   17.242  
##  Mode  :character   Mode  :character   Mode  :character   Median :   54.376  
##                                                           Mean   :  230.714  
##                                                           3rd Qu.:  210.002  
##                                                           Max.   :22638.480  
##                                                           NA's   :6

3.5 Tipe Data per Kolom

sapply(data, class)
## $`Row ID`
## [1] "numeric"
## 
## $`Order ID`
## [1] "character"
## 
## $`Order Date`
## [1] "POSIXct" "POSIXt" 
## 
## $`Ship Date`
## [1] "POSIXct" "POSIXt" 
## 
## $`Ship Mode`
## [1] "character"
## 
## $`Customer ID`
## [1] "character"
## 
## $`Customer Name`
## [1] "character"
## 
## $Segment
## [1] "character"
## 
## $Country
## [1] "character"
## 
## $City
## [1] "character"
## 
## $State
## [1] "character"
## 
## $`Postal Code`
## [1] "numeric"
## 
## $Region
## [1] "character"
## 
## $`Product ID`
## [1] "character"
## 
## $Category
## [1] "character"
## 
## $`Sub-Category`
## [1] "character"
## 
## $`Product Name`
## [1] "character"
## 
## $Sales
## [1] "numeric"

3.6 Cek Missing Values

colSums(is.na(data))
##        Row ID      Order ID    Order Date     Ship Date     Ship Mode 
##             0             0             0             0             0 
##   Customer ID Customer Name       Segment       Country          City 
##             0             0             0             0             0 
##         State   Postal Code        Region    Product ID      Category 
##             0            11             0             0             0 
##  Sub-Category  Product Name         Sales 
##             0             0             6

3.7 Cleaning Data

Terdapat beberapa baris dengan nilai kosong (missing). Baris tersebut dihapus agar analisis berjalan bersih dan akurat.

data_clean <- na.omit(data)

cat("Jumlah baris setelah cleaning:", nrow(data_clean), "\n")
## Jumlah baris setelah cleaning: 9783
cat("Jumlah kolom                 :", ncol(data_clean), "\n")
## Jumlah kolom                 : 18
cat("Missing values tersisa       :", sum(is.na(data_clean)), "\n")
## Missing values tersisa       : 0
summary(data_clean$Sales)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
##     0.444    17.244    54.320   230.060   209.975 22638.480

3.8 Parsing Tanggal & Kolom Turunan

Kolom Order Date dan Ship Date dikonversi ke tipe Date, kemudian ditambahkan kolom turunan untuk keperluan analisis waktu.

data_clean <- data_clean %>%
  mutate(
    order_date = as.Date(`Order Date`),
    ship_date  = as.Date(`Ship Date`),
    ship_days  = as.numeric(ship_date - order_date),
    year       = year(order_date),
    month      = month(order_date, label = TRUE, abbr = TRUE),
    quarter    = paste0("Q", quarter(order_date)),
    year_month = floor_date(order_date, "month")
  )

# Cek hasil parsing
head(data_clean[, c("Order Date", "order_date", "year", "month", "quarter")])

4 Visualisasi Data

4.1 Visualisasi Kategorik

Visualisasi kategorik digunakan untuk memahami distribusi frekuensi dari variabel-variabel yang bersifat kategori, seperti Category dan Ship Mode.

4.1.1 Jumlah Order per Kategori

p1a <- data_clean %>%
  count(Category) %>%
  mutate(
    Category = reorder(Category, n),
    pct = n / sum(n)
  ) %>% 
  ggplot(aes(x = Category, y = n, fill = Category)) +
  geom_col(width = 0.55, show.legend = FALSE) +
  geom_text(aes(label = paste0(comma(n), "\n(", percent(pct, accuracy = 1), ")")),
            vjust = -0.4, size = 3.8, fontface = "bold", color = "#333") +
  scale_fill_manual(values = pal_cat) +
  scale_y_continuous(expand = expansion(mult = c(0, 0.2))) +
  labs(title    = "Jumlah Order per Kategori Produk",
       subtitle = "Office Supplies mendominasi jumlah transaksi",
       x = "Kategori", y = "Jumlah Order",
       caption = "Sumber: Superstore Sales Dataset 2015-2018") +
  theme_super()

print(p1a)

4.1.2 Jumlah Order per Ship Mode

p1b <- data_clean %>%
  count(`Ship Mode`) %>%
  mutate(`Ship Mode` = fct_reorder(`Ship Mode`, n)) %>%
  ggplot(aes(x = `Ship Mode`, y = n, fill = `Ship Mode`)) +
  geom_col(width = 0.55, show.legend = FALSE) +
  geom_text(aes(label = comma(n)), hjust = -0.2,
            size = 3.8, fontface = "bold", color = "#333") +
  scale_fill_manual(values = pal_main) +
  scale_y_continuous(expand = expansion(mult = c(0, 0.18))) +
  coord_flip() +
  labs(title    = "Jumlah Order per Ship Mode",
       subtitle = "Standard Class dipilih lebih dari separuh pelanggan",
       x = NULL, y = "Jumlah Order",
       caption = "Sumber: Superstore Sales Dataset 2015-2018") +
  theme_super()

print(p1b)

4.1.3 Gabungan

vis1 <- (p1a | p1b) +
  plot_annotation(
    title = "VISUALISASI 1: KATEGORIK — Distribusi Order per Kategori & Ship Mode",
    theme = theme(plot.title = element_text(face = "bold", size = 14,
                                            hjust = 0.5, color = "#1a1a2e"))
  )
print(vis1)

4.1.4 📝 Interpretasi

Temuan Utama Visualisasi Kategorik:

  • Office Supplies memiliki jumlah transaksi terbanyak (~60% dari total order), namun bukan penyumbang revenue terbesar karena nilai per transaksinya relatif kecil.
  • Technology memiliki jumlah transaksi paling sedikit, namun nilai penjualannya tertinggi menunjukkan produk Technology memiliki harga satuan yang lebih mahal.
  • Standard Class dipilih oleh hampir 60% pelanggan di semua segmen, menunjukkan mayoritas pelanggan lebih mengutamakan biaya hemat daripada kecepatan pengiriman.
  • Same Day hanya dipilih 5.5% pelanggan ini merupakan peluang bisnis layanan pengiriman ekspres yang masih bisa dikembangkan.

4.2 Visualisasi Kontinu

Visualisasi kontinu digunakan untuk memahami sebaran/distribusi dari variabel numerik, dalam hal ini variabel Sales.

4.2.1 Distribusi Sales (Histogram)

p2a <- ggplot(data_clean, aes(x = Sales)) +
  geom_histogram(aes(y = after_stat(density)),
                 bins = 50, fill = "#264653", color = "white", alpha = 0.85) +
  geom_density(color = "#e76f51", linewidth = 1.3) +
  geom_vline(xintercept = median(data_clean$Sales),
             color = "#f4a261", linetype = "dashed", linewidth = 1) +
  geom_vline(xintercept = mean(data_clean$Sales),
             color = "#e76f51", linetype = "dashed", linewidth = 1) +
  scale_x_log10(labels = dollar) +
  annotate("text", x = median(data_clean$Sales) * 1.5, y = 0.42,
           label = paste0("Median\n$", round(median(data_clean$Sales))),
           size = 3.2, color = "#f4a261", fontface = "bold") +
  annotate("text", x = mean(data_clean$Sales) * 2.2, y = 0.30,
           label = paste0("Mean\n$", round(mean(data_clean$Sales))),
           size = 3.2, color = "#e76f51", fontface = "bold") +
  labs(title    = "Distribusi Nilai Penjualan (Sales)",
       subtitle = "Skala logaritmik — distribusi right-skewed, mayoritas transaksi di bawah $500",
       x = "Sales (skala logaritmik)", y = "Densitas",
       caption = "Sumber: Superstore Sales Dataset 2015-2018") +
  theme_super()

print(p2a)

4.2.2 Distribusi Sales per Kategori

p2b <- ggplot(data_clean, aes(x = Category, y = Sales, fill = Category)) +
  geom_violin(alpha = 0.45, trim = FALSE) +
  geom_boxplot(width = 0.15, outlier.size = 1.5,
               outlier.alpha = 0.4, outlier.color = "#e76f51") +
  scale_y_log10(labels = dollar) +
  scale_fill_manual(values = pal_cat) +
  labs(title    = "Distribusi Sales per Kategori Produk",
       subtitle = "Technology memiliki transaksi terbesar dan variasi terluas",
       x = "Kategori", y = "Sales (skala logaritmik)",
       caption = "Sumber: Superstore Sales Dataset 2015-2018") +
  theme_super() +
  theme(legend.position = "none")

print(p2b)

4.2.3 Gabungan

vis2 <- (p2a | p2b) +
  plot_annotation(
    title = "VISUALISASI 2: KONTINU — Distribusi Nilai Penjualan (Sales)",
    theme = theme(plot.title = element_text(face = "bold", size = 14,
                                            hjust = 0.5, color = "#1a1a2e"))
  )
print(vis2)

4.2.4 📝 Interpretasi

Temuan Utama Visualisasi Kontinu:

  • Distribusi Sales bersifat right-skewed (miring ke kanan) mayoritas transaksi bernilai kecil, namun ada sejumlah transaksi bernilai sangat besar yang menarik nilai rata-rata ke atas.
  • Median ($55) jauh lebih kecil dari Mean ($231), mengkonfirmasi adanya outlier transaksi bernilai tinggi.
  • Technology memiliki sebaran terluas ada produk murah (aksesoris kecil) hingga sangat mahal (mesin fotokopi, printer industri) dengan nilai >$20.000.
  • Office Supplies memiliki sebaran paling sempit dan nilai paling kecil
    konsisten dengan produk habis pakai seperti kertas, label, dan alat tulis.

4.3 Visualisasi Bivariat

Visualisasi bivariat digunakan untuk melihat hubungan antara dua variabel sekaligus, yaitu Sales dengan Sub-Category, serta Category dengan Region.

4.3.1 Total Sales per Sub-Kategori

subcat <- data_clean %>%
  group_by(`Sub-Category`, Category) %>%
  summarise(Total = sum(Sales), .groups = "drop") %>%
  mutate(`Sub-Category` = fct_reorder(`Sub-Category`, Total))

p3a <- ggplot(subcat, aes(x = Total, y = `Sub-Category`, color = Category)) +
  geom_segment(aes(x = 0, xend = Total, yend = `Sub-Category`),
               linewidth = 1.2, alpha = 0.6) +
  geom_point(size = 4.5) +
  geom_text(aes(label = paste0("$", round(Total / 1000, 0), "K")),
            hjust = -0.3, size = 3, fontface = "bold") +
  scale_x_continuous(labels = dollar, expand = expansion(mult = c(0, 0.22))) +
  scale_color_manual(values = pal_cat) +
  labs(title    = "Total Sales per Sub-Kategori",
       subtitle = "Phones & Chairs menjadi sub-kategori terlaris",
       x = "Total Penjualan (USD)", y = NULL, color = "Kategori",
       caption = "Sumber: Superstore Sales Dataset 2015-2018") +
  theme_super()

print(p3a)

4.3.2 Heatmap Kategori × Region

heat <- data_clean %>%
  group_by(Region, Category) %>%
  summarise(Total = sum(Sales), .groups = "drop")

p3b <- ggplot(heat, aes(x = Category, y = Region, fill = Total)) +
  geom_tile(color = "white", linewidth = 2, width = 0.95, height = 0.95) +
  geom_text(aes(label = paste0("$", round(Total / 1000, 0), "K")),
            size = 4.5, fontface = "bold", color = "white") +
  scale_fill_viridis(option = "plasma", labels = dollar) +
  labs(title    = "Heatmap Sales: Kategori × Region",
       subtitle = "West unggul di Technology; East kuat di Furniture",
       x = "Kategori", y = "Region", fill = "Total Sales",
       caption = "Sumber: Superstore Sales Dataset 2015-2018") +
  theme_super() +
  theme(panel.grid = element_blank(),
        axis.text  = element_text(face = "bold", size = 11))

print(p3b)

4.3.3 Gabungan

vis3 <- (p3a | p3b) +
  plot_annotation(
    title = "VISUALISASI 3: BIVARIAT — Hubungan Antar Variabel",
    theme = theme(plot.title = element_text(face = "bold", size = 14,
                                            hjust = 0.5, color = "#1a1a2e"))
  )
print(vis3)

4.3.4 📝 Interpretasi

Temuan Utama Visualisasi Bivariat:

  • Phones ($328K) dan Chairs ($323K) adalah sub-kategori dengan total penjualan tertinggi, jauh melampaui sub-kategori lainnya.
  • Fasteners hanya menghasilkan $3K merupakan kandidat untuk evaluasi portofolio produk atau penghapusan dari katalog.
  • Heatmap menunjukkan perbedaan kekuatan regional yang jelas:
    • West mendominasi kategori Technology
    • East paling kuat di Furniture
    • South konsisten paling rendah di semua kategori mengindikasikan potensi pasar yang belum tergarap maksimal.

4.4 Visualisasi Time Series

Visualisasi time series digunakan untuk mengamati tren perubahan penjualan dari waktu ke waktu selama periode 2015–2018.

4.4.1 Tren Total Bulanan

data_time <- data_clean %>%
  group_by(year_month) %>%
  summarise(total_sales = sum(Sales), .groups = "drop")

p4a <- ggplot(data_time, aes(x = year_month, y = total_sales)) +
  geom_area(fill = "#264653", alpha = 0.2) +
  geom_line(color = "#264653", linewidth = 1.3) +
  geom_point(aes(color = total_sales), size = 2.5, show.legend = FALSE) +
  scale_color_viridis(option = "plasma") +
  scale_x_date(date_breaks = "6 months", date_labels = "%b '%y") +
  scale_y_continuous(labels = dollar) +
  annotate("rect",
           xmin = as.Date("2018-10-01"), xmax = as.Date("2018-12-31"),
           ymin = -Inf, ymax = Inf, alpha = 0.12, fill = "#e76f51") +
  annotate("text",
           x = as.Date("2018-08-15"), y = max(data_time$total_sales) * 0.88,
           label = "Puncak\nQ4 2018", size = 3.2,
           color = "#e76f51", fontface = "bold") +
  labs(title    = "Tren Total Penjualan Bulanan (2015–2018)",
       subtitle = "Lonjakan konsisten setiap Q3 (September) dan Q4 (November–Desember)",
       x = NULL, y = "Total Penjualan (USD)",
       caption = "Sumber: Superstore Sales Dataset 2015-2018") +
  theme_super()

print(p4a)

4.4.2 Tren per Kategori

data_time_cat <- data_clean %>%
  group_by(year_month, Category) %>%
  summarise(total_sales = sum(Sales), .groups = "drop")

p4b <- ggplot(data_time_cat,
              aes(x = year_month, y = total_sales,
                  color = Category, fill = Category)) +
  geom_area(alpha = 0.12, position = "identity") +
  geom_line(linewidth = 1.1) +
  scale_x_date(date_breaks = "1 year", date_labels = "%Y") +
  scale_y_continuous(labels = dollar) +
  scale_color_manual(values = pal_cat) +
  scale_fill_manual(values = pal_cat) +
  labs(title    = "Tren Sales per Kategori Produk (2015–2018)",
       subtitle = "Technology paling volatil; Office Supplies paling stabil",
       x = "Periode", y = "Total Penjualan (USD)",
       color = "Kategori", fill = "Kategori",
       caption = "Sumber: Superstore Sales Dataset 2015-2018") +
  theme_super()

print(p4b)

4.4.3 Gabungan

vis4 <- (p4a / p4b) +
  plot_annotation(
    title = "VISUALISASI 4: TIME SERIES — Tren Penjualan dari Waktu ke Waktu",
    theme = theme(plot.title = element_text(face = "bold", size = 14,
                                            hjust = 0.5, color = "#1a1a2e"))
  )
print(vis4)

4.4.4 📝 Interpretasi

Temuan Utama Visualisasi Time Series:

  • Terdapat pola musiman yang sangat kuat dan berulang setiap tahun: penjualan selalu melonjak pada bulan September (Q3) dan November–Desember (Q4) kemungkinan besar terkait musim back-to-school dan holiday season.
  • Technology adalah kategori paling fluktuatif dengan lonjakan tajam di akhir tahun, kemungkinan didorong oleh pembelian gadget dan elektronik menjelang hari raya.
  • Office Supplies memiliki tren paling stabil sepanjang tahun mencerminkan kebutuhan rutin yang tidak terlalu musiman.
  • Secara keseluruhan, tren revenue meningkat konsisten dari 2015 ke 2018, menandakan pertumbuhan bisnis yang sehat.

5 Analisis Lanjutan

5.1 Pertumbuhan Penjualan Tahunan (YoY)

yoy <- data_clean %>%
  group_by(year) %>%
  summarise(Total = sum(Sales)) %>%
  mutate(Growth = (Total / lag(Total) - 1))

p_bonus <- ggplot(yoy, aes(x = factor(year), y = Total)) +
  geom_col(fill = "#2a9d8f", width = 0.5) +
  geom_text(aes(label = dollar(round(Total, 0))),
            vjust = -0.5, size = 4, fontface = "bold", color = "#1a1a2e") +
  geom_text(data = filter(yoy, !is.na(Growth)),
            aes(label = paste0("▲ ", percent(Growth, accuracy = 1))),
            vjust = -2.3, size = 3.8, fontface = "bold", color = "#e76f51") +
  scale_y_continuous(labels = dollar, expand = expansion(mult = c(0, 0.22))) +
  labs(title    = "Total Penjualan Tahunan & Pertumbuhan YoY",
       subtitle = "Pertumbuhan konsisten setiap tahun | Tertinggi pada 2017 (+28%)",
       x = "Tahun", y = "Total Penjualan (USD)",
       caption = "YoY = Year-over-Year Growth | Sumber: Superstore Sales Dataset") +
  theme_super()

print(p_bonus)

5.2 Tabel Ringkasan YoY

Tahun Total Penjualan Pertumbuhan YoY
2015 $479,331
2016 $454,316 -5%
2017 $596,525 31%
2018 $720,509 21%

6 Kesimpulan & Insight

Berikut ringkasan temuan utama dari seluruh analisis:

No Aspek Temuan
1 Kategori Produk Technology ($827K) menghasilkan revenue tertinggi, meski jumlah transaksinya paling sedikit
2 Segmen Pelanggan Consumer mendominasi 51% revenue, diikuti Corporate (31%) dan Home Office (19%)
3 Metode Pengiriman 59.8% order menggunakan Standard Class; Same Day hanya 5.5% peluang layanan ekspres
4 Pola Musiman Lonjakan penjualan konsisten terjadi di Q3 (September) dan Q4 (November–Desember)
5 Pertumbuhan YoY +26% (2016), +28% (2017), +20% (2018) bisnis tumbuh sehat meski mulai melambat
6 Sub-Kategori Phones ($328K) dan Chairs ($323K) terlaris; Fasteners ($3K) terendah
7 Regional West ($710K) dan East ($670K) terkuat; South ($389K) paling rendah potensi ekspansi