1. Đọc dữ liệu, chuẩn bị

Trước tiên, chúng ta sẽ đọc tập dữ liệu giao dịch từ siêu thị và tiến hành kiểm tra sơ bộ để hiểu rõ cấu trúc và các biến có trong dữ liệu. Đây là bước quan trọng để đảm bảo các biến được định dạng đúng trước khi phân tích. File chứa 893 giao dịch (có một số dòng không đầy đủ ở cuối). Mỗi giao dịch ghi lại thông tin về ngày mua, khách hàng, sản phẩm, số lượng và doanh thu

Các biến chính

  • PurchaseDate: Ngày mua hàng
  • CustomerID: ID khách hàng
  • Gender: Giới tính (M/F)
  • MaritalStatus: Tình trạng hôn nhân (S/M)
  • Homeowner: Có nhà riêng (Y/N)
  • Children: Số con
  • AnnualIncome: Thu nhập hàng năm (phân khoảng)
  • City, StateorProvince, Country: Địa chỉ
  • ProductFamily, ProductDepartment, ProductCategory: Phân loại sản phẩm
  • UnitsSold: Số lượng bán
  • Revenue: Doanh thu

2. Thống kê doanh thu

Phân tích tổng quan doanh thu giúp ta hình dung được quy mô hoạt động kinh doanh của siêu thị. Các chỉ số như trung bình, trung vị, giá trị tối thiểu và tối đa sẽ hỗ trợ phát hiện phân phối và sự chênh lệch giữa các giao dịch.

revenue_stats <- data %>%
  summarise(
    Tổng_doanh_thu = sum(Revenue, na.rm = TRUE),
    Doanh_thu_trung_bình = mean(Revenue, na.rm = TRUE),
    Doanh_thu_trung_vị = median(Revenue, na.rm = TRUE),
    Doanh_thu_tối_thiểu = min(Revenue, na.rm = TRUE),
    Doanh_thu_tối_đa = max(Revenue, na.rm = TRUE),
    Số_giao_dịch = n()
  ) %>%
  mutate_all(~round(., 2))
# note các ý giải thích trong đoạn code này
# %>%: Pipe operator — giúp nối mạch đọc logic từ trái sang phải.
# summarise(): Tạo ra 1 hàng duy nhất, tính toán tổng, trung bình, v.v.
# na.rm = TRUE: Bỏ qua giá trị NA (nếu có) để tránh lỗi.
# n(): Đếm số dòng (số giao dịch).
# mutate_all(): Làm tròn toàn bộ các cột kết quả đến 2 chữ số thập phân.
# min, max: Cho biết phạm vi doanh thu.
# n(): Tổng số giao dịch (số dòng dữ liệu).
# mutate_all(~round(...)): Làm tròn số dễ đọc.

kable(revenue_stats, caption = "Thống kê doanh thu") %>%
  kable_styling(bootstrap_options = "striped", full_width = FALSE)
Thống kê doanh thu
Tổng_doanh_thu Doanh_thu_trung_bình Doanh_thu_trung_vị Doanh_thu_tối_thiểu Doanh_thu_tối_đa Số_giao_dịch
182830.4 13 11.25 0.53 56.7 14059

3. Biểu đồ phân phối doanh thu

Biểu đồ histogram sau đây sẽ cho thấy sự phân phối của doanh thu theo từng giao dịch. Điều này giúp chúng ta xác định các giao dịch có giá trị bất thường hoặc hiểu rõ mức doanh thu phổ biến.

ggplot(data, aes(x = Revenue)) +
  geom_histogram(bins = 30, fill = "steelblue", color = "white") +
  labs(title = "Phân phối doanh thu mỗi giao dịch",
       x = "Doanh thu", y = "Số giao dịch") +
  theme_minimal()

# note các ý giải thích
# ggplot(): Khởi tạo biểu đồ với dữ liệu data, ánh xạ biến Revenue vào trục x.
# geom_histogram(): Tạo histogram – vẽ số lượng giao dịch trong từng khoảng doanh thu.
# bins = 30: Chia trục x thành 30 khoảng.
# fill, color: Màu của cột và viền.
# labs(): Thêm tiêu đề, tên trục.
# theme_minimal(): Theme đơn giản, nhẹ.

4. Phân tích theo giới tính

Việc phân tích hành vi mua sắm theo giới tính có thể mang lại nhiều góc nhìn thú vị. Liệu có sự khác biệt rõ rệt nào giữa nam và nữ về số lượng giao dịch hoặc giá trị trung bình không?

gender_stats <- data %>%
  group_by(Gender) %>%
  summarise(
    Số_giao_dịch = n(),
    Tổng_doanh_thu = sum(Revenue, na.rm = TRUE),
    Doanh_thu_trung_bình = mean(Revenue, na.rm = TRUE)
  ) %>%
  mutate(Tỷ_lệ = percent(Số_giao_dịch / sum(Số_giao_dịch)))

kable(gender_stats, caption = "Thống kê theo giới tính") %>%
  kable_styling(bootstrap_options = "striped", full_width = FALSE)
