Bộ dữ liệu “Supermarket Transactions” phản ánh chi
tiết các giao dịch mua sắm được thực hiện tại một siêu thị trong một
khoảng thời gian cụ thể. Mỗi quan sát trong dữ liệu tương ứng với một
giao dịch duy nhất, bao gồm thông tin liên quan đến khách hàng (giới
tính, loại thành viên, chi nhánh), sản phẩm (loại sản phẩm, phương thức
thanh toán), cũng như hành vi tiêu dùng (thời điểm giao dịch, mức độ hài
lòng, tổng giá trị đơn hàng,…). Vì dữ liệu được lưu trữ dưới định dạng
CSV, quá trình xử lý bắt đầu bằng việc sử dụng hàm
read.csv() trong R để nhập dữ liệu vào môi trường làm việc.
Sau khi đọc thành công, tập dữ liệu được lưu trong đối tượng
data và được sử dụng cho các bước phân tích tiếp theo.
library(readr)
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(skimr)
library(psych)
library(csv)
library(DT)
library(ggplot2)
##
## Attaching package: 'ggplot2'
## The following objects are masked from 'package:psych':
##
## %+%, alpha
library(forcats)
library(knitr)
library(tibble)
data <- read.csv("F:/PTDLDT/Supermarket Transactions.csv", header = T)
datatable(data)
## Warning in instance$preRenderHook(instance): It seems your data is too big for
## client-side DataTables. You may consider server-side processing:
## https://rstudio.github.io/DT/server.html
Để có cái nhìn tổng quan về dữ liệu, ta sử dụng hàm
str(). Lệnh này sẽ hiển thị số dòng và số cột của bảng dữ
liệu, tên các biến, kiểu dữ liệu tương ứng của từng biến (chẳng hạn như
số, chuỗi ký tự, hay nhân tố), cùng với một vài giá trị đầu tiên của mỗi
biến, giúp ta nhanh chóng nắm bắt được cấu trúc và đặc điểm cơ bản của
bộ dữ liệu.
str(data)
## 'data.frame': 14059 obs. of 16 variables:
## $ X : int 1 2 3 4 5 6 7 8 9 10 ...
## $ PurchaseDate : chr "2007-12-18" "2007-12-20" "2007-12-21" "2007-12-21" ...
## $ CustomerID : int 7223 7841 8374 9619 1900 6696 9673 354 1293 7938 ...
## $ Gender : chr "F" "M" "F" "M" ...
## $ MaritalStatus : chr "S" "M" "M" "M" ...
## $ Homeowner : chr "Y" "Y" "N" "Y" ...
## $ Children : int 2 5 2 3 3 3 2 2 3 1 ...
## $ AnnualIncome : chr "$30K - $50K" "$70K - $90K" "$50K - $70K" "$30K - $50K" ...
## $ City : chr "Los Angeles" "Los Angeles" "Bremerton" "Portland" ...
## $ StateorProvince : chr "CA" "CA" "WA" "OR" ...
## $ Country : chr "USA" "USA" "USA" "USA" ...
## $ ProductFamily : chr "Food" "Food" "Food" "Food" ...
## $ ProductDepartment: chr "Snack Foods" "Produce" "Snack Foods" "Snacks" ...
## $ ProductCategory : chr "Snack Foods" "Vegetables" "Snack Foods" "Candy" ...
## $ UnitsSold : int 5 5 3 4 4 3 4 6 1 2 ...
## $ Revenue : num 27.38 14.9 5.52 4.44 14 ...
Dựa trên kết quả hiển thị từ hàm str(), ta nhận thấy
rằng tập dữ liệu bao gồm 14.059 quan sát (observations)
và 16 biến (variables). Cấu trúc dữ liệu thể hiện rõ
tên từng biến, kiểu dữ liệu tương ứng,
cùng với một số giá trị mẫu minh họa cho mỗi biến. Phân
tích này hướng đến việc khám phá đặc điểm khách hàng và hành vi
tiêu dùng thông qua các kỹ thuật thống kê mô
tả và thống kê suy luận.
Trọng tâm của phân tích là làm rõ vai trò của các biến định tính trong việc phản ánh thông tin về khách hàng. Một số biến đáng chú ý bao gồm:
Gender: Phân loại theo giới tính
(F – Nữ, M – Nam)
MaritalStatus: Tình trạng hôn nhân
(S – Độc thân, M – Đã kết hôn)
Homeowner: Khách hàng có sở hữu nhà hay không
(Y – Có, N – Không)
AnnualIncome: Mức thu nhập hàng năm (được nhóm theo
khoảng)
City, StateorProvince,
Country: Thông tin vị trí địa lý
ProductFamily, ProductDepartment,
ProductCategory: Nhóm danh mục sản phẩm mà khách hàng đã
mua
Trong quá trình xử lý dữ liệu với R, việc kiểm tra trực tiếp một số
dòng đầu và cuối của tập dữ liệu là bước quan trọng để đảm bảo dữ liệu
được nhập đúng cách và không gặp lỗi định dạng. Hai hàm thường được sử
dụng cho mục đích này là head() và tail().
Việc áp dụng hai hàm này giúp:
Xác minh dữ liệu đã đọc vào có đúng định dạng mong muốn (ví dụ: ngày tháng, kiểu số, chuỗi ký tự,…).
Đánh giá sơ bộ mức độ đầy đủ và tính nhất quán của dữ liệu từ đầu đến cuối tập tin.
Phát hiện sớm các vấn đề thường gặp như dòng trống, dữ liệu sai vị trí, hoặc bị thiếu thông tin do lỗi khi nhập file.
head(data,7)
| X | PurchaseDate | CustomerID | Gender | MaritalStatus | Homeowner | Children | AnnualIncome | City | StateorProvince | Country | ProductFamily | ProductDepartment | ProductCategory | UnitsSold | Revenue |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 2007-12-18 | 7223 | F | S | Y | 2 | $30K - $50K | Los Angeles | CA | USA | Food | Snack Foods | Snack Foods | 5 | 27.38 |
| 2 | 2007-12-20 | 7841 | M | M | Y | 5 | $70K - $90K | Los Angeles | CA | USA | Food | Produce | Vegetables | 5 | 14.90 |
| 3 | 2007-12-21 | 8374 | F | M | N | 2 | $50K - $70K | Bremerton | WA | USA | Food | Snack Foods | Snack Foods | 3 | 5.52 |
| 4 | 2007-12-21 | 9619 | M | M | Y | 3 | $30K - $50K | Portland | OR | USA | Food | Snacks | Candy | 4 | 4.44 |
| 5 | 2007-12-22 | 1900 | F | S | Y | 3 | $130K - $150K | Beverly Hills | CA | USA | Drink | Beverages | Carbonated Beverages | 4 | 14.00 |
| 6 | 2007-12-22 | 6696 | F | M | Y | 3 | $10K - $30K | Beverly Hills | CA | USA | Food | Deli | Side Dishes | 3 | 4.37 |
| 7 | 2007-12-23 | 9673 | M | S | Y | 2 | $30K - $50K | Salem | OR | USA | Food | Frozen Foods | Breakfast Foods | 4 | 13.78 |
Lệnh head(data,7) dùng để hiển thị 7 dòng đầu tiên của
bộ dữ liệu, cụ thể là từ dòng 1 đến dòng 7 theo mặc định. Nếu ta muốn
xem số lượng dòng khác, chỉ cần thay đổi tham số số trong dấu ngoặc là
được.
Ý nghĩa của việc sử dụng lệnh này bao gồm:
Quan sát cấu trúc ban đầu của dữ liệu, bao gồm định dạng và kiểu của từng biến.
Đánh giá tính hợp lý của dữ liệu ở phần đầu bảng, kiểm tra xem dữ liệu nhập liệu có đúng hay không.
Giúp xác nhận xem dữ liệu đã được đọc vào đúng định dạng mong muốn, chẳng hạn như định dạng ngày tháng, kiểu ký tự hay số liệu.
tail(data,7)
| X | PurchaseDate | CustomerID | Gender | MaritalStatus | Homeowner | Children | AnnualIncome | City | StateorProvince | Country | ProductFamily | ProductDepartment | ProductCategory | UnitsSold | Revenue | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 14053 | 14053 | 2009-12-28 | 7234 | F | M | Y | 4 | $30K - $50K | Spokane | WA | USA | Food | Deli | Side Dishes | 5 | 6.80 |
| 14054 | 14054 | 2009-12-29 | 2032 | F | M | N | 3 | $10K - $30K | Yakima | WA | USA | Non-Consumable | Household | Paper Products | 5 | 14.50 |
| 14055 | 14055 | 2009-12-29 | 9102 | F | M | Y | 2 | $10K - $30K | Bremerton | WA | USA | Food | Baking Goods | Baking Goods | 3 | 9.64 |
| 14056 | 14056 | 2009-12-29 | 4822 | F | M | Y | 3 | $10K - $30K | Walla Walla | WA | USA | Food | Frozen Foods | Vegetables | 3 | 7.45 |
| 14057 | 14057 | 2009-12-31 | 250 | M | S | Y | 1 | $30K - $50K | Portland | OR | USA | Drink | Beverages | Pure Juice Beverages | 4 | 3.24 |
| 14058 | 14058 | 2009-12-31 | 6153 | F | S | N | 4 | $50K - $70K | Spokane | WA | USA | Drink | Dairy | Dairy | 2 | 4.00 |
| 14059 | 14059 | 2009-12-31 | 3656 | M | S | N | 3 | $50K - $70K | Portland | OR | USA | Non-Consumable | Household | Electrical | 5 | 25.53 |
Để kiểm tra phần cuối của bộ dữ liệu, ta sử dụng lệnh tail(data, 7), lệnh này sẽ hiển thị 7 dòng cuối cùng theo mặc định. Tương tự như head(),ta có thể thay đổi số lượng dòng muốn xem bằng cách điều chỉnh tham số số trong dấu ngoặc.
Mục đích chính của việc sử dụng lệnh này là:
Quan sát dữ liệu ở cuối bảng để phát hiện những vấn đề như dòng trống, dữ liệu bị cắt xén hoặc lỗi nhập liệu cuối tập tin.
Đánh giá tính nhất quán và đầy đủ của dữ liệu ở phần cuối.
Xác nhận rằng dữ liệu được đọc đúng định dạng và không bị mất mát thông tin khi nhập vào R.
Để đánh giá độ hoàn chỉnh của bộ dữ liệu, đặc biệt là phát hiện các giá trị bị thiếu (NA), ta có thể sử dụng một số lệnh phổ biến trong R như sau:
sum(is.na(data))
## [1] 0
Hàm is.na() sẽ trả về một vector với giá trị TRUE
tại vị trí có dữ liệu bị thiếu và FALSE tại vị trí không bị
thiếu.
Khi dùng sum(is.na(data)), các giá trị TRUE được
tính là 1, FALSE là 0, do đó kết quả là tổng số phần tử bị thiếu trong
toàn bộ dữ liệu.
Nếu kết quả trả về là 0, có nghĩa dữ liệu không có giá trị thiếu.
Để xác định vị trí các phần tử bị thiếu trong dữ liệu, ta dùng:
which(is.na(data))
## integer(0)
Nếu kết quả là integer(0), tức không tìm thấy phần tử nào bị thiếu.
Ngược lại, kết quả sẽ liệt kê các chỉ số của các giá trị bị thiếu.
Trong quá trình phân tích dữ liệu, việc xác định đúng kiểu dữ liệu
cho từng biến đóng vai trò rất quan trọng, đặc biệt là khi xử lý các
biến định tính. Dựa trên kết quả từ hàm str(), có thể thấy
các biến như Gender, MaritalStatus,
Homeowner, AnnualIncome, City,
StateorProvince, Country,
ProductFamily, ProductDepartment, và
ProductCategory hiện đang được lưu dưới dạng character
(chr).
Tuy nhiên, về bản chất, những biến này đại diện cho các nhóm phân loại (categorical variables) – tức là chúng có số lượng giá trị cố định và lặp lại, phù hợp với kiểu dữ liệu factor trong R. Nếu không chuyển đổi đúng kiểu, quá trình phân tích mô tả, trực quan hóa hoặc mô hình hóa có thể gặp sai lệch hoặc thiếu hiệu quả.
Do đó, để phục vụ tốt hơn cho các thao tác thống kê và trực quan sau
này, ta tiến hành chuyển đổi những biến định tính nói trên sang kiểu
factor bằng lệnh as.factor().
cols_to_factor <- c("Gender", "MaritalStatus", "Homeowner", "AnnualIncome",
"City", "StateorProvince", "Country",
"ProductFamily", "ProductDepartment", "ProductCategory")
dl <- lapply(data[cols_to_factor], as.factor)
c(): giúp ta tạo vector và vector này chứa danh sách
tên các biến định tính cần chuyển đổi, sau đó gán vào
cols_to_factor.
as.factor: là hàm dùng để chuyển kiểu dữ liệu của
một biến từ dạng ký tự (character) sang dạng phân loại (factor), giúp R
hiểu rằng biến đó là biến định tính.
data[cols_to_factor]: dùng để trích xuất các cột
trong dữ liệu data có tên nằm trong vector cols_to_factor, tức
là chỉ lấy những biến định tính cần xử lý.
lapply(): dùng để áp dụng hàm as.factor
cho từng cột trong tập con dữ liệu vừa trích xuất, sau đó gán vào
dl.
Mục đích của toàn bộ câu lệnh là chuẩn hóa kiểu dữ liệu cho các biến phân loại, đảm bảo dữ liệu đúng định dạng để thực hiện phân tích định tính chính xác hơn.
Để thực hiện thống kê mô tả cho các biến phân loại, ta thường bắt đầu bằng việc tạo bảng tần số và bảng tần suất. Trong R, điều này có thể thực hiện dễ dàng thông qua một số hàm cơ bản.
Bảng tần số thể hiện số lần xuất hiện (số lượng) của từng nhóm
trong một biến phân loại. Để tạo bảng này, ta sử dụng hàm
table() như sau:
table(du_lieu$ten_bien).
Bảng tần suất tương đối phản ánh tỷ lệ mà mỗi nhóm chiếm trong
tổng thể dữ liệu. Có thể tạo bảng này bằng cách lấy kết quả từ
table() chia cho tổng số quan sát, được tính bằng
nrow(). Vd:
table(du_lieu$bien) / sum(nrow(du_lieu))
Trong đó:
nrow(du_lieu) trả về số dòng trong tập dữ liệu, tương ứng với tổng số quan sát.
Dấu $ được dùng để truy cập một biến cụ thể (cột)
trong một data frame. Vd: du_lieu$ten_bien nghĩa là gọi
biến ten_bien trong bảng dữ liệu
du_lieu.
Bảng tần số
table(dl$Gender)
##
## F M
## 7170 6889
Bảng tần suất
table(dl$Gender)/sum(nrow(data))
##
## F M
## 0.5099936 0.4900064
Biểu đồ
data %>%
group_by(Gender) %>%
summarise(n = n(), .groups = "drop") %>%
mutate(percent = n / sum(n) * 100) %>%
ggplot(aes(x = "", y = percent, fill = Gender)) +
geom_col(color = "black") +
coord_polar(theta = "y") +
geom_text(aes(label = paste0(round(percent, 1), "%")),
position = position_stack(vjust = 0.5)) +
labs(caption = "Biểu đồ tròn thể hiện cơ cấu giới tính") +
theme_void()
Giải thích câu lệnh:
data %>%: sử dụng toán tử pipe
(%>%) để truyền dữ liệu data qua từng bước xử
lý kế tiếp một cách mạch lạc, dễ đọc.
group_by(Gender): nhóm dữ liệu theo biến
Gender, tức là tách dữ liệu thành từng nhóm dựa trên giới
tính.
summarise(n = n(), .groups = "drop"): tính tổng số
dòng (số lượng quan sát) cho từng nhóm giới tính, kết quả được gán vào
biến n. Tham số .groups = "drop" dùng để bỏ thông
tin nhóm sau khi tính tổng, tránh cảnh báo không cần thiết.
mutate(percent = n / sum(n) * 100): tạo thêm một
biến mới tên là percent, tính tỷ lệ phần trăm của từng nhóm
giới tính so với tổng số dòng.
ggplot(aes(x = "", y = percent, fill = Gender)):
khởi tạo biểu đồ với trục hoành (x) là chuỗi rỗng (để tạo
biểu đồ tròn), trục tung (y) là phần trăm, màu sắc được
chia theo Gender.
geom_col(color = "black"): tạo biểu đồ cột (sẽ thành
dạng tròn nhờ coord_polar()), với viền cột có màu
đen.
coord_polar(theta = "y"): chuyển biểu đồ cột thành
biểu đồ tròn bằng cách sử dụng hệ tọa độ cực.
geom_text(aes(label = paste0(round(percent, 1), "%"))):
hiển thị nhãn phần trăm bên trong biểu đồ. Sử dụng paste0()
để thêm dấu % và round(..., 1) để làm tròn đến
1 chữ số thập phân.
labs(caption = "Biểu đồ tròn thể hiện cơ cấu giới tính"):
thêm chú thích bên dưới biểu đồ.
theme_void(): loại bỏ các yếu tố không cần thiết như
trục, nhãn, lưới để làm cho biểu đồ tròn gọn gàng và dễ nhìn
hơn.
Nhận xét: Kết quả phân tích từ bảng tần suất và biểu đồ cho thấy, trong tổng số 14.059 giao dịch, số lượng giao dịch do khách hàng nữ (F) thực hiện là 7.170, trong khi khách hàng nam (M) thực hiện 6.889 giao dịch. Như vậy, khách hàng nữ chiếm tỷ trọng lớn hơn một chút so với khách hàng nam trong toàn bộ tập dữ liệu. Cụ thể, tỷ lệ giao dịch từ khách hàng nữ chiếm khoảng 50.9993598%,** trong khi giao dịch từ khách hàng nam chiếm khoảng 49.0006402%. Mặc dù sự khác biệt không quá lớn, nhưng vẫn cho thấy một mức độ chênh lệch nhẹ, cho thấy nữ giới có xu hướng mua sắm tại siêu thị cao hơn so với nam giới trong giai đoạn khảo sát.
Bảng tần số
table(dl$MaritalStatus)
##
## M S
## 6866 7193
Bảng tần suất
table(dl$MaritalStatus)/sum(nrow(data))
##
## M S
## 0.4883704 0.5116296
Biểu đồ:
data %>%
group_by(MaritalStatus) %>%
summarise(n = n(), .groups = "drop") %>%
mutate(percent = n / sum(n) * 100) %>%
ggplot(aes(x = "", y = percent, fill = MaritalStatus)) +
geom_col(color = "black") +
coord_polar(theta = "y") +
geom_text(aes(label = paste0(round(percent, 1), "%")),
position = position_stack(vjust = 0.5)) +
labs(caption = "Biểu đồ tròn thể hiện tỷ lệ kết hôn") +
theme_void()
Nhận xét: Kết quả phân tích cho thấy rằng, trong tổng số 14.059 giao dịch được ghi nhận tại siêu thị, có 6.866 giao dịch đến từ nhóm khách hàng đã kết hôn, trong khi 7.193 giao dịch được thực hiện bởi khách hàng chưa kết hôn. Điều này cho thấy số lượng giao dịch của nhóm chưa kết hôn nhỉnh hơn đôi chút so với nhóm đã kết hôn trong tập dữ liệu khảo sát. Dựa trên kết quả tính toán và biểu đồ tròn trực quan, tỷ lệ giao dịch từ khách hàng chưa kết hôn chiếm khoảng 51.1629561%, trong khi nhóm đã kết hôn chiếm khoảng 48.8370439%. Sự khác biệt này phần nào phản ánh xu hướng tiêu dùng tích cực hơn của nhóm khách hàng chưa kết hôn trong giai đoạn khảo sát.
Bảng tần số
table(data$Homeowner)
##
## N Y
## 5615 8444
Bảng tần suất
table(data$Homeowner)/sum(nrow(data))
##
## N Y
## 0.3993883 0.6006117
Biểu đồ:
data %>% group_by(Homeowner) %>% summarise(n = n()) %>%
ggplot(aes(x = '', y = n,fill = Homeowner)) +
geom_col(color = 'black') +
coord_polar('y') +
geom_text(aes(x = 1.3, label = n),position = position_stack(vjust = .5)) +
labs(caption = "Biểu đồ tròn về tần số sở hữu nhà") +
theme_void()
Giải thích câu lệnh:
data %>%: sử dụng toán tử pipe
(%>%) để truyền dữ liệu data lần lượt qua các
bước xử lý tiếp theo.
group_by(Homeowner): nhóm dữ liệu theo biến
Homeowner, tức là phân chia dữ liệu thành từng nhóm dựa trên
quyền sở hữu nhà (ví dụ: “Yes” hoặc “No”).
summarise(n = n()): tính số lượng quan sát trong
từng nhóm Homeowner và lưu kết quả vào biến mới tên là
n.
ggplot(aes(x = '', y = n, fill = Homeowner)): khởi
tạo biểu đồ. Trục x đặt là chuỗi rỗng để phục vụ mục tiêu tạo biểu đồ
tròn, trục y là số lượng (n), và màu sắc đại diện cho từng nhóm
Homeowner.
geom_col(color = 'black'): tạo biểu đồ cột với viền
màu đen. Các cột sẽ được chuyển thành lát trong biểu đồ tròn nhờ
coord_polar().
coord_polar('y'): chuyển đổi biểu đồ cột thành biểu
đồ tròn bằng hệ tọa độ cực, với trục góc (theta) là trục
y.
geom_text(aes(x = 1.3, label = n), position = position_stack(vjust = .5)):
chèn nhãn hiển thị số lượng (n) vào giữa các lát của biểu đồ
tròn. Giá trị x = 1.3 giúp đẩy nhãn ra phía ngoài một chút
để dễ quan sát.
labs(caption = "Biểu đồ tròn về tần số sở hữu nhà"):
thêm chú thích bên dưới biểu đồ để mô tả nội dung biểu diễn.
theme_void(): loại bỏ các yếu tố không cần thiết như
trục, nhãn trục, và lưới nhằm làm biểu đồ gọn gàng, tập trung vào nội
dung chính.
Nhận xét: Khoảng 60% khách hàng trong dữ liệu là người sở hữu nhà, cho thấy phần lớn khách hàng thuộc nhóm có nền tảng tài chính ổn định. Điều này có thể gợi ý rằng họ có xu hướng chi tiêu cao hơn hoặc sẵn sàng đầu tư vào các sản phẩm/dịch vụ có giá trị lớn hơn so với nhóm chưa sở hữu nhà. Đây là một yếu tố quan trọng để cân nhắc trong các chiến lược định giá và phân khúc thị trường.
Bảng tần số
table(dl$AnnualIncome)
##
## $10K - $30K $110K - $130K $130K - $150K $150K + $30K - $50K
## 3090 643 760 273 4601
## $50K - $70K $70K - $90K $90K - $110K
## 2370 1709 613
Bảng tần suất
table(dl$AnnualIncome)/sum(nrow(data))
##
## $10K - $30K $110K - $130K $130K - $150K $150K + $30K - $50K
## 0.21978804 0.04573583 0.05405790 0.01941817 0.32726367
## $50K - $70K $70K - $90K $90K - $110K
## 0.16857529 0.12155914 0.04360196
Biểu đồ
data %>%
ggplot(aes(x = AnnualIncome)) +
geom_bar(fill = "#5DADE2") +
labs(title = "Biểu đồ cột thể hiện tần số các mức thu nhập hằng năm ", x = "Mức thu nhập hằng năm", y = "Tần số") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Giải thích câu lệnh:
data %>%: sử dụng toán tử pipe
(%>%) để truyền dữ liệu data vào hàm
ggplot() ở bước tiếp theo.
ggplot(aes(x = AnnualIncome)): khởi tạo biểu đồ với
trục hoành (x) là biến AnnualIncome. Vì chỉ có
trục x, ggplot() sẽ mặc định vẽ biểu đồ cột
với trục tung là tần số (số lần xuất hiện của mỗi giá
trị).
geom_bar(fill = "#5DADE2"): tạo biểu đồ cột đơn
giản, với màu cột là xanh (#5DADE2). Mỗi cột thể hiện tần số xuất hiện
của một mức thu nhập trong dữ liệu.
labs(...): thêm tiêu đề và nhãn cho trục:
title: tiêu đề biểu đồ.x: tên trục hoành là “Mức thu nhập hằng
năm”.y: tên trục tung là “Tần số”.theme_minimal(): áp dụng một giao diện trực quan đơn
giản, loại bỏ chi tiết không cần thiết như đường viền, lưới nền rối
mắt.
theme(axis.text.x = element_text(angle = 45, hjust = 1)):
xoay nhãn trục hoành (các mức thu nhập) một góc 45 độ để tránh bị chồng
chữ nếu có quá nhiều mức thu nhập, đồng thời căn lề phải
(hjust = 1) để dễ đọc hơn.
Nhận xét: Phần lớn khách hàng (gần 50%) tập trung trong nhóm thu nhập trung bình từ 30K đến 70K, cho thấy đây là phân khúc chủ đạo và có tiềm năng chi tiêu ổn định. Điều này gợi ý rằng siêu thị nên tập trung phát triển các dòng sản phẩm phổ thông, có mức giá hợp lý, cùng với chương trình khuyến mãi và ưu đãi phù hợp với khả năng chi tiêu của nhóm này. Bên cạnh đó, đây cũng là nhóm có thể dễ dàng chuyển đổi thành khách hàng thân thiết thông qua chính sách chăm sóc và tích điểm mua sắm hiệu quả.
Bảng tần số
table(dl$City)
##
## Acapulco Bellingham Beverly Hills Bremerton Camacho
## 383 143 811 834 452
## Guadalajara Hidalgo Los Angeles Merida Mexico City
## 75 845 926 654 194
## Orizaba Portland Salem San Andres San Diego
## 464 876 1386 621 866
## San Francisco Seattle Spokane Tacoma Vancouver
## 130 922 875 1257 633
## Victoria Walla Walla Yakima
## 176 160 376
Bảng tần suất
table(dl$City)/sum(nrow(data))
##
## Acapulco Bellingham Beverly Hills Bremerton Camacho
## 0.027242336 0.010171420 0.057685468 0.059321431 0.032150224
## Guadalajara Hidalgo Los Angeles Merida Mexico City
## 0.005334661 0.060103848 0.065865282 0.046518245 0.013798990
## Orizaba Portland Salem San Andres San Diego
## 0.033003770 0.062308841 0.098584537 0.044170994 0.061597553
## San Francisco Seattle Spokane Tacoma Vancouver
## 0.009246746 0.065580767 0.062237712 0.089408920 0.045024539
## Victoria Walla Walla Yakima
## 0.012518671 0.011380610 0.026744434
Biểu đồ:
data %>%
ggplot(aes(x = City)) +
geom_bar(fill = "#28B463") +
labs(title = "Biểu đồ tần số khách hàng ở các thành phố", x = "Sở hữu nhà", y = "Số lượng") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Nhận xét: Dữ liệu cho thấy Salem (9.86%), Tacoma (8.94%), Los Angeles (6.59%), Seattle (6.56%), và Portland (6.23%) là những thành phố có mật độ giao dịch cao nhất, cho thấy đây là các khu vực thị trường trọng điểm mà siêu thị nên ưu tiên đầu tư về quảng bá, phân phối và chăm sóc khách hàng.
Ngoài ra, các thành phố như Bremerton, Spokane, San Diego và Hidalgo cũng chiếm tỷ trọng khá lớn (trên 5%), đóng vai trò bổ trợ quan trọng trong chiến lược mở rộng thị trường.
Ngược lại, các thành phố như Guadalajara, Victoria, San Francisco chỉ chiếm tỷ lệ dưới 1.5%, nên có thể được xem là khu vực tiềm năng dài hạn hoặc dùng để thử nghiệm các chiến dịch nhỏ trước khi mở rộng quy mô.
Bảng tần số
tsb <- table(data$StateorProvince)
tsb
##
## BC CA DF Guerrero Jalisco OR Veracruz WA
## 809 2733 815 383 75 2262 464 4567
## Yucatan Zacatecas
## 654 1297
Bảng tần suất
tsuat3<- table(data$StateorProvince)/sum(nrow(data))
tsuat3
##
## BC CA DF Guerrero Jalisco OR
## 0.057543211 0.194395049 0.057969984 0.027242336 0.005334661 0.160893378
## Veracruz WA Yucatan Zacatecas
## 0.033003770 0.324845295 0.046518245 0.092254072
Biểu đồ:
data %>%
ggplot(aes(x = StateorProvince)) +
geom_bar(fill = "#5DADE2") +
labs(title = "Phân bố theo bang/tỉnh", x = "Bang/Tỉnh", y = "Số lượng") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Nhận xét: Ba bang Washington (WA - 32.48%), California (CA - 19.44%) và Oregon (OR - 16.09%) chiếm tổng cộng gần 70% số lượng khách hàng, khẳng định đây là khu vực trọng tâm trong chiến lược thị trường.
→ Các hoạt động như phân tích hành vi tiêu dùng, tối ưu hóa danh mục sản phẩm, và triển khai chiến dịch tiếp thị nên ưu tiên tập trung vào nhóm bang này để đạt hiệu quả cao nhất.
Bên cạnh đó, Zacatecas (9.23%) và DF (5.80%) cũng thể hiện là những thị trường đáng chú ý với quy mô trung bình, phù hợp cho việc mở rộng dịch vụ hoặc thử nghiệm chính sách mới. Các bang có tỷ trọng thấp hơn như Guerrero, Veracruz, Yucatan hay Jalisco có thể được xem là thị trường vệ tinh, tiềm năng để phát triển dài hạn hoặc triển khai các chiến dịch khu vực nhỏ, có chọn lọc.
Bảng tần số
table(dl$Country)
##
## Canada Mexico USA
## 809 3688 9562
Bảng tần suất
table(dl$Country)/sum(nrow(data))
##
## Canada Mexico USA
## 0.05754321 0.26232307 0.68013372
Biểu đồ:
data %>% group_by(Country) %>% summarise(n = n()) %>%
ggplot(aes(x = '', y = n,fill = Country)) +
geom_col(color = 'black') +
coord_polar('y') +
geom_text(aes(x = 1.3, label = n),position = position_stack(vjust = .5)) +
labs(caption = "Biểu đồ tròn về tần số quốc gia") +
theme_void()
Nhận xét: Hoa Kỳ (USA) chiếm tỷ lệ áp đảo với 68.0133722% số khách hàng, khẳng định đây là thị trường cốt lõi cần được ưu tiên đầu tư về hạ tầng, dịch vụ và chiến lược tiếp thị.
Mexico đứng thứ hai với 26.2323067%, là một thị trường tiềm năng với quy mô đủ lớn để triển khai chiến dịch bản địa hóa, như điều chỉnh ngôn ngữ, giá cả, ưu đãi và sản phẩm phù hợp văn hóa.
Canada tuy chỉ chiếm 5.7543211%, nhưng vẫn là một thị trường vệ tinh đáng chú ý, phù hợp để duy trì hiện diện thương hiệu và thử nghiệm sản phẩm/dịch vụ mới trước khi mở rộng sâu hơn.
→ Chiến lược phân khúc địa lý rõ ràng sẽ giúp doanh nghiệp tối ưu hóa nguồn lực và nâng cao hiệu quả tiếp cận từng thị trường.
Bảng tần số
table(dl$ProductFamily)
##
## Drink Food Non-Consumable
## 1250 10153 2656
Bảng tần suất
table(dl$ProductFamily)/sum(nrow(data))
##
## Drink Food Non-Consumable
## 0.08891102 0.72217085 0.18891813
Biểu đồ:
data %>%
ggplot(aes(x = ProductFamily)) +
geom_bar(fill = "#1F618D") +
labs(title = "Phân bố nhóm sản phẩm", x = "Nhóm sản phẩm", y = "Số lượng") +
theme_minimal()
Nhận xét: Nhóm sản phẩm Food chiếm tỷ trọng áp đảo với hơn 72% tổng số giao dịch, khẳng định đây là mảng kinh doanh cốt lõi của siêu thị và cần được ưu tiên trong chiến lược sản phẩm, trưng bày và khuyến mãi. Trong khi đó, các nhóm Non-Consumable (18.89%) và Drink (8.89%) tuy chiếm tỷ lệ thấp hơn nhưng vẫn thể hiện tiềm năng phát triển, đặc biệt thông qua các chiến dịch bán chéo hoặc kết hợp combo để tăng giá trị đơn hàng và mở rộng hành vi tiêu dùng của khách.
Bảng tần số
table(dl$ProductDepartment)
##
## Alcoholic Beverages Baked Goods Baking Goods Beverages
## 356 425 1072 680
## Breakfast Foods Canned Foods Canned Products Carousel
## 188 977 109 59
## Checkout Dairy Deli Eggs
## 82 903 699 198
## Frozen Foods Health and Hygiene Household Meat
## 1382 893 1420 89
## Periodicals Produce Seafood Snack Foods
## 202 1994 102 1600
## Snacks Starchy Foods
## 352 277
Bảng tần suất
table(dl$ProductDepartment)/sum(nrow(data))
##
## Alcoholic Beverages Baked Goods Baking Goods Beverages
## 0.025321858 0.030229746 0.076250089 0.048367594
## Breakfast Foods Canned Foods Canned Products Carousel
## 0.013372217 0.069492852 0.007753041 0.004196600
## Checkout Dairy Deli Eggs
## 0.005832563 0.064229319 0.049719041 0.014083505
## Frozen Foods Health and Hygiene Household Meat
## 0.098300021 0.063518031 0.101002916 0.006330464
## Periodicals Produce Seafood Snack Foods
## 0.014368020 0.141830856 0.007255139 0.113806103
## Snacks Starchy Foods
## 0.025037343 0.019702682
Biểu đồ:
data %>%
ggplot(aes(x = ProductDepartment)) +
geom_bar(fill = "#D35400") +
labs(title = "Phòng ban sản phẩm", x = "Phòng ban", y = "Số lượng") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Nhận xét: Rau quả (Produce), Snack Foods và Household là ba bộ phận sản phẩm chiếm tỷ trọng lớn nhất trong tổng doanh số, lần lượt đạt 14,18%, 11,38% và 10,10%. Điều này cho thấy sức mua mạnh mẽ và sự quan tâm lớn của khách hàng đối với những nhóm hàng này. Do vậy, việc tập trung đầu tư vào trưng bày bắt mắt, triển khai các chương trình khuyến mãi hấp dẫn và phát triển đa dạng sản phẩm trong các nhóm này sẽ là chiến lược then chốt giúp tăng trưởng doanh thu và củng cố vị thế cạnh tranh trên thị trường.
Bảng tần số
table(dl$ProductCategory)
##
## Baking Goods Bathroom Products Beer and Wine
## 484 365 356
## Bread Breakfast Foods Candles
## 425 417 45
## Candy Canned Anchovies Canned Clams
## 352 44 53
## Canned Oysters Canned Sardines Canned Shrimp
## 35 40 38
## Canned Soup Canned Tuna Carbonated Beverages
## 404 87 154
## Cleaning Supplies Cold Remedies Dairy
## 189 93 903
## Decongestants Drinks Eggs
## 85 135 198
## Electrical Frozen Desserts Frozen Entrees
## 355 323 118
## Fruit Hardware Hot Beverages
## 765 129 226
## Hygiene Jams and Jellies Kitchen Products
## 197 588 217
## Magazines Meat Miscellaneous
## 202 761 42
## Packaged Vegetables Pain Relievers Paper Products
## 48 192 345
## Pizza Plastic Products Pure Juice Beverages
## 194 141 165
## Seafood Side Dishes Snack Foods
## 102 153 1600
## Specialty Starchy Foods Vegetables
## 289 277 1728
Bảng tần suất
table(dl$ProductCategory)/sum(nrow(data))
##
## Baking Goods Bathroom Products Beer and Wine
## 0.034426346 0.025962017 0.025321858
## Bread Breakfast Foods Candles
## 0.030229746 0.029660716 0.003200797
## Candy Canned Anchovies Canned Clams
## 0.025037343 0.003129668 0.003769827
## Canned Oysters Canned Sardines Canned Shrimp
## 0.002489508 0.002845153 0.002702895
## Canned Soup Canned Tuna Carbonated Beverages
## 0.028736041 0.006188207 0.010953837
## Cleaning Supplies Cold Remedies Dairy
## 0.013443346 0.006614980 0.064229319
## Decongestants Drinks Eggs
## 0.006045949 0.009602390 0.014083505
## Electrical Frozen Desserts Frozen Entrees
## 0.025250729 0.022974607 0.008393200
## Fruit Hardware Hot Beverages
## 0.054413543 0.009175617 0.016075112
## Hygiene Jams and Jellies Kitchen Products
## 0.014012376 0.041823743 0.015434953
## Magazines Meat Miscellaneous
## 0.014368020 0.054129028 0.002987410
## Packaged Vegetables Pain Relievers Paper Products
## 0.003414183 0.013656732 0.024539441
## Pizza Plastic Products Pure Juice Beverages
## 0.013798990 0.010029163 0.011736254
## Seafood Side Dishes Snack Foods
## 0.007255139 0.010882709 0.113806103
## Specialty Starchy Foods Vegetables
## 0.020556227 0.019702682 0.122910591
Biểu đồ:
data %>%
ggplot(aes(x = ProductCategory)) +
geom_bar(fill = "#45B39D") +
labs(title = "Danh mục sản phẩm cụ thể", x = "Danh mục", y = "Số lượng") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Nhận xét: Danh mục Vegetables và Snack Foods đứng đầu về số lượng giao dịch, lần lượt chiếm 12,29% và 11,38% tổng số. Điều này minh chứng cho sự ưu tiên rõ ràng của khách hàng đối với các sản phẩm thực phẩm tươi sống và đồ ăn nhanh tiện lợi trong thói quen mua sắm hàng ngày. Vì vậy, doanh nghiệp nên tiếp tục củng cố và phát triển mạnh mẽ hai nhóm hàng này thông qua việc nâng cao chất lượng, đa dạng hóa sản phẩm và xây dựng các chương trình khuyến mãi sáng tạo nhằm giữ vững lòng trung thành của khách hàng và tăng trưởng bền vững.
Ba hạng mục được chọn từ các biến định tính trong dữ liệu là:
n_female <- sum(data$Gender == "F")
n_total <- nrow(data)
prop.test(n_female, n_total, conf.level = 0.95)
##
## 1-sample proportions test with continuity correction
##
## data: n_female out of n_total, null probability 0.5
## X-squared = 5.5765, df = 1, p-value = 0.0182
## alternative hypothesis: true p is not equal to 0.5
## 95 percent confidence interval:
## 0.5016931 0.5182886
## sample estimates:
## p
## 0.5099936
Giải thích câu lệnh:
sum(data$Gender == "F"): Đếm số lượng khách hàng có
giới tính là nữ (“F”).nrow(data): Tính tổng số quan sát trong bộ dữ
liệu.prop.test(...): Hàm tính khoảng tin cậy 95% và thực
hiện kiểm định giả thuyết cho tỷ lệ.Tỷ lệ ước lượng: Khoảng 50.99% khách hàng là nữ.
Khoảng tin cậy 95%: nằm trong khoảng từ 50.17% đến 51.82%.
Diễn giải: Kết quả kiểm định cho thấy tỷ lệ nữ trong
mẫu là khoảng 51%. Khoảng tin cậy 95% cho tỷ lệ này được tính từ kết quả
prop.test(). Nếu khoảng này không bao gồm 50% và p-value
nhỏ hơn 0.05, ta có thể kết luận rằng tỷ lệ nữ trong tổng thể không bằng
50% với độ tin cậy cao.
Nếu p-value < 0.05 → Bác bỏ giả thuyết H0: Tỷ lệ nữ khác 50%, có ý nghĩa thống kê.
Nếu p-value ≥ 0.05 → Không bác bỏ H0: Không đủ bằng chứng để kết luận tỷ lệ nữ khác 50%
.
prop.test(n_female, n_total, p = 0.6, alternative = "two.sided", conf.level = 0.95)
##
## 1-sample proportions test with continuity correction
##
## data: n_female out of n_total, null probability 0.6
## X-squared = 474.18, df = 1, p-value < 2.2e-16
## alternative hypothesis: true p is not equal to 0.6
## 95 percent confidence interval:
## 0.5016931 0.5182886
## sample estimates:
## p
## 0.5099936
→ Kết quả: p-value < 2.2e-16 < 0.05 → bác bỏ H0. Do đó, có thể kết luận rằng tỷ lệ khách hàng nữ khác 50%, và thực tế là cao hơn một chút (khoảng 51%).
n_homeowner <- sum(data$Homeowner == "Y")
n_total <- nrow(data)
prop.test(n_homeowner, n_total, conf.level = 0.95)
##
## 1-sample proportions test with continuity correction
##
## data: n_homeowner out of n_total, null probability 0.5
## X-squared = 568.86, df = 1, p-value < 2.2e-16
## alternative hypothesis: true p is not equal to 0.5
## 95 percent confidence interval:
## 0.5924537 0.6087145
## sample estimates:
## p
## 0.6006117
Giải thích câu lệnh:
sum(data$Homeowner == "Y"): Đếm số lượng khách hàng có
ghi nhận sở hữu nhà (“Y”).nrow(data): Tính tổng số quan sát trong bộ dữ
liệu.prop.test(...): Hàm dùng để tính khoảng tin cậy 95% và
thực hiện kiểm định giả thuyết cho tỷ lệ.Tỷ lệ ước lượng: Khoảng 67.88% khách hàng sở hữu nhà.
Khoảng tin cậy 95%: từ 59.25% đến 60.87%.
Diễn giải: Kết quả kiểm định cho thấy tỷ lệ khách
hàng sở hữu nhà trong mẫu là khoảng 60.06%. Khoảng tin cậy 95% cho tỷ lệ
này được tính từ kết quả prop.test().
Nếu p-value < 0.05 → Bác bỏ giả thuyết H0: Tỷ lệ khách hàng sở hữu nhà khác 60%, có ý nghĩa thống kê.
Nếu p-value ≥ 0.05 → Không bác bỏ H0: Không đủ bằng chứng để kết luận tỷ lệ khách hàng sở hữu nhà khác 60%.
prop.test(n_homeowner, n_total, p = 0.6, alternative = "two.sided", conf.level = 0.95)
##
## 1-sample proportions test with continuity correction
##
## data: n_homeowner out of n_total, null probability 0.6
## X-squared = 0.019445, df = 1, p-value = 0.8891
## alternative hypothesis: true p is not equal to 0.6
## 95 percent confidence interval:
## 0.5924537 0.6087145
## sample estimates:
## p
## 0.6006117
→ Kết quả: p-value = 0.8891 > 0.05 → bác bỏ giả thuyết H1, chấp nhận giả thuyết H0. Do đó, có thể kết luận rằng tỷ lệ khách hàng sở hữu nhà gần bằng 60% (~60.06%).
n_food <- sum(data$ProductFamily == "Food")
n_total <- nrow(data)
prop.test(n_food, n_total, conf.level = 0.95)
##
## 1-sample proportions test with continuity correction
##
## data: n_food out of n_total, null probability 0.5
## X-squared = 2774.9, df = 1, p-value < 2.2e-16
## alternative hypothesis: true p is not equal to 0.5
## 95 percent confidence interval:
## 0.7146709 0.7295489
## sample estimates:
## p
## 0.7221709
Giải thích câu lệnh:
sum(data$ProductFamily == "Food"): Đếm số lượng sản
phẩm thuộc nhóm “Food” trong dữ liệu.nrow(data): Tính tổng số quan sát trong bộ dữ
liệu.prop.test(...): Hàm dùng để tính khoảng tin cậy và kiểm
định giả thuyết cho tỷ lệ.Tỷ lệ ước lượng: Khoảng 72.22% sản phẩm thuộc nhóm “Food”.
Khoảng tin cậy 95%: từ 71.47% đến 72.95%.
Diễn giải: Tỷ lệ sản phẩm thuộc nhóm Food trong mẫu
là khoảng 72.22%. Khoảng tin cậy 95% cho tỷ lệ này được tính từ kết quả
prop.test().
prop.test(n_food, n_total, p = 0.7, alternative = "less", conf.level = 0.95)
##
## 1-sample proportions test with continuity correction
##
## data: n_food out of n_total, null probability 0.7
## X-squared = 32.802, df = 1, p-value = 1
## alternative hypothesis: true p is less than 0.7
## 95 percent confidence interval:
## 0.0000000 0.7283768
## sample estimates:
## p
## 0.7221709
Nếu p-value < 0.05 → Bác bỏ giả thuyết H0: Tỷ lệ sản phẩm nhóm Food nhỏ hơn 70%, có ý nghĩa thống kê.
Nếu p-value ≥ 0.05 → Không bác bỏ H0: Không đủ bằng chứng để kết luận tỷ lệ sản phẩm nhóm Food nhỏ hơn 70%.
→ Kết quả: p-value = 1 > 0.05 → Không có đủ bằng chứng thống kê để khẳng định rằng tỷ lệ sản phẩm thuộc nhóm Food nhỏ hơn 70%. Mặc dù tỷ lệ thực tế theo mẫu là khoảng 72.22%, nhưng mức ý nghĩa thống kê không đủ mạnh để kết luận rằng nhóm Food chiếm tỷ lệ thấp hơn 70% trong tổng thể.
Ta chọn 3 cặp biến định tính như sau:
Gender và MaritalStatus
Homeowner và Country
StateorProvince và ProductFamily
Ta xây dựng bảng tần suất chéo cho hai biến Gender và MaritalStatus như sau:
table_gender_marital <- table(data$Gender, data$MaritalStatus)
table_gender_marital
##
## M S
## F 3602 3568
## M 3264 3625
Từ bảng tần suất chéo giữa giới tính và tình trạng hôn nhân, ta rút ra một số quan sát như sau:
Trong nhóm nữ giới, có 3.602 người đã kết hôn và 3.568 người độc thân.
Đối với nam giới, số người đã kết hôn là 3.264, trong khi 3.625 người vẫn còn độc thân.
prop.table(table_gender_marital, margin = 1)
##
## M S
## F 0.5023710 0.4976290
## M 0.4737988 0.5262012
Giải thích câu lệnh:
prop.table(...): Là hàm trong R dùng để tính
tỷ lệ phần trăm (proportion) từ một bảng tần suất
(contingency table).
margin = 1: Tham số này xác định chiều mà tỷ lệ được
tính.
margin = 1: Tính tỷ lệ theo hàng →
tức là, với mỗi hàng (ví dụ: từng giới tính), tổng giá trị trong
hàng đó = 1.0 (hay 100%).
margin = 2: Tính tỷ lệ theo cột →
tức là, với mỗi cột (ví dụ: từng tình trạng hôn nhân), tổng giá trị
trong cột = 1.
Không có margin: Tỷ lệ được tính trên toàn
bảng, tức là tổng toàn bộ bảng sẽ bằng 1.
Diễn giải kết quả:
Kết quả từ bảng tần suất và bảng tỷ lệ cho thấy:
Cả nam và nữ có tỷ lệ đã kết hôn và độc thân khá tương đồng, tuy nhiên có một số khác biệt nhỏ:
Nữ giới có xu hướng đã kết hôn nhiều hơn nam giới (50.24% so với 47.38%).
Ngược lại, nam giới có tỷ lệ độc thân nhỉnh hơn nữ giới (52.62% so với 49.76%).
Sự khác biệt này có thể phản ánh đặc điểm xã hội như tuổi kết hôn trung bình khác nhau giữa nam và nữ, hoặc nữ có xu hướng kết hôn sớm hơn, sống lâu hơn, nên tỷ lệ đã kết hôn cao hơn.
# Trực quan hóa bằng biểu đồ cột chồng
ggplot(data, aes(x = Gender, fill = MaritalStatus)) +
geom_bar(position = "fill") +
labs(title = "Tỷ lệ tình trạng hôn nhân theo giới tính", y = "Tỷ lệ", x = "Giới tính") +
scale_y_continuous(labels = scales::percent)
Giải thích câu lệnh:
ggplot(data, aes(x = Gender, fill = MaritalStatus)):
Tạo biểu đồ sử dụng dữ liệu data, với trục hoành là biến
Gender và màu sắc biểu diễn biến
MaritalStatus.
geom_bar(position = "fill"): Vẽ biểu đồ cột chồng
theo tỷ lệ (stacked bar chart), trong đó mỗi cột có tổng chiều cao bằng
1 (100%), thể hiện tỷ lệ các nhóm trong biến MaritalStatus
theo từng giới tính.
labs(title = "Tỷ lệ tình trạng hôn nhân theo giới tính", y = "Tỷ lệ", x = "Giới tính"):
Đặt tiêu đề biểu đồ và nhãn cho trục hoành và tung.
scale_y_continuous(labels = scales::percent): Định
dạng nhãn trục tung thành phần trăm, giúp dễ đọc và so sánh tỷ lệ giữa
các nhóm.
Nhận xét: Biểu đồ cột chồng thể hiện tỷ lệ tình trạng hôn nhân theo giới tính cho thấy rằng cả nam và nữ đều có sự phân bố tương đối đồng đều giữa hai nhóm hôn nhân là “Married” (M) và “Single” (S), tuy nhiên vẫn có những khác biệt nhỏ. Cụ thể, tỷ lệ nam giới độc thân (S) có xu hướng nhỉnh hơn một chút so với nữ, trong khi nữ giới kết hôn (M) lại chiếm tỷ lệ cao hơn nhẹ so với nam. Dù sự khác biệt này không lớn và phân bố giữa hai giới khá tương đồng, vẫn cần tiến hành kiểm định thống kê (như kiểm định Chi-square) để xác định xem mối liên hệ giữa giới tính và tình trạng hôn nhân có ý nghĩa thống kê hay không.
chisq.test(table_gender_marital)
##
## Pearson's Chi-squared test with Yates' continuity correction
##
## data: table_gender_marital
## X-squared = 11.365, df = 1, p-value = 0.0007485
Giả thuyết kiểm định:
Giải thích ý nghĩa kiểm định:
Kết quả kiểm định:
Kết luận thống kê: Vì giá trị p-value rất nhỏ (p = 0.0007485), nhỏ hơn ngưỡng ý nghĩa 0.05, ta bác bỏ giả thuyết H0. Điều này cho thấy có mối liên hệ có ý nghĩa thống kê giữa giới tính và tình trạng hôn nhân. Nói cách khác, việc một người kết hôn hay chưa có liên quan đáng kể đến giới tính của họ trong tập dữ liệu này.
Thảo luận thêm: Mối liên hệ giữa giới tính và tình trạng hôn nhân có thể phản ánh những khác biệt xã hội, văn hóa hoặc độ tuổi trung bình khác nhau giữa nam và nữ trong mẫu khảo sát. Ví dụ, có thể một giới có xu hướng kết hôn sớm hơn hoặc có tỷ lệ độc thân cao hơn vì lý do nghề nghiệp, giáo dục, hay kỳ vọng xã hội. Dù không xác định được nguyên nhân từ kiểm định này, kết quả vẫn chỉ ra sự phân phối khác biệt rõ rệt giữa hai giới về tình trạng hôn nhân.
# Bảng tần suất chéo
table_home_country <- table(data$Homeowner, data$Country)
table_home_country
##
## Canada Mexico USA
## N 320 1473 3822
## Y 489 2215 5740
Từ bảng tần suất chéo giữa tình trạng sở hữu nhà và quốc gia, ta có thể quan sát được phân bố số lượng người sở hữu và không sở hữu nhà tại từng quốc gia như sau:
Ở Canada, có 320 người không sở hữu nhà và 489 người sở hữu nhà.
Ở Mexico, có 1.473 người không sở hữu nhà và 2.215 người sở hữu nhà.
Ở USA, có 3.822 người không sở hữu nhà và 5.740 người sở hữu nhà.
# Tỷ lệ theo hàng
prop.table(table_home_country, margin = 1)
##
## Canada Mexico USA
## N 0.05699020 0.26233304 0.68067676
## Y 0.05791094 0.26231644 0.67977262
Giải thích câu lệnh:
prop.table(...): Tính tỷ lệ phần trăm từ bảng tần
suất.
margin = 1: Tính tỷ lệ theo hàng, tức là trong mỗi
nhóm Homeowner (Y/N), tổng các giá trị trong hàng bằng 1
(100%).
Nhận xét:
Trong nhóm không sở hữu nhà (N): Canada chiếm khoảng 5.70%, Mexico 26.23%, USA 68.07%.
Trong nhóm sở hữu nhà (Y): Canada chiếm khoảng 5.79%, Mexico 26.23%, USA 67.98%.
=> Tỷ lệ phân bố giữa các quốc gia khá tương đồng ở hai nhóm, cho thấy không có sự khác biệt rõ rệt về quốc tịch giữa người có và không có nhà trong tập dữ liệu này.
data_summary <- data %>% group_by(Homeowner, Country) %>% summarise(count = n()) %>% ungroup()
## `summarise()` has grouped output by 'Homeowner'. You can override using the
## `.groups` argument.
ggplot(data_summary, aes(x = Homeowner, y = count, fill = Country)) +
geom_col(position = position_dodge()) +
geom_text(aes(label = count), position = position_dodge(width = 0.9), vjust = -0.5) +
labs(title = "Số lượng sở hữu nhà theo quốc gia", y = "Số lượng", x = "Tình trạng sở hữu nhà")
Giải thích câu lệnh:
group_by(Homeowner, Country) %>% summarise(count = n()):
Tính tổng số quan sát theo từng nhóm kết hợp giữa Homeowner
và Country.
geom_col(position = position_dodge()): Vẽ biểu đồ
cột so sánh số lượng giữa các quốc gia theo tình trạng sở hữu
nhà.
geom_text(...): Hiển thị số liệu cụ thể trên từng
cột.
labs(...): Đặt tiêu đề và nhãn trục cho biểu
đồ.
Nhận xét: Biểu đồ cho thấy số lượng người sở hữu và không sở hữu nhà ở từng quốc gia, trong đó Hoa Kỳ chiếm tỷ lệ cao vượt trội so với hai quốc gia còn lại. Tuy nhiên, sự chênh lệch giữa nhóm sở hữu và không sở hữu nhà trong mỗi quốc gia không quá rõ rệt. Điều này gợi ý rằng tình trạng sở hữu nhà có thể không bị ảnh hưởng đáng kể bởi quốc tịch, tuy nhiên cần kiểm định thống kê để xác nhận điều này.
chisq.test(table_home_country)
##
## Pearson's Chi-squared test
##
## data: table_home_country
## X-squared = 0.053742, df = 2, p-value = 0.9735
Giả thuyết kiểm định:
Giải thích ý nghĩa kiểm định:
Kết quả kiểm định:
Kết luận thống kê: Vì p-value = 0.9735 > mức ý nghĩa 0.05 => Không đủ bằng chứng để bác bỏ giả thuyết H0. Do đó, ta kết luận rằng không có mối liên hệ có ý nghĩa thống kê giữa tình trạng sở hữu nhà và quốc gia. Nói cách khác, việc một cá nhân có sở hữu nhà hay không không phụ thuộc đáng kể vào quốc tịch của họ trong tập dữ liệu này.
Thảo luận thêm:Kết quả này cho thấy sự phân bố trạng thái sở hữu nhà giữa các quốc gia tương đối đồng đều. Điều này có thể phản ánh tính đồng nhất về điều kiện sống hoặc chính sách nhà ở tại các quốc gia khảo sát. Tuy nhiên, để hiểu sâu hơn nguyên nhân, cần phân tích thêm các yếu tố như thu nhập, nghề nghiệp hoặc độ tuổi.
# Bảng tần suất chéo
table_state_product <- table(data$StateorProvince, data$ProductFamily)
table_state_product
##
## Drink Food Non-Consumable
## BC 69 580 160
## CA 258 1974 501
## DF 65 598 152
## Guerrero 41 272 70
## Jalisco 5 57 13
## OR 199 1629 434
## Veracruz 44 322 98
## WA 399 3287 881
## Yucatan 48 494 112
## Zacatecas 122 940 235
Từ bảng tần suất chéo giữa tiểu bang và nhóm sản phẩm, ta có thể quan sát được phân bố số lượng sản phẩm tiêu thụ tại từng tiểu bang như sau*
Ở WA, có 399 sản phẩm thuộc nhóm Drink, 3.287 sản phẩm Food, và 881 sản phẩm Non-Consumable.
Ở CA, có 258 sản phẩm Drink, 1.974 sản phẩm Food, và 501 sản phẩm Non-Consumable.
Ở OR, có 199 sản phẩm Drink, 1.629 sản phẩm Food, và 434 sản phẩm Non-Consumable.
Ở Zacatecas, có 122 sản phẩm Drink, 940 sản phẩm Food, và 235 sản phẩm Non-Consumable.
Ở DF, có 65 sản phẩm Drink, 598 sản phẩm Food, và 152 sản phẩm Non-Consumable.
Ở BC, có 69 sản phẩm Drink, 580 sản phẩm Food, và 160 sản phẩm Non-Consumable.
Ở Veracruz, có 44 sản phẩm Drink, 322 sản phẩm Food, và 98 sản phẩm Non-Consumable.
Ở Guerrero, có 41 sản phẩm Drink, 272 sản phẩm Food, và 70 sản phẩm Non-Consumable.
Ở Yucatan, có 48 sản phẩm Drink, 494 sản phẩm Food, và 112 sản phẩm Non-Consumable.
Ở Jalisco, có 5 sản phẩm Drink, 57 sản phẩm Food, và 13 sản phẩm Non-Consumable.
# Tỷ lệ theo hàng
prop.table(table_state_product, margin = 1)
##
## Drink Food Non-Consumable
## BC 0.08529048 0.71693449 0.19777503
## CA 0.09440176 0.72228321 0.18331504
## DF 0.07975460 0.73374233 0.18650307
## Guerrero 0.10704961 0.71018277 0.18276762
## Jalisco 0.06666667 0.76000000 0.17333333
## OR 0.08797524 0.72015915 0.19186561
## Veracruz 0.09482759 0.69396552 0.21120690
## WA 0.08736589 0.71972849 0.19290563
## Yucatan 0.07339450 0.75535168 0.17125382
## Zacatecas 0.09406322 0.72474942 0.18118736
Giải thích câu lệnh:
prop.table(...): Tính tỷ lệ phần trăm từ bảng tần
suất.
margin = 1: Tính tỷ lệ theo hàng, tức là trong mỗi
tiểu bang, tổng ba nhóm sản phẩm (Drink, Food,
Non-Consumable) bằng 1 (100%).
Nhận xét:
Trong tất cả các tiểu bang, nhóm Food chiếm tỷ trọng lớn nhất, dao động từ khoảng 69% đến 76%, cho thấy đây là nhóm sản phẩm được tiêu thụ nhiều nhất.
Nhóm Drink thường chiếm tỷ lệ thấp nhất, phổ biến trong khoảng 6.6% đến 10.7%, thấp nhất là ở Jalisco (6.67%) và cao nhất là ở Guerrero (10.7%).
Nhóm Non-Consumable dao động khoảng 17% đến 21%, với tỷ lệ cao nhất ở Veracruz (21.1%).
=> Có thể thấy rằng Food là nhóm sản phẩm chủ đạo trong cơ cấu tiêu dùng của các tiểu bang, trong khi Drink và Non-Consumable có mức độ tiêu thụ thấp hơn và không có sự biến động lớn giữa các khu vực.
data_summary1 <- data %>% group_by(StateorProvince, ProductFamily) %>% summarise(count = n()) %>% mutate(total = sum(count), pct = count / total) %>% ungroup()
## `summarise()` has grouped output by 'StateorProvince'. You can override using
## the `.groups` argument.
ggplot(data_summary1, aes(x = StateorProvince, y = pct, fill = ProductFamily)) +
geom_col(position = position_dodge()) +
geom_text(aes(label = scales::percent(pct, accuracy = 0.1)),
position = position_dodge(width = 0.9), vjust = -0.3, size = 3) +
scale_y_continuous(labels = scales::percent_format()) +
labs(title = "Tỷ lệ nhóm sản phẩm theo bang",
y = "Tỷ lệ",
x = "Bang")
Giải thích câu lệnh:
group_by(StateorProvince, ProductFamily) %>% summarise(count = n()):
Tính tổng số sản phẩm được tiêu thụ theo từng nhóm kết hợp giữa tiểu
bang và nhóm sản phẩm.
mutate(total = sum(count), pct = count / total):
Tính tổng số sản phẩm trong mỗi tiểu bang, sau đó tính tỷ lệ phần trăm
của từng nhóm sản phẩm.
geom_col(position = position_dodge()): Vẽ biểu đồ
cột so sánh tỷ lệ giữa các nhóm sản phẩm theo từng tiểu bang.
geom_text(...): Hiển thị số liệu tỷ lệ phần trăm cụ
thể trên từng cột.
labs(...): Đặt tiêu đề và nhãn trục cho biểu
đồ.
Nhận xét: Biểu đồ thể hiện tỷ lệ ba nhóm sản phẩm (Drink, Food, Non-Consumable) được tiêu thụ tại từng tiểu bang. Nhóm Food luôn chiếm tỷ trọng lớn nhất, dao động từ khoảng 69.4% đến 76%, cho thấy đây là nhóm sản phẩm chủ lực ở hầu hết các bang. Nhóm Drink có tỷ lệ thấp nhất, đặc biệt ở Jalisco chỉ chiếm khoảng 6.7%. Trong khi đó, nhóm Non-Consumable dao động từ 17% đến 21.1%, với tỷ lệ cao nhất ở Veracruz.
chisq.test(table_state_product)
##
## Pearson's Chi-squared test
##
## data: table_state_product
## X-squared = 12.3, df = 18, p-value = 0.8314
Giả thuyết kiểm định:
Giải thích ý nghĩa kiểm định:
Kết quả kiểm định:
Kết luận thống kê: Vì p-value = 0.8314 > 0.05, ta không đủ bằng chứng để bác bỏ giả thuyết H0. Do đó, ta kết luận rằng không có mối liên hệ có ý nghĩa thống kê giữa loại sản phẩm và bang. Nói cách khác, việc tiêu thụ các nhóm sản phẩm (Drink, Food, Non-Consumable) không khác biệt đáng kể giữa các bang trong tập dữ liệu này.
Thảo luận thêm: Kết quả kiểm định phù hợp với quan sát từ biểu đồ trước đó: tỷ lệ các nhóm sản phẩm được tiêu thụ tại các bang khá tương đồng, đặc biệt là nhóm Food luôn chiếm ưu thế. Điều này có thể phản ánh một xu hướng tiêu dùng phổ biến và ổn định trên toàn khu vực khảo sát. Tuy nhiên, để hiểu rõ hơn bản chất và nguyên nhân của sự phân bố này, cần tiếp tục phân tích thêm các yếu tố như dân số, thu nhập bình quân, đặc điểm vùng miền hoặc văn hóa tiêu dùng tại từng bang.
Qua toàn bộ quá trình phân tích các biến định tính trong bộ dữ liệu “Supermarket Transactions”, một số điểm nổi bật về đặc điểm khách hàng và hành vi tiêu dùng đã được xác định:
Giới tính: Tỷ lệ khách hàng nữ (51%) nhỉnh hơn nam giới (49%), và kết quả kiểm định cho thấy sự khác biệt này là có ý nghĩa thống kê. Điều này cho thấy nữ giới có thể là nhóm khách hàng chủ lực tại siêu thị.
Tình trạng hôn nhân: Nhóm chưa kết hôn chiếm tỷ lệ cao hơn (51.16%) so với nhóm đã kết hôn (48.84%). Kiểm định Chi-squared cũng cho thấy có mối liên hệ có ý nghĩa thống kê giữa giới tính và tình trạng hôn nhân.
Sở hữu nhà: Có 60.06% khách hàng sở hữu nhà – một tỷ lệ cao và có ý nghĩa thống kê. Điều này có thể phản ánh một nhóm khách hàng ổn định về tài chính.
Phân bố địa lý:
Bang: Các bang WA, CA và OR chiếm hơn 70% tổng số giao dịch – cho thấy đây là những thị trường chủ lực.
Thành phố: Salem, Tacoma, Los Angeles, Seattle và Portland là những địa phương có lượng giao dịch lớn.
Quốc gia: USA chiếm đến 68% khách hàng, trong khi Mexico là thị trường thứ hai (~26%).
Thu nhập: Khoảng gần 50% khách hàng rơi vào nhóm thu nhập từ 30K–70K. Đây là phân khúc chủ lực có tiềm năng chi tiêu ổn định.
Sản phẩm tiêu dùng:
Nhóm Food chiếm hơn 72% tổng số giao dịch, khẳng định là dòng sản phẩm chủ lực.
Các nhóm Drink và Non-Consumable chiếm tỷ lệ thấp hơn (~9% và 19%) nhưng có tiềm năng mở rộng.
Các phòng ban như Produce, Snack Foods, Household và danh mục như Vegetables, Snack Foods là những phân khúc bán chạy nhất.
Mối quan hệ giữa biến:
Gender vs. MaritalStatus: Có mối liên hệ thống kê (p-value = 0.0007485).
Homeowner vs. Country: Không có mối liên hệ thống kê (p-value = 0.9735).
ProductFamily vs. StateorProvince: Không có mối liên hệ rõ rệt giữa loại sản phẩm và tiểu bang (p-value = 0.8314).
Giới hạn dữ liệu: Phân tích chủ yếu tập trung vào các biến định tính. Những biến định lượng như doanh thu, số lượng sản phẩm bán ra, hoặc số con chưa được khai thác.
Không xét đến yếu tố thời gian: Các biến về thời gian như ngày giao dịch chưa được sử dụng để phân tích xu hướng theo tháng, quý, hay năm.
Không điều chỉnh độ lệch mẫu: Một số bang, thành phố hoặc nhóm thu nhập có kích thước mẫu quá nhỏ (như Jalisco, Guadalajara…), có thể gây sai lệch trong kết luận tổng thể.
Không có thông tin nhân khẩu học chi tiết hơn: Ví dụ như độ tuổi, nghề nghiệp, sở thích,… điều này hạn chế khả năng phân khúc khách hàng sâu hơn.
Ưu tiên chiến dịch marketing hướng đến nhóm khách hàng nữ đã kết hôn và nam chưa kết hôn – vì bảng tần suất chéo cho thấy nữ đã kết hôn chiếm ưu thế trong nhóm nữ, trong khi nam chưa kết hôn lại chiếm ưu thế ở nhóm nam. Điều này có thể giúp thiết kế chương trình phù hợp hơn với từng nhóm đặc thù.
Duy trì đầu tư mạnh vào nhóm sản phẩm Food – vì đây là sản phẩm chủ lực, có thể tạo gói combo, giảm giá theo mùa, hoặc chương trình tích điểm riêng.
Phát triển Drink và Non-Consumable bằng hình thức bán chéo với nhóm Food để tăng giá trị đơn hàng.
Tập trung thị trường trọng điểm ở các bang WA, CA, OR và các thành phố như Salem, Los Angeles, Portland… với chính sách phân phối và quảng bá mạnh hơn.
Tối ưu trải nghiệm cho nhóm khách hàng có nhà – đây là nhóm có tiềm năng chi tiêu cao, có thể hướng đến các sản phẩm cao cấp, dịch vụ giao hàng tận nơi.
Ảnh hưởng của giới tính và tình trạng hôn nhân đến lựa
chọn sản phẩm? – Có sự khác biệt về nhóm sản phẩm
(ProductFamily, ProductDepartment,
ProductCategory) giữa khách hàng theo Gender
(F/M) và MaritalStatus (S/M) không?
Mối liên hệ giữa sở hữu nhà và mức thu nhập? –
Khách hàng sở hữu nhà (Homeowner = Y) có xu hướng nằm trong
nhóm thu nhập (AnnualIncome) cao hơn và lựa chọn sản phẩm
khác biệt so với nhóm không sở hữu nhà không?
Phân bố đặc điểm khách hàng theo vùng địa lý? –
Có sự khác biệt về hành vi mua sắm và mức thu nhập giữa các khu vực
City, StateorProvince và Country
hay không?
Mối quan hệ giữa mức thu nhập và nhóm sản phẩm mua? – Các phân khúc thu nhập khác nhau có xu hướng ưu tiên mua những loại sản phẩm nào?