1 .Phần 1.Tìm hiểu và chuẩn bị dữ liệu

1.1 .Đọc dữ liệu từ file CSV

Bộ dữ liệu “Supermarket Transactions” lưu trữ thông tin chi tiết về các giao dịch mua sắm tại một siêu thị trong một khoảng thời gian cụ thể. Mỗi dòng trong tập dữ liệu tương ứng với một giao dịch, bao gồm nhiều trường thông tin liên quan đến khách hàng (giới tính, hạng thành viên, chi nhánh mua hàng), sản phẩm (nhóm hàng, phương thức thanh toán), cũng như các yếu tố phản ánh hành vi tiêu dùng (thời điểm mua hàng, mức độ hài lòng, tổng giá trị đơn hàng,…).

Dữ liệu được lưu dưới định dạng CSV, vì vậy cần được đọc từ tệp .csv. Ta gán bộ dữ liệu Supermarket Transactions vào một đối tượng có tên là data.

data <- read.csv("/Users/lengoctuongvy/Downloads/TLHK2:2025/T2_PHÂN TÍCH DỮ LIỆU ĐỊNH TÍNH/Book2.csv")
head(data,10)
X PurchaseDate CustomerID Gender MaritalStatus Homeowner Children AnnualIncome City StateorProvince Country ProductFamily ProductDepartment ProductCategory UnitsSold Revenue
1 12/18/2007 7223 F S Y 2 $30K - $50K Los Angeles CA USA Food Snack Foods Snack Foods 5 27.38
2 12/20/2007 7841 M M Y 5 $70K - $90K Los Angeles CA USA Food Produce Vegetables 5 14.90
3 12/21/2007 8374 F M N 2 $50K - $70K Bremerton WA USA Food Snack Foods Snack Foods 3 5.52
4 12/21/2007 9619 M M Y 3 $30K - $50K Portland OR USA Food Snacks Candy 4 4.44
5 12/22/2007 1900 F S Y 3 $130K - $150K Beverly Hills CA USA Drink Beverages Carbonated Beverages 4 14.00
6 12/22/2007 6696 F M Y 3 $10K - $30K Beverly Hills CA USA Food Deli Side Dishes 3 4.37
7 12/23/2007 9673 M S Y 2 $30K - $50K Salem OR USA Food Frozen Foods Breakfast Foods 4 13.78
8 12/25/2007 354 F M Y 2 $150K + Yakima WA USA Food Canned Foods Canned Soup 6 7.34
9 12/25/2007 1293 M M Y 3 $10K - $30K Bellingham WA USA Non-Consumable Household Cleaning Supplies 1 2.41
10 12/25/2007 7938 M S N 1 $50K - $70K San Diego CA USA Non-Consumable Health and Hygiene Pain Relievers 2 8.96

1.2 .Cấu trúc của data

Để kiểm tra cấu trúc tổng quát của bộ dữ liệu, ta sử dụng hàm str(), viết tắt của từ structure.

Hàm str() thường được dùng để khảo sát nhanh đặc điểm của một đối tượng, đặc biệt là các data frame. Cụ thể, lệnh này cung cấp:

  • Số lượng dòng và số lượng cột trong dữ liệu

  • Tên các biến (cột)

  • Kiểu dữ liệu của từng biến (số, chuỗi, nhân tố,…)

  • Một vài giá trị mẫu ban đầu của mỗi biến

Nhờ đó, str() là công cụ hữu ích giúp ta hình dung sơ bộ về cấu trúc dữ liệu trước khi tiến hành phân tích chi tiết.

str(data) #Kiểm tra cấu trúc data
## 'data.frame':    14059 obs. of  16 variables:
##  $ X                : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ PurchaseDate     : chr  "12/18/2007" "12/20/2007" "12/21/2007" "12/21/2007" ...
##  $ 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 ...

Từ kết quả hiển thị của hàm str(), ta nhận thấy:

Bộ dữ liệu gồm 14.059 quan sát (obs) và 16 biến (variables).

Tên các biến, kiểu dữ liệu tương ứng, cùng với một vài giá trị tiêu biểu đã được liệt kê rõ ràng.

Mục tiêu của phân tích: Áp dụng các kỹ thuật thống kê mô tả và thống kê suy diễn nhằm khám phá những đặc điểm nổi bật của khách hàng cũng như hành vi mua sắm của họ. Phân tích tập trung vào các biến định tính (categorical variables), bao gồm:

  • Gender: 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: Tình trạng sở hữu nhà (Y – Có, N – Không)

  • AnnualIncome: Thu nhập hàng năm (được phân chia theo các khoảng)

  • City, StateorProvince, Country: Thông tin về vị trí địa lý của khách hàng

  • ProductFamily, ProductDepartment, ProductCategory: Các cấp độ phân loại sản phẩm mà khách hàng đã mua

Thông qua việc phân tích các biến định tính này, ta kỳ vọng có thể rút ra những hiểu biết giá trị về phân khúc khách hàng, xu hướng tiêu dùng, cũng như các yếu tố ảnh hưởng đến hành vi mua sắm tại siêu thị.

1.3 .Thực hiên thao tác xem dòng đầu và cuối của data

Trong R, hai hàm thường được sử dụng để kiểm tra trực quan phần đầu và cuối của bộ dữ liệu là head() và tail().

Việc sử dụng hai hàm này mang lại nhiều lợi ích trong quá trình phân tích dữ liệu, cụ thể:

  • Giúp nhanh chóng xác nhận dữ liệu đã được nhập đúng định dạng (ví dụ: kiểu ngày tháng, ký tự, số liệu, v.v.).

  • Cho phép kiểm tra tính đầy đủ và nhất quán của dữ liệu từ đầu đến cuối.

  • Hỗ trợ phát hiện sớm các lỗi thường gặp như: dòng trống, dữ liệu bị cắt xén, giá trị nằm sai cột, hoặc lỗi định dạng.

Nhờ đó, head() và tail() là những công cụ đơn giản nhưng hiệu quả, thường được sử dụng ở giai đoạn tiền xử lý để đảm bảo chất lượng dữ liệu trước khi tiến hành phân tích chuyên sâu.

head(data,10) #Thực hiện xem 10 dòng đầu tiên của data
X PurchaseDate CustomerID Gender MaritalStatus Homeowner Children AnnualIncome City StateorProvince Country ProductFamily ProductDepartment ProductCategory UnitsSold Revenue
1 12/18/2007 7223 F S Y 2 $30K - $50K Los Angeles CA USA Food Snack Foods Snack Foods 5 27.38
2 12/20/2007 7841 M M Y 5 $70K - $90K Los Angeles CA USA Food Produce Vegetables 5 14.90
3 12/21/2007 8374 F M N 2 $50K - $70K Bremerton WA USA Food Snack Foods Snack Foods 3 5.52
4 12/21/2007 9619 M M Y 3 $30K - $50K Portland OR USA Food Snacks Candy 4 4.44
5 12/22/2007 1900 F S Y 3 $130K - $150K Beverly Hills CA USA Drink Beverages Carbonated Beverages 4 14.00
6 12/22/2007 6696 F M Y 3 $10K - $30K Beverly Hills CA USA Food Deli Side Dishes 3 4.37
7 12/23/2007 9673 M S Y 2 $30K - $50K Salem OR USA Food Frozen Foods Breakfast Foods 4 13.78
8 12/25/2007 354 F M Y 2 $150K + Yakima WA USA Food Canned Foods Canned Soup 6 7.34
9 12/25/2007 1293 M M Y 3 $10K - $30K Bellingham WA USA Non-Consumable Household Cleaning Supplies 1 2.41
10 12/25/2007 7938 M S N 1 $50K - $70K San Diego CA USA Non-Consumable Health and Hygiene Pain Relievers 2 8.96

Câu lệnh head(data, 10) dùng để hiển thị 10 dòng đầu tiên của bộ dữ liệu data tức là từ 1-10. Lệnh head() mặc định sẽ lấy 6 dòng đầu tiên, nếu ngưới dùng muốn thay đổi lượng các dòng đầu tiên để xem thì có thể đổi thành số khác.

Mục đích của lệnh head() gồm:

  • Quan sát cấu trúc dữ liệu ban đầu, định dạng và kiểu dữ liệu của các biến.

  • Kiểm tra tính hợp lý của việc nhập liệu ở những dòng đầu tiên.

  • Xác nhận dữ liệu đã được đọc đúng cách từ file nguồn (đặc biệt hữu ích với các định dạng như ngày tháng, ký tự, số,…).

Hàm head() đặc biệt hữu dụng trong bước tiền xử lý dữ liệu, giúp phát hiện nhanh các vấn đề tiềm ẩn trước khi tiến hành phân tích sâu hơn.

tail(data,10) #Xem 10 dòng cuối của data
X PurchaseDate CustomerID Gender MaritalStatus Homeowner Children AnnualIncome City StateorProvince Country ProductFamily ProductDepartment ProductCategory UnitsSold Revenue
14050 14050 12/28/2009 1394 M M Y 0 $30K - $50K San Andres DF Mexico Food Deli Meat 4 7.15
14051 14051 12/28/2009 6251 M S N 4 $50K - $70K Vancouver BC Canada Food Dairy Dairy 5 19.95
14052 14052 12/28/2009 378 M S Y 1 $30K - $50K Spokane WA USA Food Dairy Dairy 5 28.08
14053 14053 12/28/2009 7234 F M Y 4 $30K - $50K Spokane WA USA Food Deli Side Dishes 5 6.80
14054 14054 12/29/2009 2032 F M N 3 $10K - $30K Yakima WA USA Non-Consumable Household Paper Products 5 14.50
14055 14055 12/29/2009 9102 F M Y 2 $10K - $30K Bremerton WA USA Food Baking Goods Baking Goods 3 9.64
14056 14056 12/29/2009 4822 F M Y 3 $10K - $30K Walla Walla WA USA Food Frozen Foods Vegetables 3 7.45
14057 14057 12/31/2009 250 M S Y 1 $30K - $50K Portland OR USA Drink Beverages Pure Juice Beverages 4 3.24
14058 14058 12/31/2009 6153 F S N 4 $50K - $70K Spokane WA USA Drink Dairy Dairy 2 4.00
14059 14059 12/31/2009 3656 M S N 3 $50K - $70K Portland OR USA Non-Consumable Household Electrical 5 25.53