Thống kê theo giới tính
Gender Số_giao_dịch Tổng_doanh_thu Doanh_thu_trung_bình Tỷ_lệ
F 7170 93525.73 13.04403 51%
M 6889 89304.70 12.96338 49%
# note các ý giải thích code
# group_by(Gender): Nhóm dữ liệu theo giới tính (Male, Female) để phân tích riêng từng nhóm.
# summarise(...): Tính toán các thống kê:
# n() đếm số dòng, tức số lượng giao dịch.
# sum(Revenue) tính tổng doanh thu cho mỗi giới tính.
# mean(Revenue) lấy trung bình doanh thu mỗi giao dịch.
# na.rm = TRUE: Bỏ qua các giá trị bị thiếu (NA) trong tính toán.
# mutate(Tỷ_lệ = ...): Thêm cột tỷ lệ phần trăm số giao dịch so với tổng.
# percent(...): Chuyển sang định dạng phần trăm (ví dụ 0.45 → 45%)

5. Phân tích theo nhân khẩu học

Ở phần này, chúng ta đi sâu vào phân tích các đặc điểm nhân khẩu học như tình trạng hôn nhân, sở hữu nhà, thu nhập và giới tính. Những yếu tố này thường có ảnh hưởng đến hành vi tiêu dùng và lựa chọn sản phẩm.

data <- data %>%
  mutate(
    IncomeGroup = factor(AnnualIncome, 
                         levels = c("$10K - $30K", "$30K - $50K", "$50K - $70K", 
                                    "$70K - $90K", "$90K - $110K", "$110K - $130K",
                                    "$130K - $150K", "$150K +"),
                         ordered = TRUE)
  )
# note các ý giải thích
# mutate(IncomeGroup = factor(...)): Tạo biến phân loại IncomeGroup từ AnnualIncome.
# ordered = TRUE: Sắp xếp theo thứ tự tăng dần để dùng cho biểu đồ sau này (trục x có logic).


#Phân tích nhân khẩu học
demographic_analysis <- data %>%
  group_by(Gender, MaritalStatus, Homeowner, IncomeGroup) %>%
  summarise(Count = n(), Revenue = sum(Revenue), .groups = "keep") %>%
  group_by(MaritalStatus, Homeowner) %>%
  mutate(Pct = Count / sum(Count))
# note các ý giải thích
# group_by(...): Nhóm theo tổ hợp các đặc điểm nhân khẩu học.
# summarise(...): Đếm số giao dịch và tính tổng doanh thu cho từng nhóm.
# .groups = "keep": Giữ lại cấu trúc nhóm để tiếp tục xử lý.
# mutate(Pct = ...): Tính tỷ lệ phần trăm mỗi nhóm trong tổng số theo từng MaritalStatus và Homeowner.


# Biểu đồ phân bố nhân khẩu học

ggplot(demographic_analysis, aes(x = IncomeGroup, y = Pct, fill = Gender)) +
  geom_col(position = "dodge") +
  facet_grid(Homeowner ~ MaritalStatus) +
  scale_y_continuous(labels = percent_format(scale = 100)) +
  labs(
    title = "Phân bố nhân khẩu học theo nhóm thu nhập",
    x = "Nhóm thu nhập",
    y = "Tỷ lệ (%)",
    fill = "Giới tính"
  ) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

# note các ý giải thích
# ggplot(...): Vẽ biểu đồ tỷ lệ phần trăm theo nhóm thu nhập.
# geom_col(position = "dodge"): Dùng cột ghép cạnh nhau để so sánh theo giới tính.
# facet_grid(...): Tạo nhiều biểu đồ nhỏ theo từng nhóm hôn nhân và sở hữu nhà.
# percent_format(scale = 100): Hiển thị y-axis dưới dạng phần trăm.
# angle = 45: Xoay nhãn trục x cho dễ đọc.

6. Phân tích theo thời gian

Dữ liệu giao dịch sẽ được phân chia theo các mùa trong năm và các dịp lễ cuối năm. Mục tiêu là tìm hiểu xem thời gian có ảnh hưởng đến doanh thu và mức độ tiêu dùng của khách hàng hay không.

data <- data %>%
  mutate(PurchaseDate = as.Date(PurchaseDate))

data <- data %>%
  mutate(
    Season = case_when(
      month(PurchaseDate) %in% 3:5 ~ "Spring",
      month(PurchaseDate) %in% 6:8 ~ "summer",
      month(PurchaseDate) %in% 9:11 ~ "Autumn",
      TRUE ~ "Winter"
    ),
    IsHoliday = ifelse(month(PurchaseDate) == 12 & day(PurchaseDate) >= 15, "Holiday", "Normal")
  )
# note các ý giải thích
# case_when(...): Gán mùa theo tháng (Spring = 3–5, Summer = 6–8,...).
# ifelse(...): Xác định giao dịch vào dịp lễ (cuối tháng 12) và gán nhãn Holiday hay Normal.


# Tổng hợp doanh thu theo mùa và ngày lễ

seasonal_analysis <- data %>%
  group_by(Season, IsHoliday, ProductFamily) %>%
  summarise(Revenue = sum(Revenue), .groups = "drop")

# Biểu đồ doanh thu theo mùa và ngày lễ
ggplot(seasonal_analysis, aes(x = Season, y = Revenue, fill = IsHoliday)) +
  geom_col(position = "dodge") +
  facet_wrap(~ ProductFamily, scales = "free_y") +
  labs(title = "Revenue by Season and Holiday",
       y = "Revenue", x = "Season") +
  theme_minimal()

# note các ý giải thích
# facet_wrap(...): Tách biểu đồ cho từng dòng sản phẩm (ProductFamily).
# scales = "free_y": Cho phép từng biểu đồ có trục y riêng để dễ so sánh.