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
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)
| 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 |
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ẹ.
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)
| 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%)
Ở 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.
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.