Ngược lại với head(), hàm tail() được sử dụng để hiển thị các dòng cuối cùng của bộ dữ liệu. Cụ thể, câu lệnh tail(data, 10) sẽ trả về 10 dòng cuối của đối tượng data. Người dùng có thể điều chỉnh số dòng hiển thị bằng cách thay đổi giá trị số trong hàm, tương tự như với head().

Mục đích của lệnh tail() gồm:

  • Kiểm tra phần kết thúc của tập dữ liệu, nhằm phát hiện xem có bị thiếu dòng, lỗi định dạng, hoặc dữ liệu không hợp lệ ở cuối hay không.

  • Đánh giá tính đồng nhất và nhất quán của dữ liệu từ đầu đến cuối.

  • Phát hiện các lỗi nhập liệu muộn như dòng trống, lỗi lệch cột, ký tự không mong muốn,…

Hàm tail() là công cụ hữu ích giúp đảm bảo rằng toàn bộ tập dữ liệu – không chỉ phần đầu – đều được xử lý và đọc đúng cách trước khi bước vào phân tích.

1.4 .Kiểm tra xem có giá trị bị thiếu NA trong các cột định tính không?

Trước tiên, ta xem tên các biến của toàn bộ data và lọc lấy chỉ lấy các biến định tính.

names(data) #Xem tên các biến có trong data
##  [1] "X"                 "PurchaseDate"      "CustomerID"       
##  [4] "Gender"            "MaritalStatus"     "Homeowner"        
##  [7] "Children"          "AnnualIncome"      "City"             
## [10] "StateorProvince"   "Country"           "ProductFamily"    
## [13] "ProductDepartment" "ProductCategory"   "UnitsSold"        
## [16] "Revenue"
tbdt <- c("Gender", "MaritalStatus", "Homeowner",  "AnnualIncome", "City",  "StateorProvince", "Country",  "ProductFamily", "ProductDepartment", "ProductCategory" ) #Gán tên các biến định tính vào object tên tbdt

Ta tạo 1 dataframe mới chỉ chứa các biến định tính, ta gán data mới này vào object với tên dt.

dt<- data[,tbdt]
head(dt)
Gender MaritalStatus Homeowner AnnualIncome City StateorProvince Country ProductFamily ProductDepartment ProductCategory
F S Y $30K - $50K Los Angeles CA USA Food Snack Foods Snack Foods
M M Y $70K - $90K Los Angeles CA USA Food Produce Vegetables
F M N $50K - $70K Bremerton WA USA Food Snack Foods Snack Foods
M M Y $30K - $50K Portland OR USA Food Snacks Candy
F S Y $130K - $150K Beverly Hills CA USA Drink Beverages Carbonated Beverages
F M Y $10K - $30K Beverly Hills CA USA Food Deli Side Dishes

Để kiểm tra tính hoàn chỉnh của dữ liệu, đặc biệt là nhằm phát hiện các giá trị bị thiếu (NA), ta sử dụng kết hợp hai hàm trong R: sum(is.na())

sum(is.na(dt))
## [1] 0

Giải thích câu lệnh:

Hàm is.na() trong R được sử dụng để xác định các giá trị bị thiếu (NA) trong dữ liệu.

  • Kết quả trả về là TRUE tại những vị trí có giá trị thiếu.

  • Trả về FALSE nếu giá trị tại vị trí đó không bị thiếu.

Khi kết hợp với hàm sum(), ta có thể đếm tổng số giá trị bị thiếu trong toàn bộ tập dữ liệu: Trong R, TRUE được quy đổi thành 1, còn FALSE là 0. Do đó, hàm sum() sẽ cộng tất cả các giá trị TRUE, tương đương với tổng số ô bị thiếu dữ liệu trong data.

Ta thực hiện xem vị trí phần tử bị thiếu NA trong dt bằng hàm which(is.na()).

which(is.na(dt))
## integer(0)

Kết quả trả về là integer(0), điều đó có nghĩa là không có bất kỳ giá trị thiếu nào (NA) trong toàn bộ bộ dữ liệu. Ngược lại, nếu có giá trị bị thiếu xuất hiện, một phương pháp đơn giản để xử lý là sử dụng hàm na.omit(), cụ thể: Lệnh này sẽ loại bỏ toàn bộ các dòng (hàng) trong dataset data mà có chứa ít nhất một giá trị NA. Kết quả là một phiên bản mới của dữ liệu – data_clean – chỉ bao gồm các dòng hoàn chỉnh, không có thiếu sót.


2 .Phần 2.Phân tích Mô tả Một biến Định tính (Univariate Descriptive Analysis)

2.1 .Biến Gender

2.1.1 .Thống kê tần số và tần suất

Ta thực hiện thống kê tần số và tần suất cho biến Gender (Giơi tính) được kết quả như sau: Hàm table() trong R được sử dụng để tạo bảng tần số, hiển thị số lần xuất hiện của từng giá trị trong một biến cụ thể của bộ dữ liệu. Ví dụ:

table(dt$Gender)
## 
##    F    M 
## 7170 6889

Lệnh trên sẽ trả về số lượng quan sát theo từng giới tính.

Nhận xét: Dựa trên bảng tần số thu được, bộ dữ liệu gồm 14.059 quan sát, trong đó: 7.170 người thuộc giới tính nữ (F – Female) và 6.889 người thuộc giới tính nam (M – Male). Điều này cho thấy tỷ lệ nữ chiếm ưu thế nhẹ so với nam trong tập dữ liệu khảo sát hành vi mua sắm.

Giải thích các lệnh tạo bảng tần suất: table(dt$Gender): Trả về số lượng quan sát tương ứng với từng nhóm giới tính trong biến Gender.

nrow(dt): Trả về tổng số dòng (quan sát) trong toàn bộ dataframe – chính là kích thước của tập dữ liệu.

Để tính tần suất (tỷ lệ), ta chia số lượng của từng nhóm cho tổng số quan sát, ví dụ:

table(dt$Gender)/sum(nrow(dt))
## 
##         F         M 
## 0.5099936 0.4900064

Dựa trên bảng tần suất thu được:Giới tính nữ (F - Female) chiếm khoảng 50,99%. Giới tính nam (M - Male) chiếm khoảng 49,01%. Như vậy, sự chênh lệch giữa hai nhóm giới tính là không đáng kể, cho thấy mẫu dữ liệu khá cân bằng về mặt giới tính.

2.1.2 .Trực quan hóa

library(ggplot2)
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
dt %>%
  group_by(Gender) %>%
  summarise(n = n()) %>%
  mutate(perc = round(n / sum(n) * 100, 1)) %>%
  ggplot(aes(x = "", y = perc, fill = Gender)) +
  geom_col(color = "black") +
  coord_polar("y") +
  geom_text(aes(x = 1.3, label = paste0(perc, "%")), 
            position = position_stack(vjust = 0.5), size = 5) +
  labs(caption = "Biểu đồ tròn thể hiện tỷ lệ giới tính") +
  theme_void() +
  theme(legend.title = element_blank()) +
  scale_fill_manual(values = c("F" = "deeppink",  
                               "M" = "yellow")) 

🔹 Thư viện sử dụng:

  • library(ggplot2): Dùng để vẽ biểu đồ.

  • library(dplyr): Dùng để xử lý, tổng hợp dữ liệu.

🔹 Xử lý dữ liệu:

  • group_by(Gender): Nhóm dữ liệu theo giới tính (F hoặc M).

  • summarise(n = n()): Đếm số lượng từng nhóm, lưu vào biến n.

  • mutate(perc = round(n / sum(n) * 100, 1)): Tạo thêm biến perc – tính phần trăm từng nhóm, làm tròn 1 chữ số thập phân.

🔹 Vẽ biểu đồ bằng ggplot:

  • ggplot(aes(x = ““, y = perc, fill = Gender)):

x = ““: Không cần phân chia trục x.

y = perc: Dùng phần trăm làm chiều cao.

fill = Gender: Tô màu theo giới tính.

🔹 Tạo hình dạng biểu đồ:

  • geom_col(color = “black”): Vẽ cột, thêm viền đen để phân biệt rõ từng lát bánh.

  • coord_polar(“y”): Chuyển biểu đồ cột thành biểu đồ tròn (hệ tọa độ cực).

🔹 Thêm nhãn vào lát bánh:

  • geom_text(aes(x = 1.3, label = paste0(perc, “%”)), …):

  • label = paste0(perc, “%”): Hiển thị số % kèm dấu phần trăm.

x = 1.3: Đặt nhãn hơi lệch tâm để dễ nhìn.

position_stack(vjust = 0.5): Căn giữa nhãn trong mỗi phần.

size = 5: Cỡ chữ vừa phải, dễ đọc.

🔹 Trang trí biểu đồ:

  • labs(caption = “Biểu đồ tròn thể hiện tỷ lệ giới tính”): Thêm chú thích bên dưới biểu đồ.

  • theme_void(): Loại bỏ trục, lưới, nhãn trục để biểu đồ sạch sẽ, tối giản.

  • theme(legend.title = element_blank()): Xóa tiêu đề chú giải, giữ lại nhãn (F, M).

  • scale_fill_manual(values = c(“F” = “red”,
    “M” = “yellow”)) : thêm màu sắc cho biểu đồ

Nhận xét: Biểu đồ tròn trên thể hiện tần suất phân bố giới tính trong bộ dữ liệu. Có thể thấy, phần màu hồng đại diện cho nữ (F) chiếm khoảng 51%, nhỉnh hơn một chút so với phần màu vàng tượng trưng cho nam (M). Mặc dù sự chênh lệch không lớn, biểu đồ vẫn cho thấy nữ giới có xu hướng chiếm ưu thế nhẹ so với nam giới trong tập dữ liệu này. Biểu đồ trực quan giúp người xem dễ dàng so sánh và nhận diện sự khác biệt nhỏ giữa hai nhóm giới tính.

2.2 .MaritalStatus

2.2.1 .Thống kê tần số và tần suất

Ta thực hiện thống kê tần số và tần suất cho biến MaritalStatus(tình trạng hôn nhân) được kết quả như sau:

table(dt$MaritalStatus)
## 
##    M    S 
## 6866 7193

Nhận xét: Dựa trên bảng kết quả, có 6,866 người đã kết hôn (M - Married) và 7,193 người còn độc thân (S - Single), cho thấy số lượng những người đã kết hôn ít hơn người còn độc thân.

table(dt$MaritalStatus)/sum(nrow(dt))
## 
##         M         S 
## 0.4883704 0.5116296

Nhận xét: Khi xét về tần suất, những người còn độc thân chiếm khoảng 51,16% trong toàn bộ dữ liệu, trong khi những người đã kết hôn chiếm khoảng 48,83%. Mức chênh lệch giữa 2 mức tình trạng hôn nhân là khoảng 3%.

2.2.2 .Trực quan hóa

dt %>%
  group_by(MaritalStatus) %>%
  summarise(n = n()) %>%
  mutate(perc = round(n / sum(n) * 100, 1)) %>%
  ggplot(aes(x = "", y = perc, fill = MaritalStatus)) +
  geom_col(color = "black") +
  coord_polar("y") +
  geom_text(aes(x = 1.3, label = paste0(perc, "%")), 
            position = position_stack(vjust = 0.5), size = 5) +
  labs(caption = "Biểu đồ tròn thể hiện tần suất tình trạng hôn nhân") +
  theme_void() +
  theme(legend.title = element_blank()) +
  scale_fill_manual(values = c("S" = "deeppink",  
                               "M" = "yellow"))

Nhận xét: Biểu đồ tròn trên thể hiện tần suất phân bố tình trạng hôn nhân trong bộ dữ liệu. Có thể thấy, phần màu hồng đại diện cho đã kết hôn(M) chiếm khoảng 51,2%, nhỉnh hơn một chút so với phần màu vàng tượng trưng cho độc thân(S). Mặc dù sự chênh lệch không lớn, biểu đồ vẫn cho thấy những người đã kết hôn có xu hướng chiếm ưu thế nhẹ so với những người độc thân trong tập dữ liệu này. Biểu đồ trực quan giúp người xem dễ dàng so sánh và nhận diện sự khác biệt nhỏ giữa hai nhóm tình trạng hôn nhân.

2.3 .Homeowner

2.3.1 .Thống kê tần số và tần suất

Ta thực hiện thống kê tần số và tần suất cho biến Homeowner (tình trạng sở hữu nhà) được kết quả như sau:

table(dt$Homeowner)
## 
##    N    Y 
## 5615 8444
table(dt$Homeowner)/sum(nrow(dt))
## 
##         N         Y 
## 0.3993883 0.6006117

Nhận xét: Dựa vào bảng tần số và tần suất của biến Homeowner (tình trạng sở hữu nhà) có thể thấy trong tổng số dữ liệu, có 8.444 người sở hữu nhà (ký hiệu Y) và 5.615 người không sở hữu nhà (ký hiệu N). Tính theo tỷ lệ, khoảng 60,06% số người trong tập dữ liệu là chủ sở hữu nhà, trong khi khoảng 39,94% còn lại không có nhà.

2.3.2 .Trực quan hóa

dt %>%
  group_by(Homeowner) %>%
  summarise(n = n()) %>%
  mutate(perc = round(n / sum(n) * 100, 1)) %>%
  ggplot(aes(x = "", y = perc, fill = Homeowner)) +
  geom_col(color = "black") +
  coord_polar("y") +
  geom_text(aes(x = 1.3, label = paste0(perc, "%")), 
            position = position_stack(vjust = 0.5), size = 5) +
  labs(caption = "Biểu đồ tròn thể hiện tần suất sở hữu nhà") +
  theme_void() +
  theme(legend.title = element_blank()) +
  scale_fill_manual(values = c("Y" = "deeppink",  
                               "N" = "yellow"))

Nhận xét: Biểu đồ tròn trên thể hiện tần suất sở hữu nhà trong bộ dữ liệu. Có thể thấy, phần màu hồng đại diện cho có sở hữu nhà (Y) chiếm khoảng 60,1%, nhỉnh hơn một chút so với phần màu vàng tượng trưng cho chưa sở hữu nhà (N). Có sự chênh lệch lênh đến khoảng 20% giữa 2 nhóm này, cho thấy đa số khách hàng là người có sở hữu nhà.

2.4 .AnnualIncome

2.4.1 .Thống kê tần số và tần suất

Ta thực hiện thống kê tần số và tần suất cho biến Gender (Giơi tính) được kết quả như sau:

table(dt$AnnualIncome)
## 
##   $10K - $30K $110K - $130K $130K - $150K       $150K +   $30K - $50K 
##          3090           643           760           273          4601 
##   $50K - $70K   $70K - $90K  $90K - $110K 
##          2370          1709           613
table(dt$AnnualIncome)/sum(nrow(dt))
## 
##   $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

2.4.2 .Trực quan hóa

dt %>% group_by(AnnualIncome) %>% summarise(n = n()) %>%
  ggplot(aes(x = n, y = AnnualIncome))+
  geom_col(fill='deeppink')+
  labs(y = "Các mức thu nhập hằng năm", x="Tần số")+
  labs(caption = "Biểu đồ tần số mức thu nhập hằng năm")+
  geom_text(aes(label =n), vjust=0, color = 'black')

Nhận xét: Từ biểu đồ, có thể thấy mức thu nhập từ 30.000 đến 50.000 USD/năm là nhóm chiếm tỷ lệ cao nhất, với 4.601 người, phản ánh đây là khoảng thu nhập phổ biến nhất trong bộ dữ liệu. Theo sau là nhóm 10.000–30.000 USD với 3.090 người, và 50.000–70.000 USD với 2.370 người, cho thấy phần lớn người trong tập dữ liệu có thu nhập thuộc nhóm trung bình – thấp. Các nhóm có thu nhập cao hơn như 70.000–90.000 USD và 90.000–110.000 USD ghi nhận lần lượt 1.709 và 613 người, thể hiện sự giảm dần đều về tần suất khi mức thu nhập tăng lên.Đáng chú ý, những nhóm thu nhập rất cao như 130.000–150.000 USD và trên 150.000 USD có số lượng lần lượt chỉ là 760 và 273 người, chiếm tỷ lệ rất nhỏ – phản ánh rằng thu nhập cao là điều ít phổ biến trong tập dữ liệu này. Tổng thể, biểu đồ cho thấy phân phối thu nhập có xu hướng tập trung ở mức trung bình, với hai đầu – thu nhập rất thấp và rất cao – chiếm tỷ lệ nhỏ. Đây là một mô hình điển hình của phân phối không đối xứng, nơi số đông tập trung ở mức thu nhập vừa phải, và chỉ một phần nhỏ rơi vào hai cực của phổ thu nhập.

Giải thích: dt %>%: sử dụng toán tử pipe (%>%) để nối các bước xử lý dữ liệu.

group_by(AnnualIncome): nhóm dữ liệu theo từng mức thu nhập hàng năm.

summarise(n = n()): đếm số lượng quan sát trong mỗi nhóm, lưu vào biến n.

ggplot(aes(x = n, y = AnnualIncome)): khởi tạo biểu đồ với trục x là tần số, trục y là mức thu nhập.

geom_col(fill = ‘deeppink’): vẽ biểu đồ cột nằm ngang, màu hồng đậm (deeppink) để nổi bật.

labs(y = …, x = …): đặt tên cho trục tung và trục hoành.

labs(caption = …): thêm chú thích bên dưới biểu đồ để mô tả nội dung.

geom_text(aes(label = n), vjust = 0, color = ‘black’): thêm nhãn số lượng ngay trên đầu mỗi cột, chữ màu đen để dễ đọc.

2.5 .City

2.5.1 .Thống kê tần số và tần suất

Ta thực hiện thống kê tần số và tần suất cho biến Gender (Giơi tính) được kết quả như sau:

table(dt$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
table(dt$City)/sum(nrow(dt))
## 
##      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

2.5.2 .Trực quan hóa

dt %>% group_by(City) %>% summarise(n = n()) %>%
  ggplot(aes(x = n, y = City))+
  geom_col(fill='#FF10F0')+
  labs(y = "Thành phố", x="Tần số")+
  labs(caption = "Biểu đồ tần số các thành phố")+
  geom_text(aes(label =n), vjust=0, color = 'black')

Nhận xét: Biểu đồ phản ánh tần số khách hàng phân bố theo các thành phố nơi họ sinh sống. Trong đó, San Andres dẫn đầu với 1.386 khách hàng, theo sau là Seattle (1.257) và Los Angeles (926) – cho thấy sự tập trung lớn tại các khu đô thị phát triển. Bên cạnh đó, một số thành phố như Spokane, San Francisco và Merida cũng ghi nhận lượng khách hàng khá cao (trên 800 người). Ngược lại, các thành phố như Guadalajara (75), San Francisco (130) và Walla Walla (160) nằm trong nhóm có số lượng khách hàng thấp nhất. Điều này cho thấy sự phân bố khách hàng không đồng đều, với xu hướng tập trung tại các thành phố lớn hoặc có mức độ phát triển kinh tế cao, trong khi các khu vực nhỏ hơn thu hút ít khách hàng hơn đáng kể.

2.6 .StateorProvince

2.6.1 .Thống kê tần số và tần suất

Ta thực hiện thống kê tần số và tần suất cho biến Gender (Giơi tính) được kết quả như sau:

table(dt$StateorProvince)
## 
##        BC        CA        DF  Guerrero   Jalisco        OR  Veracruz        WA 
##       809      2733       815       383        75      2262       464      4567 
##   Yucatan Zacatecas 
##       654      1297
table(dt$StateorProvince)/sum(nrow(dt))
## 
##          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

2.6.2 .Trực quan hóa

dt %>% group_by(StateorProvince) %>% summarise(n = n()) %>%
  ggplot(aes(x = n, y = StateorProvince))+
  geom_col(fill='#FFB6C1')+
  labs(y = "Tiểu bang", x="Tần số")+
  labs(caption = "Biểu đồ tần số các tiểu bang")+
  geom_text(aes(label =n), vjust=0, color = 'black')

Nhận xét:Biểu đồ thể hiện số lượng khách hàng phân bố theo từng tiểu bang. Có thể thấy: WA (Washington) dẫn đầu với 4.567 khách hàng, chiếm tỷ lệ áp đảo so với các tiểu bang khác. Theo sau là CA (California) với 2.733 khách hàng, và OR (Oregon) với 2.262 khách hàng – đây đều là những tiểu bang có dân số và mức độ đô thị hóa cao. Zacatecas cũng có số lượng khách hàng đáng kể (1.297 người), vượt nhiều tiểu bang còn lại. Ở nhóm trung bình, các tiểu bang như DF (815), BC (809) và Yucatan (654) có lượng khách hàng vừa phải. Trong khi đó, Jalisco chỉ có 75 khách hàng, là tiểu bang có tần số thấp nhất trong biểu đồ, cùng với Guerrero (383) và Veracruz (464) cũng thuộc nhóm có lượng khách hàng thấp.

2.7 .Country

2.7.1 .Thống kê tần số và tần suất

Ta thực hiện thống kê tần số và tần suất cho biến Gender (Giơi tính) được kết quả như sau:

table(dt$Country)
## 
## Canada Mexico    USA 
##    809   3688   9562
table(dt$Country)/sum(nrow(dt))
## 
##     Canada     Mexico        USA 
## 0.05754321 0.26232307 0.68013372

2.7.2 .Trực quan hóa

dt %>%
  group_by(Country) %>%
  summarise(n = n()) %>%
  mutate(perc = round(n / sum(n) * 100, 1)) %>%
  ggplot(aes(x = "", y = perc, fill = Country)) +
  geom_col(color = "black") +
  coord_polar("y") +
  geom_text(aes(x = 1.3, label = paste0(perc, "%")), 
            position = position_stack(vjust = 0.5), size = 5) +
  labs(caption = "Biểu đồ tròn thể hiện tần suất các quốc gia") +
  theme_void() +
  theme(legend.title = element_blank()) +
  scale_fill_manual(values = c("USA" = "deeppink",  
                               "Mexico" = "yellow",
                               "Canada"="blue"))

Nhận xét:Biểu đồ tròn trên thể hiện tần suất phân bố khách hàng theo quốc gia trong bộ dữ liệu. Dễ dàng nhận thấy: Phần màu hồng đại diện cho USA chiếm tỷ lệ lớn nhất, khoảng 68%, cho thấy phần lớn khách hàng đến từ Hoa Kỳ. Phần màu vàng tương ứng với Mexico, chiếm khoảng 26,2%, là nhóm đứng thứ hai về số lượng. Canada được biểu diễn bằng màu xanh, chiếm tỷ lệ thấp nhất – khoảng 5,8%, thể hiện số lượng khách hàng ở đây tương đối nhỏ.

2.8 .ProductFamily

2.8.1 .Thống kê tần số và tần suất

Ta thực hiện thống kê tần số và tần suất cho biến Gender (Giơi tính) được kết quả như sau:

table(dt$ProductFamily)
## 
##          Drink           Food Non-Consumable 
##           1250          10153           2656
table(dt$ProductFamily)/sum(nrow(dt))
## 
##          Drink           Food Non-Consumable 
##     0.08891102     0.72217085     0.18891813

2.8.2 .Trực quan hóa

dt %>%
  group_by(ProductFamily) %>%
  summarise(n = n()) %>%
  mutate(perc = round(n / sum(n) * 100, 1)) %>%
  ggplot(aes(x = "", y = perc, fill = ProductFamily)) +
  geom_col(color = "black") +
  coord_polar("y") +
  geom_text(aes(x = 1.3, label = paste0(perc, "%")), 
            position = position_stack(vjust = 0.5), size = 5) +
  labs(caption = "Biểu đồ tròn thể hiện tần suất danh mục sản phẩm") +
  theme_void() +
  theme(legend.title = element_blank()) +
  scale_fill_manual(values = c("Food" = "deeppink",  
                               "Non-Consumable" = "yellow",
                               "Drink"="blue"))

Nhận xét:Biểu đồ tròn trên thể hiện tần suất phân bố khách hàng theo danh mục sản phẩm trong bộ dữ liệu. Dễ dàng nhận thấy: Phần màu hồng đại diện cho Food chiếm tỷ lệ lớn nhất, khoảng 72,2%, cho thấy phần lớn khách hàng mua Food. Phần màu vàng tương ứng với Non-consumable, chiếm khoảng 18,9%, là nhóm đứng thứ hai về số lượng. drink được biểu diễn bằng màu xanh, chiếm tỷ lệ thấp nhất – khoảng 8,9%, thể hiện số lượng khách hàng ở đây tương đối nhỏ.

2.9 .ProductDepartment

2.9.1 .Thống kê tần số và tần suất

Ta thực hiện thống kê tần số và tần suất cho biến Gender (Giơi tính) được kết quả như sau:

table(dt$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
table(dt$ProductDepartment)/sum(nrow(dt))
## 
## 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

2.9.2 .Trực quan hóa

dt %>% group_by(ProductDepartment) %>% summarise(n = n()) %>%
  ggplot(aes(x = n, y = ProductDepartment))+
  geom_col(fill='#7F00FF')+
  labs(y = "Thông tin về bộ phận sản phẩm", x="Tần số")+
  labs(caption = "Biểu đồ tần số các bộ phận sản phẩm")+
  geom_text(aes(label =n), vjust=0.5, color = 'black')

Nhận xét: Biểu đồ cho thấy tần số xuất hiện của các bộ phận sản phẩm không đồng đều. Nhóm Produce (rau củ quả) có tần số cao nhất (1.994), tiếp theo là Snack Foods (1.600) và Household (1.420), phản ánh nhu cầu cao của người tiêu dùng với các sản phẩm thiết yếu và tiện lợi. Ngược lại, các bộ phận như Carousel (59), Checkout (82), và Meat (89) có tần số thấp, cho thấy ít được quan tâm hơn. Biểu đồ giúp nhận diện rõ nhóm sản phẩm được ưa chuộng và có thể hỗ trợ cho việc quản lý hàng hóa hiệu quả hơn.

2.10 .ProductCategory

2.10.1 .Thống kê tần số và tần suất

Ta thực hiện thống kê tần số và tần suất cho biến Gender (Giơi tính) được kết quả như sau:

table(dt$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
table(dt$ProductCategory)/sum(nrow(dt))
## 
##         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

2.10.2 .Trực quan hóa

dt %>% group_by(ProductCategory) %>% summarise(n = n()) %>%
  ggplot(aes(x = n, y = ProductCategory))+
  geom_col(fill='#BF00FF')+
  labs(y = "Thông tin về từng loại sản phẩm", x="Tần số")+
  labs(caption = "Biểu đồ tần số từng loại sản phẩm")+
  geom_text(aes(label =n), vjust=0.5, color = 'black')

Nhận xét: Biểu đồ cho thấy tần số xuất hiện của các bộ phận sản phẩm không đồng đều. Nhóm Vegetables (rau củ quả) có tần số cao nhất (1.728), tiếp theo là Snack Foods (1.600) và Diary (1903), phản ánh nhu cầu cao của người tiêu dùng với các sản phẩm thiết yếu và tiện lợi. Ngược lại, các bộ phận như Canned Oysters (35), Canned Shrimp (38), và Canned Sardines (40) có tần số thấp, cho thấy ít được quan tâm hơn. Biểu đồ giúp nhận diện rõ nhóm sản phẩm được ưa chuộng và có thể hỗ trợ cho việc quản lý hàng hóa hiệu quả hơn.


3 .Phần 3: Ước lượng Khoảng và Kiểm định Giả thuyết cho Tỷ lệ (Một biến)

3.1 .Xác định hạng mục Quan tâm

Ta chọn 3 hạng mục từ 3 biến của danh sách các biến định tính trên, bao gồm:

Hạng mục “F” của biến Gender - giới tính

Hạng mục “USA” của biến Country - quốc gia

Hạng mục “Food” của biến ProductFamily - Danh mục sản phẩm

3.2 .Hạng mục “F” của biến Gender - giới tính

Ta thực hiện khoảng ước lượng với độ tin cậy 95% cho tỷ lệ giới tính nữ (Gender=“F”), ta thực hiện như sau:

# Số người là nữ
n_female <- sum(dt$Gender == "F")

# Tổng số quan sát
n_total <- nrow(dt)

# Ước lượng khoảng tin cậy 95% cho tỷ lệ nữ
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

Từ bảng kết quả, ta có một số nhận xét sau: Tỷ lệ khách hàng là nữ được ước lượng từ dữ liệu là 0.5099, tức 50.99%. Khoảng tin cậy 95% cho tỷ lệ này là [50.17%, 51.82%], cho thấy độ chính xác cao và biên độ không quá rộng. Vì khoảng tin cậy không chứa giá trị 50%, cùng với p-value = 0.0182 < 0.05, ta có đủ bằng chứng để bác bỏ giả thuyết rằng tỷ lệ nữ là đúng bằng 50%. Có bằng chứng thống kê cho thấy tỷ lệ khách hàng nữ khác 50%, và cụ thể là cao hơn một chút. Dù mức chênh lệch không lớn, nhưng vẫn có ý nghĩa thống kê trong bối cảnh phân tích dữ liệu khách hàng.

Bài toán kiểm định

\[ \left\{ \begin{array}{ll} H_0: & \text{Tỷ lệ nữ trong tổng thể } = 0.5 \\ H_1: & \text{Tỷ lệ nữ trong tổng thể } \ne 0.5 \end{array} \right. \] Mức ý nghĩa 5%. Diễn giải kết quả kiểm định:

Nếu p-value < 0.05 ⇒ bác bỏ H0 ⇒ tỷ lệ nữ ≠ 50%, có ý nghĩa thống kê.

Nếu p-value ≥ 0.05 ⇒ Chấp nhận H0, không đủ bằng chứng để nói tỷ lệ nữ khác 50%.

Ta thấy p_value=0.0182 <0.05, bác bỏ H0. Vậy tỷ lệ nữ trong mẫu khác 0.5 tại mức ý nghĩa 5%.

3.3 .Hạng mục “USA” của biến Country - quốc gia

Ta thực hiện khoảng ước lượng với độ tin cậy 95% cho tỷ lệ giới tính nữ (Country=“USA”), ta thực hiện như sau:

# Số người sống ở USA
n_USA <- sum(dt$Country == "USA")

# Ước lượng khoảng tin cậy 95% cho tỷ lệ sống ở USA
prop.test(n_USA, n_total, conf.level = 0.95)
## 
##  1-sample proportions test with continuity correction
## 
## data:  n_USA out of n_total, null probability 0.5
## X-squared = 1824, df = 1, p-value < 2.2e-16
## alternative hypothesis: true p is not equal to 0.5
## 95 percent confidence interval:
##  0.6723397 0.6878289
## sample estimates:
##         p 
## 0.6801337

Từ bảng kết quả, ta có một số nhận xét sau: Tỷ lệ khách hàng sống tại USA được ước lượng từ dữ liệu là 0.6801, tức 68.01%. Khoảng tin cậy 95% cho tỷ lệ này là [67.23%;68.78%], cho thấy độ chính xác cao và biên độ không quá rộng. Ta có thể hiểu rằng cứ 10 người khách là có gần 7 người sống ở Mỹ.

Bài toán kiểm định

\[ \left\{ \begin{array}{ll} H_0: & \text{Tỷ lệ khách hàng sống ở Mỹ } = 0.5 \\ H_1: & \text{Tỷ lệ khách hàng sống ở Mỹ } \ne 0.5 \end{array} \right. \] Mức ý nghĩa 5%. Diễn giải kết quả kiểm định:

Ta thấy p_value=2.2e-16<0.05, bác bỏ H0. Vậy tỷ lệ khách hàng sống ở Mỹ khác 0.5 tại mức ý nghĩa 5%.

3.3.1 .Hạng mục “Food” của biến ProductFamily - Danh mục sản phẩm

Ta thực hiện khoảng ước lượng với độ tin cậy 95% cho tỷ lệ giới tính nữ (ProductFamily=“Food”), ta thực hiện như sau:

# Số người sống ở USA
n_Food <- sum(dt$ProductFamily == "Food")

# Ước lượng khoảng tin cậy 95% cho tỷ lệ sống ở USA
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

Từ bảng kết quả, ta có một số nhận xét sau: Tỷ lệ khách hàng mua Food được ước lượng từ dữ liệu là 0.7221, tức 72.21%. Khoảng tin cậy 95% cho tỷ lệ này là [71.48%;72.95%], cho thấy độ chính xác cao và biên độ không quá rộng. Ta có thể hiểu rằng cứ 10 người khách là có hơn 7 người mua food.

Bài toán kiểm định

\[ \left\{ \begin{array}{ll} H_0: & \text{Tỷ lệ khách hàng mua Food} = 0.5 \\ H_1: & \text{Tỷ lệ khách hàng mua Food } \ne 0.5 \end{array} \right. \] Mức ý nghĩa 5%.

Diễn giải kết quả kiểm định:

Ta thấy p_value=2.2e-16<0.05, bác bỏ H0. Vậy tỷ lệ khách hàng mua food khác 0.5 tại mức ý nghĩa 5%.


4 .Phần 4. Phân tích Mối quan hệ giữa Hai biến Định tính (Bivariate Analysis)

Ta chọn 3 cặp biến định tính để Phân tích mối quan hệ giữa Hai biến Định tính (Bivariate Analysis) như sau:

  • Gender và ProductFamily

  • AnnualIncome và Homeowner

  • Country và MaritalStatus

4.1 . Cặp biến Gender và ProductFamily

4.1.1 .Thống kê tần số và tần suất

Ta tạo bảng tần suất chéo (contingency table) cho hai biến Gender và ProductFamily như sau:

# Bảng tần suất chéo
table_gender_product <- table(dt$Gender, dt$ProductFamily)
table_gender_product
##    
##     Drink Food Non-Consumable
##   F   669 5149           1352
##   M   581 5004           1304

Kết quả diễn giải như sau:

  • Có 669 giao dịch mua drink, 5149 mua food, 1352 mua Non-Consumable là nữ.

  • Có 581 giao dịch mua drink, 5004 mua food, 1304 mua non-Consumable là nam.

# Hiển thị bảng tần suất chéo theo tỷ lệ hàng
prop.table(table_gender_product, margin = 1)
##    
##          Drink       Food Non-Consumable
##   F 0.09330544 0.71813110     0.18856346
##   M 0.08433735 0.72637538     0.18928727

Kết quả diễn giải như sau:

  • Ở khách hàng nữ: Có 9.33% giao dịch mua drink, 71.81% mua food, 18.86% mua Non-Consumable.

  • Ở Khách hàng nam: Có 8.43% giao dịch mua drink, 72.64 mua food, 18.93 mua non-Consumable là nam.

4.1.2 .Trực quan hóa

Ta thể hiện bằng biểu đồ cột đôi:

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ lubridate 1.9.4     ✔ tibble    3.2.1
## ✔ purrr     1.0.4     ✔ tidyr     1.3.1
## ✔ readr     2.1.5     
## ── 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
# Tạo dữ liệu
df <- tribble(
  ~Gender, ~Drink, ~Food, ~Non_Consumable,
  "F",     0.09330544, 0.71813110, 0.18856346,
  "M",     0.08433735, 0.72637538, 0.18928727
)

# Chuyển sang dạng long
df_long <- df %>%
  pivot_longer(cols = -Gender, names_to = "Category", values_to = "Proportion")

# Vẽ biểu đồ có hiển thị số trên cột
ggplot(df_long, aes(x = Category, y = Proportion, fill = Gender)) +
  geom_col(position = position_dodge(width = 0.9)) +
  geom_text(
    aes(label = scales::percent(Proportion, accuracy = 1)),
    position = position_dodge(width = 0.9),
    vjust = -0.5, size = 4
  ) +
  scale_y_continuous(labels = scales::percent_format(accuracy = 1), limits = c(0, 0.8)) +
  labs(
    title = "Tỷ lệ tiêu dùng theo giới tính và nhóm hàng",
    x = "Nhóm hàng",
    y = "Tỷ lệ (%)"
  ) +
  scale_fill_manual(values = c("F" = "violet", "M" = "green")) +
  theme_minimal() +
  theme(text = element_text(size = 12))

Nhận xét: Biểu đồ thể hiện tỷ lệ tiêu dùng theo giới tính (nam và nữ) đối với ba nhóm hàng: Drink, Food, và Non_Consumable. Nhìn chung, nhóm hàng Food chiếm tỷ lệ cao nhất, với 72% ở nữ và 73% ở nam. Trong khi đó, nhóm hàng Drink có tỷ lệ tiêu dùng thấp nhất, lần lượt là 9% ở nữ và 8% ở nam. Đối với nhóm hàng Non_Consumable, tỷ lệ tiêu dùng ở cả hai giới đều bằng nhau là 19%. Sự khác biệt giữa nam và nữ không đáng kể ở cả ba nhóm hàng. Điều này cho thấy hành vi tiêu dùng của nam và nữ tương đối tương đồng trong từng nhóm sản phẩm.

Giải thích câu lệnh:

  • library(tidyverse) dùng để nạp gói tidyverse, bao gồm các công cụ xử lý dữ liệu và vẽ biểu đồ trong R.

  • df <- tribble(…) tạo một bảng dữ liệu nhỏ với các cột giới tính và tỷ lệ tiêu dùng cho từng nhóm hàng.

  • df_long <- df %>% pivot_longer(…) chuyển dữ liệu từ dạng rộng sang dạng dài, gom các cột nhóm hàng thành cột Category và giá trị tỉ lệ vào cột Proportion.

  • ggplot(df_long, aes(…)) khởi tạo biểu đồ với trục x là nhóm hàng, trục y là tỉ lệ, và màu sắc theo giới tính.

  • geom_col(position = position_dodge(width = 0.9)) vẽ các cột dữ liệu, đặt các cột của từng giới tính cạnh nhau mà không chồng lên.

  • geom_text(aes(label = scales::percent(Proportion, accuracy = 1)), position = position_dodge(width = 0.9), vjust = -0.5, size = 4) thêm nhãn phần trăm lên trên mỗi cột, định dạng phần trăm với 1 chữ số thập phân, căn chỉnh nhãn nằm trên cột, cỡ chữ 4.

  • scale_y_continuous(labels = scales::percent_format(accuracy = 1), limits = c(0, 0.8)) định dạng trục y hiển thị dưới dạng phần trăm và giới hạn giá trị từ 0 đến 80%.

  • labs(title = …, x = …, y = …) đặt tiêu đề biểu đồ và tên cho trục x, trục y.

  • scale_fill_manual(values = c(“F” = “violet”, “M” = “green”)) tùy chỉnh màu sắc cho giới tính nữ là tím và nam là xanh lá.

  • theme_minimal() chọn giao diện biểu đồ đơn giản, tinh gọn.

  • theme(text = element_text(size = 12)) thiết lập kích cỡ chữ cho toàn bộ biểu đồ là 12.

4.1.3 .Kiểm định Thống kê - Kiểm định Chi-bình phương

chisq.test(table_gender_product)
## 
##  Pearson's Chi-squared test
## 
## data:  table_gender_product
## X-squared = 3.5185, df = 2, p-value = 0.1722

Bài toán kiểm định

\[ \left\{ \begin{array}{ll} H_0: & \text{Giới tính và nhóm hàng mua là độc lập}\\ H_1: & \text{Giới tính và nhóm hàng mua có liên quan} \end{array} \right. \]

Mức ý nghĩa 5%.

Ta thấy p_value=0.1722>0.05, chấp nhận H0. Vậy Giới tính và nhóm hàng mua là độc lập tại mức ý nghĩa 5%.

4.2 .Cặp biến AnnualIncome và Homeowner

4.2.1 .Thống kê tần số và tần suất

Ta tạo bảng tần suất chéo (contingency table) cho hai biến AnnualIncome và Homeownernhư sau:

# Bảng tần suất chéo
table_annual_home <- table(data$AnnualIncome, data$Homeowner)
table_annual_home
##                
##                    N    Y
##   $10K - $30K   1359 1731
##   $110K - $130K  119  524
##   $130K - $150K  136  624
##   $150K +         48  225
##   $30K - $50K   2087 2514
##   $50K - $70K   1063 1307
##   $70K - $90K    686 1023
##   $90K - $110K   117  496
# Hiển thị bảng tần suất chéo theo tỷ lệ hàng
prop.table(table_annual_home, margin = 1)
##                
##                         N         Y
##   $10K - $30K   0.4398058 0.5601942
##   $110K - $130K 0.1850700 0.8149300
##   $130K - $150K 0.1789474 0.8210526
##   $150K +       0.1758242 0.8241758
##   $30K - $50K   0.4535970 0.5464030
##   $50K - $70K   0.4485232 0.5514768
##   $70K - $90K   0.4014043 0.5985957
##   $90K - $110K  0.1908646 0.8091354

4.2.2 .Trực quan hóa

library(tidyverse)

# Tạo dữ liệu
df <- tribble(
  ~Income, ~N, ~Y,
  "$10K - $30K",   1359, 1731,
  "$30K - $50K",   2087, 2514,
  "$50K - $70K",   1063, 1307,
  "$70K - $90K",    686, 1023,
  "$90K - $110K",   117, 496,
  "$110K - $130K",  119, 524,
  "$130K - $150K",  136, 624,
  "$150K +",         48, 225
)

# Chuyển sang long format
df_long <- df %>%
  pivot_longer(cols = c(N, Y), names_to = "Ownership", values_to = "Count")

# Vẽ biểu đồ
ggplot(df_long, aes(x = Income, y = Count, fill = Ownership)) +
  geom_col(position = position_dodge(width = 0.9)) +
  geom_text(
    aes(label = Count),
    position = position_dodge(width = 0.9),
    vjust = -0.3,
    size = 3.5
  ) +
  scale_fill_manual(values = c("N" = "pink", "Y" = "#00BFC4")) +
  labs(
    title = "Số người sở hữu và không sở hữu nhà theo thu nhập",
    x = "Thu nhập hằng năm",
    y = "Số lượng người",
    fill = "Sở hữu nhà"
  ) +
  theme_minimal() +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1),
    text = element_text(size = 12)
  )

Nhận xét: Biểu đồ cho thấy sự phân bố số lượng người sở hữu và không sở hữu nhà theo từng nhóm thu nhập hằng năm. Nhóm thu nhập 30 nghìn – 50 nghìn USD có số người sở hữu nhà nhiều nhất, với 2.514 người, so với 2.087 người không sở hữu. Ngược lại, nhóm có số người sở hữu nhà ít nhất là trên 150 nghìn USD, chỉ với 225 người sở hữu và 48 người không sở hữu. Ngoài ra, các nhóm thu nhập từ 50 nghìn – 70 nghìn USD và 10 nghìn – 30 nghìn USD cũng có số lượng người sở hữu nhà tương đối cao (lần lượt là 1.307 và 1.731 người). Các nhóm thu nhập cao từ 70 nghìn USD trở lên cho thấy xu hướng sở hữu nhà tăng đều, nhưng không vượt quá mức đỉnh như nhóm 30–50 nghìn USD. Điều này cho thấy mức thu nhập trung bình vẫn là nhóm có tỷ lệ sở hữu nhà cao nhất trong tổng thể dân cư.

Giải thích câu lệnh:

  • library(tidyverse) nạp gói tidyverse để dùng các hàm xử lý dữ liệu và vẽ biểu đồ trong R.

  • df <- tribble(…) tạo bảng dữ liệu với 3 cột: mức thu nhập (Income), số người không sở hữu nhà (N), và số người sở hữu nhà (Y).

  • df_long <- df %>% pivot_longer(cols = c(N, Y), names_to = “Ownership”, values_to = “Count”) chuyển dữ liệu từ dạng rộng sang dạng dài, gom 2 cột N và Y thành 1 cột Ownership lưu giá trị “N” hoặc “Y”, và cột Count chứa số lượng tương ứng.

  • ggplot(df_long, aes(x = Income, y = Count, fill = Ownership)) khởi tạo biểu đồ với trục x là thu nhập, trục y là số lượng người, và màu cột phân biệt theo việc sở hữu nhà hay không.

  • geom_col(position = position_dodge(width = 0.9)) vẽ các cột dữ liệu, các cột của nhóm sở hữu nhà và không sở hữu nhà được đặt cạnh nhau.

  • geom_text(aes(label = Count), position = position_dodge(width = 0.9), vjust = -0.3, size = 3.5) thêm nhãn số lượng người lên trên các cột, căn chỉnh vị trí nhãn hơi cao hơn cột, cỡ chữ 3.5.

  • scale_fill_manual(values = c(“N” = “pink”, “Y” = “#00BFC4”)) tùy chỉnh màu cột cho nhóm không sở hữu nhà là hồng, nhóm sở hữu nhà là màu xanh da trời.

  • labs(title = …, x = …, y = …, fill = …) đặt tiêu đề biểu đồ, tên trục x, trục y, và tên chú giải màu sắc.

  • theme_minimal() chọn giao diện biểu đồ đơn giản, sạch sẽ.

  • theme(axis.text.x = element_text(angle = 45, hjust = 1), text = element_text(size = 12)) xoay chữ trục x nghiêng 45 độ cho dễ đọc và đặt cỡ chữ chung trong biểu đồ là 12.

4.2.3 .Kiểm định Chi-bình phương

chisq.test(table_annual_home)
## 
##  Pearson's Chi-squared test
## 
## data:  table_annual_home
## X-squared = 546.37, df = 7, p-value < 2.2e-16

Bài toán kiểm định

\[ \left\{ \begin{array}{ll} H_0: & \text{Thu nhập hằng năm và tình trạng sở hữu nhà là độc lập}\\ H_1: & \text{Thu nhập hằng năm và tình trạng sở hữu nhà có liên quan } \end{array} \right. \]

Mức ý nghĩa 5%.

Ta thấy p_value=2.2e-16<0.05, bác bỏ H0. Vậy Thu nhập hằng năm và tình trạng sở hữu nhà có liên quan tại mức ý nghĩa 5%.

4.3 . Cặp biến Country và MaritalStatus

4.3.1 .Thống kê tần số và tần suất

Ta tạo bảng tần suất chéo (contingency table) cho hai biến Country và MaritalStatus như sau:

# Bảng tần suất chéo
table_country_marital <- table(dt$Country, dt$MaritalStatus)
table_country_marital
##         
##             M    S
##   Canada  387  422
##   Mexico 1799 1889
##   USA    4680 4882
# Hiển thị bảng tần suất chéo theo tỷ lệ hàng
prop.table(table_country_marital, margin = 1)
##         
##                  M         S
##   Canada 0.4783684 0.5216316
##   Mexico 0.4877983 0.5122017
##   USA    0.4894374 0.5105626

4.3.2 .Trực quan hóa

library(tidyverse)

# Dữ liệu
df <- tribble(
  ~Country, ~M, ~S,
  "Canada", 387, 422,
  "Mexico", 1799, 1889,
  "USA",    4680, 4882
)

# Dài dòng để vẽ
df_long <- df %>%
  pivot_longer(cols = c(M, S), names_to = "MaritalStatus", values_to = "Count")

# Vẽ biểu đồ
ggplot(df_long, aes(x = Country, y = Count, fill = MaritalStatus)) +
  geom_col(position = position_dodge(width = 0.8)) +
  geom_text(
    aes(label = Count),
    position = position_dodge(width = 0.8),
    vjust = -0.3,
    size = 4
  ) +
  scale_fill_manual(values = c("M" = "#00B", "S" = "orange")) +
  labs(
    title = "Tình trạng hôn nhân theo quốc gia",
    x = "Quốc gia",
    y = "Số lượng người",
    fill = "Tình trạng"
  ) +
  theme_minimal() +
  theme(text = element_text(size = 12))

Nhận xét: Biểu đồ thể hiện tình trạng hôn nhân theo quốc gia cho thấy sự khác biệt rõ rệt về số lượng người đã kết hôn (ký hiệu M) và người độc thân (S) tại ba quốc gia: Canada, Mexico và Hoa Kỳ. Trong đó, Hoa Kỳ là quốc gia có số lượng người thuộc cả hai tình trạng hôn nhân cao nhất, với 4.680 người đã kết hôn và 4.882 người độc thân. Điều này phản ánh quy mô dân số lớn của Mỹ so với hai quốc gia còn lại. Mexico xếp thứ hai, với số lượng người đã kết hôn là 1.799 người, trong khi số người độc thân là 1.889 người – chênh lệch không nhiều, nhưng vẫn cho thấy xu hướng số người chưa lập gia đình chiếm ưu thế nhẹ. Canada có quy mô thấp nhất, khi chỉ ghi nhận 387 người đã kết hôn và 422 người độc thân, cho thấy quy mô mẫu khảo sát hoặc dân số tại đây nhỏ hơn rõ rệt so với hai nước còn lại. Tổng thể, ở cả ba quốc gia, số người độc thân đều cao hơn đôi chút so với người đã kết hôn, phản ánh một phần xu hướng sống độc thân hoặc kết hôn muộn đang ngày càng phổ biến tại nhiều quốc gia hiện nay.

4.3.3 .Kiểm định Chi bình phương

chisq.test(table_country_marital)
## 
##  Pearson's Chi-squared test
## 
## data:  table_country_marital
## X-squared = 0.3723, df = 2, p-value = 0.8301

Bài toán kiểm định

\[ \left\{ \begin{array}{ll} H_0: & \text{Quốc gia đang sống và tình trạng hôn nhân là độc lập}\\ H_1: & \text{Quốc gia đang sống và tình trạng hôn nhân có liên quan } \end{array} \right. \]

Mức ý nghĩa 5%.

Ta thấy p_value=0.8301>0.05, chấp nhận H0. Vậy Quốc gia đang sống và tình trạng hôn nhân là độc lập tại mức ý nghĩa 5%.


5 .Phần 5. Tổng kết và Thảo luận

5.1 .Tóm tắt những phát hiện chính

  • Tóm tắt kết quả phân tích tỉ lệ chi tiêu theo loại hàng hóa, phân theo giới tính: Cả nữ (F) và nam (M) đều chi tiêu nhiều nhất vào nhóm Food (đồ ăn), chiếm khoảng 72% tổng chi tiêu.mNhóm Non-Consumable (hàng không tiêu thụ nhanh) chiếm khoảng 19% ở cả hai giới. Nhóm Drink (đồ uống) chiếm tỷ lệ thấp nhất, khoảng 8.4% đến 9.3%, nữ giới chi tiêu cho đồ uống nhỉnh hơn nam giới một chút. Tỷ lệ chi tiêu vào các nhóm hàng hóa giữa hai giới khá tương đồng, với sự khác biệt nhỏ ở nhóm Drink và Food.

  • Phân bố theo quốc gia và tình trạng hôn nhân: Số người độc thân (S) và kết hôn (M) tại ba quốc gia (Canada, Mexico, USA) có sự phân bố khá cân bằng. USA có số lượng người cao nhất ở cả hai nhóm, tiếp theo là Mexico và Canada.

  • Tỷ lệ sở hữu nhà theo thu nhập: Tỷ lệ sở hữu nhà (Y) tăng dần theo nhóm thu nhập cao hơn. Ví dụ, nhóm thu nhập từ $150K+ có tỷ lệ sở hữu nhà trên 82%, trong khi nhóm thấp nhất ($10K - $30K) chỉ khoảng 56%. Ngược lại, tỷ lệ không sở hữu nhà (N) giảm khi thu nhập tăng lên, cho thấy thu nhập ảnh hưởng tích cực đến khả năng sở hữu nhà.

5.2 .Hạn chế của phân tích

  • Tập trung chủ yếu vào biến định tính: Phân tích chỉ dựa trên các đặc điểm định tính như giới tính, tình trạng hôn nhân, quốc tịch mà chưa lồng ghép các biến định lượng như thu nhập, doanh thu hoặc khối lượng bán hàng, làm hạn chế khả năng hiểu rõ hơn về hành vi người tiêu dùng.

  • Dữ liệu chưa được chuẩn hóa đầy đủ: Một số thông tin quan trọng như thu nhập vẫn còn dưới dạng chuỗi ký tự, cần được chuyển đổi sang dạng số hoặc phân nhóm hợp lý để thuận tiện cho các phân tích chuyên sâu hơn.

  • Chưa khai thác được yếu tố thời gian: Dù có dữ liệu về ngày mua hàng, nhưng hiện chưa thực hiện phân tích theo chu kỳ thời gian như tháng, quý hoặc năm để phát hiện các xu hướng hay đặc điểm mùa vụ.

5.3 .Đề xuất

  • Nhắm mục tiêu theo thu nhập và sở hữu nhà: Nhóm khách hàng có thu nhập cao (trên $110K) có tỷ lệ sở hữu nhà lớn, có thể ưu tiên các sản phẩm cao cấp, dịch vụ tiện ích hoặc gói khuyến mãi đặc biệt hướng tới nhóm này.Với nhóm thu nhập thấp hơn, tập trung phát triển các sản phẩm giá tốt, chương trình ưu đãi hấp dẫn để tăng khả năng mua sắm.

  • Phân loại và điều chỉnh danh mục sản phẩm: Vì nhóm Food chiếm tỷ trọng lớn trong chi tiêu của cả nam và nữ, doanh nghiệp nên đảm bảo cung cấp đa dạng, chất lượng tốt trong danh mục thực phẩm. Nhóm Non-Consumable cũng chiếm tỷ lệ đáng kể, cần chú trọng đến các sản phẩm thiết yếu và các mặt hàng lâu bền phù hợp với nhu cầu đa dạng của khách hàng.

  • Chiến dịch marketing theo giới tính và hành vi tiêu dùng: Chiến lược quảng cáo có thể nhấn mạnh vào các sản phẩm đồ uống nhiều hơn với nhóm nữ (F) vì tỷ lệ chi tiêu nhóm này nhỉnh hơn so với nam. Phân tích tình trạng hôn nhân và quốc gia để cá nhân hóa nội dung marketing, ví dụ ưu đãi riêng cho nhóm độc thân hoặc nhóm đã kết hôn, phù hợp từng khu vực.

  • Khai thác yếu tố thời gian: Lên kế hoạch các chương trình khuyến mãi theo mùa, lễ, hoặc các chu kỳ tiêu dùng để tối ưu hóa doanh thu dựa trên xu hướng thời gian mà bạn sẽ phân tích thêm.

  • Xây dựng chương trình khách hàng trung thành: Dựa trên các nhóm có hành vi mua sắm ổn định và khả năng chi trả cao, thiết kế các chương trình tích điểm, ưu đãi cá nhân hóa để tăng sự gắn bó và duy trì khách hàng lâu dài.

5.4 .Câu hỏi mở

1. Hành vi mua hàng thay đổi thế nào theo mùa vụ hoặc dịp lễ?

2. Các yếu tố kinh tế như thu nhập hay lạm phát ảnh hưởng ra sao đến chi tiêu?

3. Có thể phân nhóm khách hàng hiệu quả hơn dựa trên đặc điểm đa chiều không?

4. Yếu tố nào thúc đẩy khách hàng trung thành hoặc khiến họ rời bỏ siêu thị?

5. Loại hình khuyến mãi nào hiệu quả nhất với từng nhóm khách hàng?

x <- table(dt $Country)

prop.test(c(x[["Canada"]],x[["Mexico"]]),c(sum(x),sum(x)),correct = TRUE)
## 
##  2-sample test for equality of proportions with continuity correction
## 
## data:  c(x[["Canada"]], x[["Mexico"]]) out of c(sum(x), sum(x))
## X-squared = 2192.5, df = 1, p-value < 2.2e-16
## alternative hypothesis: two.sided
## 95 percent confidence interval:
##  -0.2130785 -0.1964812
## sample estimates:
##     prop 1     prop 2 
## 0.05754321 0.26232307

5.5 .Rủi ro tương đối (Relative Risk)

v <- table(dt$Homeowner, dt$MaritalStatus)
addmargins(v)
##      
##           M     S   Sum
##   N    1719  3896  5615
##   Y    5147  3297  8444
##   Sum  6866  7193 14059

Nhận xét:

  • Trong số 6866 người đã kết hôn (M), có 5147 người sở hữu nhà và 1719 người không sở hữu nhà.

  • Trong số 7193 người độc thân (S), chỉ có 3297 người sở hữu nhà, còn 3896 người không sở hữu nhà.

  • Tỷ lệ sở hữu nhà cao hơn rõ rệt ở nhóm đã kết hôn so với nhóm độc thân.

  • Điều này có thể gợi ý rằng việc đã kết hôn có liên quan tích cực đến khả năng sở hữu nhà.

  • hàm table() dùng để tạo bảng chéo (contingency table) giữa hai biến:

  • addmargins(v): Thêm tổng hàng và tổng cột vào bảng v

## Lệnh này tính relativerisk cho vị trí 12 và 22
library(DescTools)
RelRisk(v) #package DescTools
## [1] 0.5022503
  • hàm RelRisk() từ package DescTools để tính rủi ro tương đối (Relative Risk):

Ý nghĩa: Nhóm Single (cột thứ 2 trong bảng v) có nguy cơ sở hữu nhà thấp hơn so với nhóm Married (cột thứ 1), với tỷ lệ là: RR=0.502. Nói cách khác, người độc thân chỉ có khoảng 50.2% khả năng sở hữu nhà so với người đã kết hôn.

## Lệnh này sẽ tính relativerisk cho vị trí 22 và 12
library(epitools)
riskratio(v) #package epitools
## $data
##        
##            M    S Total
##   N     1719 3896  5615
##   Y     5147 3297  8444
##   Total 6866 7193 14059
## 
## $measure
##    risk ratio with 95% C.I.
##      estimate     lower     upper
##   N 1.0000000        NA        NA
##   Y 0.5627319 0.5451115 0.5809219
## 
## $p.value
##    two-sided
##     midp.exact  fisher.exact    chi.square
##   N         NA            NA            NA
##   Y          0 1.822183e-277 3.663022e-272
## 
## $correction
## [1] FALSE
## 
## attr(,"method")
## [1] "Unconditional MLE & normal approximation (Wald) CI"

Khoảng tin cậy 95% cho RR là [0.5451, 0.5809] và giá trị p gần bằng 0, cho thấy sự khác biệt này là có ý nghĩa thống kê.

➡️ Diễn giải: Người độc thân cónhà có khoảng 56.3% khả năng sở hữu nhà so vớingười độc thân không có nhà.

## Khi có thêm tham số rev = 'c' thì sẽ thực hiện việc
## đổi chỗ 2 cột trong bảng ngẫu nhiên.
riskratio(v, rev = 'c') #package epitools
## $data
##        
##            S    M Total
##   N     3896 1719  5615
##   Y     3297 5147  8444
##   Total 7193 6866 14059
## 
## $measure
##    risk ratio with 95% C.I.
##     estimate    lower    upper
##   N 1.000000       NA       NA
##   Y 1.991039 1.907395 2.078352
## 
## $p.value
##    two-sided
##     midp.exact  fisher.exact    chi.square
##   N         NA            NA            NA
##   Y          0 1.822183e-277 3.663022e-272
## 
## $correction
## [1] FALSE
## 
## attr(,"method")
## [1] "Unconditional MLE & normal approximation (Wald) CI"

Giải thích: rev = ‘c’ nghĩa là reverse columns → Đổi chỗ 2 cột trong bảng v.Nếu ban đầu là [Married | Single], thì giờ thành [Single | Married]. Điều này thay đổi nhóm đối chiếu, dẫn đến thay đổi kết quả tính Relative Risk (RR).

Nhận xét: Sau khi sử dụng hàm riskratio(v, rev = ‘c’) từ package epitools, ta thu được kết quả: RR (Risk Ratio) = 1.991. Khoảng tin cậy 95% (95% CI): [1.907, 2.078]. P-value ≈ 0, cho thấy sự khác biệt có ý nghĩa thống kê mạnh. Như vậy, ta có thể kết luận rằng người đã kết hôn có khả năng sở hữu nhà cao gần gấp 2 lần so với người độc thân. Nói cách khác, trạng thái hôn nhân có liên quan rõ rệt đến khả năng sở hữu nhà, với người đã kết hôn có xác suất sở hữu nhà cao hơn đáng kể.

Kết luận: Nếu đảo cột, kết quả RR sẽ nghịch đảo lại (RR mới ≈ 1 / RR cũ).

## Câu lệnh này sẽ cung cấp nhiều thông tin hơn và thuận tiện hơn
## khi chúng ta biết thêm khái niệm oddratio trong phần tiếp theo
epitab(v, method = 'riskratio', rev = 'c')
## $tab
##    
##        S        p0    M        p1 riskratio    lower    upper       p.value
##   N 3896 0.6938557 1719 0.3061443  1.000000       NA       NA            NA
##   Y 3297 0.3904548 5147 0.6095452  1.991039 1.907395 2.078352 1.822183e-277
## 
## $measure
## [1] "wald"
## 
## $conf.level
## [1] 0.95
## 
## $pvalue
## [1] "fisher.exact"

Diễn giải: Tỷ lệ sở hữu nhà ở người đã kết hôn (M) là \(p_1 = 0.610\) cao hơn nhiều so với người độc thân (S) là \(p_0 = 0.390\). Rủi ro tương đối (RR) giữa hai nhóm là: RR = 1.991

→ Nghĩa là người đã kết hôn có khả năng sở hữu nhà cao gấp gần 2 lần so với người độc thân.

Khoảng tin cậy 95%: \([1.907,\ 2.078]\) → Hẹp và ổn định.

P-value: \(< 2.2 \times 10^{-16}\) → Có ý nghĩa thống kê rất mạnh.

Giải thích từng phần:

  • epitab() là hàm trong package epitools dùng để phân tích bảng chéo 2 chiều (2x2), tính toán các chỉ số thống kê như Risk Ratio, Odds Ratio, và xác suất.

  • v: là bảng chéo bạn đã tạo từ dữ liệu, ví dụ như bảng giữa Homeowner và MaritalStatus.

  • method = ‘riskratio’: yêu cầu hàm tính Relative Risk (RR) hay còn gọi là Rủi ro tương đối giữa hai nhóm.

  • rev = ‘c’: đảo cột của bảng (thay đổi thứ tự nhóm so sánh). Nếu cột ban đầu là M, S thì sau lệnh này sẽ thành S, M. Điều này làm thay đổi nhóm tham chiếu trong phân tích (ai là nhóm so với ai).

  • $tab: chỉ trích xuất phần bảng kết quả chính (không lấy toàn bộ output của epitab() mà chỉ lấy bảng tổng hợp).

5.6 .Tỷ lệ chênh (Odd Ratio)

## Câu lệnh này sẽ tính oddratio cho vị trí 11 và 21
OddsRatio(v) #package DescTools
## [1] 0.2826322

Kết quả OddsRatio(v) = 0.2826322 được tính từ bảng chéo giữa biến Homeowner (sở hữu nhà) và MaritalStatus (tình trạng hôn nhân). Đây là tỷ số odds so sánh khả năng là người độc thân giữa hai nhóm: không sở hữu nhà và có sở hữu nhà. Giá trị OR = 0.2826 < 1 cho thấy odds là người độc thân ở nhóm không sở hữu nhà thấp hơn đáng kể so với nhóm có sở hữu nhà. Cụ thể, odds là độc thân ở nhóm không sở hữu nhà chỉ bằng khoảng 28.26% so với nhóm có nhà. Nói cách khác, trong dữ liệu này, người có nhà có xu hướng độc thân nhiều hơn người không có nhà.

## Câu lệnh này sẽ tính oddratio cho vị trí 22 và 12
oddsratio(v) #package epitools
## $data
##        
##            M    S Total
##   N     1719 3896  5615
##   Y     5147 3297  8444
##   Total 6866 7193 14059
## 
## $measure
##    odds ratio with 95% C.I.
##     estimate     lower     upper
##   N 1.000000        NA        NA
##   Y 0.282673 0.2630995 0.3036164
## 
## $p.value
##    two-sided
##     midp.exact  fisher.exact    chi.square
##   N         NA            NA            NA
##   Y          0 1.822183e-277 3.663022e-272
## 
## $correction
## [1] FALSE
## 
## attr(,"method")
## [1] "median-unbiased estimate & mid-p exact CI"

Nhóm có nhà (Homeowner = Y) có khả năng đã kết hôn thấp hơn rõ rệt so với nhóm không có nhà (Homeowner = N). Cụ thể, odds là người đã kết hôn ở nhóm có nhà chỉ bằng 28.27% odds ở nhóm không có nhà. Sự khác biệt này có ý nghĩa thống kê mạnh với p-value gần bằng 0.

##Khi có thêm tham số rev = 'c' thì sẽ thực hiện việc
## đổi chỗ 2 cột trong bảngngẫu nhiên.
oddsratio(v, rev = 'c') #package epitools
## $data
##        
##            S    M Total
##   N     3896 1719  5615
##   Y     3297 5147  8444
##   Total 7193 6866 14059
## 
## $measure
##    odds ratio with 95% C.I.
##     estimate   lower    upper
##   N 1.000000      NA       NA
##   Y 3.537687 3.29363 3.800843
## 
## $p.value
##    two-sided
##     midp.exact  fisher.exact    chi.square
##   N         NA            NA            NA
##   Y          0 1.822183e-277 3.663022e-272
## 
## $correction
## [1] FALSE
## 
## attr(,"method")
## [1] "median-unbiased estimate & mid-p exact CI"

Kết quả: OR = 3.5377 > 1 ⇒ Nhóm có nhà (Y) có odds là người độc thân (S) cao gấp ~3.54 lần so với nhóm không có nhà (N). 95% CI: (3.2936 – 3.8008) → không chứa 1 ⇒ có ý nghĩa thống kê. p-value ≈ 0 → khác biệt này là rất rõ ràng, không phải ngẫu nhiên. Khi đảo thứ tự cột để lấy “Single” làm mốc so sánh, kết quả cho thấy: Những người có nhà (Homeowner = Y) có khả năng là người độc thân cao hơn rất nhiều so với người không có nhà. Tỷ số odds là ~3.54 lần, và sự khác biệt này có ý nghĩa thống kê rất mạnh.

epitab(v, method = 'oddsratio') #package epitools
## $tab
##    
##        M        p0    S        p1 oddsratio     lower     upper       p.value
##   N 1719 0.2503641 3896 0.5416377 1.0000000        NA        NA            NA
##   Y 5147 0.7496359 3297 0.4583623 0.2826322 0.2630929 0.3036227 1.822183e-277
## 
## $measure
## [1] "wald"
## 
## $conf.level
## [1] 0.95
## 
## $pvalue
## [1] "fisher.exact"

Kết quả: Dòng N (Homeowner = No) 1719 người kết hôn (M), chiếm 25.04%

3896 người độc thân (S), chiếm 54.16%

Đây là nhóm tham chiếu (OR = 1)

Dòng Y (Homeowner = Yes) 5147 người kết hôn (M), chiếm 74.96%

3297 người độc thân (S), chiếm 45.84%

Odds Ratio = 0.2826, CI = [0.2631; 0.3036] → Người có nhà có odds kết hôn chỉ bằng ~28.26% so với người không có nhà.

p-value ≈ 0 → Sự khác biệt có ý nghĩa thống kê rất mạnh.

Kết quả từ epitab() cho thấy rằng những người có nhà (Homeowner = Y) có khả năng kết hôn thấp hơn rõ rệt so với người không có nhà (Homeowner = N). Cụ thể, odds kết hôn ở nhóm có nhà chỉ bằng ~28.26% so với nhóm không có nhà, và sự khác biệt này rất có ý nghĩa thống kê (p < 2.2e-16).