OLS là một trong những mô hình hồi quy cơ bản nhất, được sử dụng rộng rãi khi biến phụ thuộc là định lượng và có phân phối chuẩn.
Chỉ phù hợp cho biến phụ thuộc định lượng (liên tục).
Giả định: Phần dư của mô hình tuân theo phân phối chuẩn.
Mối quan hệ: Mô hình OLS giả định một mối quan hệ tuyến tính trực tiếp giữa biến phụ thuộc và các biến độc lập, được biểu diễn bằng công thức:
\[Y = \beta_0 + \beta_1 X_1 + \ldots + \beta_k X_k + \epsilon\] Trong đó:
GLM là tổng quát và mạnh mẽ hơn OLS, cho phép làm việc với nhiều loại biến phụ thuộc khác nhau, không chỉ giới hạn ở biến định lượng với phân phối chuẩn.
Khả năng xử lý: GLM có thể xử lý biến phụ thuộc định tính (phân loại) hoặc biến định lượng nhưng không theo phân phối chuẩn (ví dụ: dữ liệu đếm, dữ liệu tỷ lệ).
Cấu trúc của GLM: Một GLM bao gồm ba thành phần chính:
Đây là phần tuyến tính của mô hình, tương tự như vế phải của phương trình OLS, biểu diễn mối quan hệ tuyến tính giữa các biến độc lập và một giá trị \(\eta\) (eta):
\[\eta = \beta_0 + \beta_1 X_1 + \ldots + \beta_k X_k\] Trong đó \(\beta_0, \beta_1, \ldots, \beta_k\) là các hệ số tuyến tính cần ước lượng.
Là một hàm \(g(\cdot)\) biến đổi giá trị trung bình kỳ vọng của biến phụ thuộc, \(E(Y) = \mu\), sao cho nó có mối quan hệ tuyến tính với thành phần hệ thống \(\eta\):
\[g(\mu) = \eta\]
Hàm liên kết cho phép chúng ta kết nối miền giá trị của biến phụ thuộc (ví dụ: xác suất từ 0 đến 1) với miền giá trị không giới hạn của phần tuyến tính.
Hồi quy Logistic là một trường hợp đặc biệt và rất phổ biến của GLM.
Biến phụ thuộc: Được sử dụng khi biến phụ thuộc là định tính nhị phân (chỉ có hai cấp độ, ví dụ: 0 hoặc 1, “Có” hoặc “Không”, “Sống sót” hoặc “Tử vong”).
Phân phối ngẫu nhiên: Biến phụ thuộc tuân theo phân phối Bernoulli.
Hàm liên kết: Hồi quy Logistic sử dụng Hàm Logit làm hàm liên kết:
\[g(p) = \ln\left(\frac{p}{1-p}\right)\] Trong đó \(p = P(Y=1|X)\) là xác suất của sự kiện “thành công” (biến phụ thuộc bằng 1) với điều kiện các biến độc lập \(X\).
Mô hình toán học: Khi kết hợp các thành phần của GLM, mô hình hồi quy Logistic được biểu diễn như sau:
\[\ln\left(\frac{P(Y=1|X)}{1 - P(Y=1|X)}\right) = \beta_0 + \beta_1 X_1 + \ldots + \beta_k X_k\]
Hồi quy Logistic là công cụ mạnh mẽ để dự đoán xác suất xảy ra một sự kiện nhị phân, thay vì dự đoán trực tiếp kết quả 0 hoặc 1, giúp chúng ta hiểu rõ hơn về ảnh hưởng của các yếu tố đầu vào.
Trong hồi quy Logistic, chúng ta không trực tiếp dự đoán giá trị 0 hoặc 1, mà dự đoán xác suất của sự kiện (thường là sự kiện “thành công”, được mã hóa là 1). Xác suất này, \(P\), luôn nằm trong khoảng \([0,1]\).
Tuy nhiên, một mô hình tuyến tính (\(\beta_0 + \beta_1X\)) có thể cho ra giá trị bất kỳ từ \((-\infty, +\infty)\), không phù hợp để dự đoán trực tiếp xác suất. Do đó, chúng ta cần một hàm để “ép” giá trị tuyến tính vào khoảng \([0,1]\). Hàm đó chính là Hàm Logistic (hoặc Sigmoid).
Hồi quy Logistic sử dụng Hàm Logit làm hàm liên kết (link function). Hàm Logit biến đổi xác suất \(p\) thành logarit của “odds” (tỷ lệ cơ hội):
\[ g(p) = \ln\left(\frac{p}{1-p}\right) \]
\(p\): Xác suất của sự kiện (biến phụ thuộc = 1).
\((1-p)\): Xác suất của việc sự kiện không xảy ra (biến phụ thuộc = 0).
\(\frac{p}{1-p}\): Tỷ lệ cơ hội (Odds) - cho biết khả năng xảy ra sự kiện so với khả năng không xảy ra sự kiện.
\(\ln(\text{Odds})\): Logarit của tỷ lệ cơ hội (Log-Odds hay Logit).
Miền giá trị của \(p\) là \([0,1]\), trong khi miền giá trị của \(g(p)\) là \((-\infty, +\infty)\). Điều này cho phép chúng ta thiết lập mối quan hệ tuyến tính giữa log-odds và các biến độc lập:
\[ \ln\left(\frac{P(Y=1|X)}{1-P(Y=1|X)}\right) = \beta_0 + \beta_1X_1 + \dots + \beta_kX_k \]
Để chuyển đổi từ giá trị trên thang logit (kết quả của phần tuyến tính \(\beta_0+\beta_1X+\dots\)) trở lại xác suất \(P\), chúng ta sử dụng hàm ngược của Logit, còn gọi là Hàm Logistic hay Hàm Sigmoid:
\[ P(Y=1|X) = \frac{1}{1 + e^{-(\beta_0 + \beta_1X_1 + \dots + \beta_kX_k)}} = \frac{e^{\beta_0 + \beta_1X_1 + \dots + \beta_kX_k}}{1 + e^{\beta_0 + \beta_1X_1 + \dots + \beta_kX_k}} \]
Trong R, hàm plogis(x)
chính là hàm sigmoid.
# Minh họa hàm sigmoid
# Tạo một dãy giá trị tuyến tính (logit)
linear_predictor_values <- seq(-5, 5, length.out = 100)
# Chuyển đổi sang xác suất bằng hàm plogis (sigmoid)
probabilities <- plogis(linear_predictor_values)
# Vẽ đồ thị
plot(linear_predictor_values, probabilities, type = "l", col = "blue", lwd = 2,
xlab = "Giá trị trên thang Logit (Linear Predictor)",
ylab = "Xác suất P(Y=1)",
main = "Hàm Sigmoid (Hàm Ngược của Logit)")
grid()
Đồ thị minh họa hàm Sigmoid, chuyển đổi giá trị tuyến tính thành xác suất.
Các tham số \(\beta_0, \beta_1, \dots, \beta_k\) trong hồi quy Logistic được ước lượng bằng phương pháp Maximum Likelihood Estimation (MLE). Mục tiêu là tìm các giá trị \(\beta\) mà tối đa hóa hàm log-khả năng (log-likelihood) của mô hình.
Đối với hồi quy Logistic, biến phụ thuộc \(Y\) tuân theo phân phối Bernoulli. Hàm khối lượng xác suất của Bernoulli là \(f(y|p) = p^y(1-p)^{1-y}\). Hàm log-khả năng tổng quát sẽ là:
\[ \ln L(\beta|X,y) = \sum_{i=1}^{n} \left[y_i \ln(P_i) + (1-y_i)\ln(1-P_i)\right] \]
Trong đó \(P_i = \frac{1}{1 + e^{-(\beta_0 + \beta_1X_{i1} + \dots + \beta_kX_{ik})}}\).
Việc tối đa hóa hàm này thường được thực hiện bằng các thuật toán tối ưu hóa số học (ví dụ: Newton-Raphson), được tích hợp sẵn trong các hàm của R.
Sử dụng bộ dữ liệu iris
. Vì hồi quy Logistic nhị phân
yêu cầu biến phụ thuộc nhị phân, tạo một biến mới is_setosa
(1 nếu là setosa
, 0 nếu không phải).
# Tải bộ dữ liệu Iris
data(iris)
# Tạo biến phụ thuộc nhị phân: 1 nếu Species là "setosa", 0 nếu không phải
iris$is_setosa <- ifelse(iris$Species == "setosa", 1, 0)
# Kiểm tra phân bố của biến mới
table(iris$is_setosa)
##
## 0 1
## 100 50
Dự đoán is_setosa
dựa trên một biến độc lập, ví dụ
Sepal.Length
.
# Chạy mô hình hồi quy logistic đơn biến
# family = binomial chỉ định phân phối Bernoulli cho biến phụ thuộc
# link = "logit" là hàm liên kết mặc định cho binomial, nhưng rõ ràng hơn khi chỉ rõ
model_logistic_univariate <- glm(is_setosa ~ Sepal.Length,
data = iris,
family = binomial(link = "logit"))
# Xem tóm tắt kết quả mô hình
summary(model_logistic_univariate)
##
## Call:
## glm(formula = is_setosa ~ Sepal.Length, family = binomial(link = "logit"),
## data = iris)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 27.8285 4.8276 5.765 8.19e-09 ***
## Sepal.Length -5.1757 0.8934 -5.793 6.90e-09 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 190.954 on 149 degrees of freedom
## Residual deviance: 71.836 on 148 degrees of freedom
## AIC: 75.836
##
## Number of Fisher Scoring iterations: 7
Giải thích kết quả summary()
:
Call: Hàm được gọi để tạo mô hình.
Deviance Residuals: Đánh giá độ phù hợp của mô hình. Các giá trị nhỏ cho thấy mô hình khớp tốt.
Coefficients:
Estimate: Ước lượng các hệ số \(\beta\). Intercept
là \(\hat{\beta}_0\) và
Sepal.Length
là \(\hat{\beta}_1\).
Std. Error: Sai số chuẩn của các ước lượng.
z value: Giá trị thống kê kiểm định Wald (tỷ lệ Estimate / Std. Error).
Pr(>|z|): P-value tương ứng, cho biết ý nghĩa thống kê của từng hệ số. Một P-value nhỏ (thường < 0.05) cho thấy hệ số đó khác 0 một cách có ý nghĩa thống kê.
Null Deviance: Độ lệch của mô hình chỉ có hệ số chặn (intercept), không có biến độc lập.
Residual Deviance: Độ lệch của mô hình hiện tại (có các biến độc lập). Sự giảm đáng kể từ Null Deviance sang Residual Deviance cho thấy mô hình có các biến độc lập là tốt hơn đáng kể.
AIC (Akaike Information Criterion): Một tiêu chí để so sánh các mô hình; giá trị AIC thấp hơn thường được ưa thích hơn.
Diễn giải hệ số: Giá trị \(\hat{\beta}_1\) (ví dụ:
Sepal.Length
) trong hồi quy logistic đại diện cho sự thay
đổi của log-odds khi biến độc lập tăng thêm 1 đơn vị. Có thể chuyển đổi
thành Odds Ratio (OR) bằng cách lấy \(\exp(\hat{\beta}_1)\). Ví dụ, nếu \(\hat{\beta}_1 = -5.15\), thì \(OR = e^{-5.15} \approx 0.0058\). Điều này
có nghĩa là, với mỗi cm tăng của Sepal.Length
, tỷ lệ cơ hội
(odds) để hoa là setosa
giảm xuống còn 0.58% (hay giảm
99.42%), giữ các yếu tố khác không đổi.
Dự đoán is_setosa
dựa trên nhiều biến độc lập.
# Chạy mô hình hồi quy logistic đa biến
model_logistic_multivariate <- glm(is_setosa ~ Sepal.Length + Sepal.Width + Petal.Length + Petal.Width,
data = iris,
family = binomial(link = "logit"))
summary(model_logistic_multivariate)
##
## Call:
## glm(formula = is_setosa ~ Sepal.Length + Sepal.Width + Petal.Length +
## Petal.Width, family = binomial(link = "logit"), data = iris)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -16.946 457457.097 0 1
## Sepal.Length 11.759 130504.042 0 1
## Sepal.Width 7.842 59415.385 0 1
## Petal.Length -20.088 107724.594 0 1
## Petal.Width -21.608 154350.616 0 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 1.9095e+02 on 149 degrees of freedom
## Residual deviance: 3.2940e-09 on 145 degrees of freedom
## AIC: 10
##
## Number of Fisher Scoring iterations: 25
Kết quả tương tự như mô hình đơn biến, nhưng có các hệ số cho từng biến độc lập, mỗi hệ số thể hiện sự thay đổi của log-odds khi biến đó tăng 1 đơn vị, giữ các biến độc lập khác không đổi.
Sử dụng mô hình để dự đoán xác suất và phân loại các quan sát.
# Dự đoán xác suất sử dụng mô hình đơn biến
# type = "response" trực tiếp trả về xác suất P(Y=1|X)
predicted_probs <- predict(model_logistic_univariate, newdata = iris, type = "response")
# Xem vài giá trị dự đoán đầu tiên
head(predicted_probs)
## 1 2 3 4 5 6
## 0.8072845 0.9218391 0.9707656 0.9823690 0.8754500 0.4699741
# Chuyển xác suất thành dự đoán lớp (ví dụ: ngưỡng 0.5)
predicted_class <- ifelse(predicted_probs > 0.5, 1, 0)
# So sánh dự đoán với giá trị thực tế (ma trận nhầm lẫn - Confusion Matrix)
confusion_matrix <- table(Actual = iris$is_setosa, Predicted = predicted_class)
print("Ma trận nhầm lẫn:")
## [1] "Ma trận nhầm lẫn:"
## Predicted
## Actual 0 1
## 0 94 6
## 1 10 40
# Tính độ chính xác (Accuracy)
accuracy <- sum(diag(confusion_matrix)) / sum(confusion_matrix)
cat("\nAccuracy của mô hình đơn biến:", round(accuracy, 3), "\n")
##
## Accuracy của mô hình đơn biến: 0.893
Mô hình Probit cũng là một dạng hồi quy nhị phân thuộc GLM, tương tự như Logistic, nhưng sử dụng một hàm liên kết khác.
Hàm liên kết của Probit là hàm phân vị nghịch đảo của phân phối chuẩn tắc (\(\Phi^{-1}\)), còn gọi là hàm quantiles chuẩn:
\[ g(p) = \Phi^{-1}(p) \]
Trong đó \(\Phi\) là hàm phân phối tích lũy (CDF) của phân phối chuẩn tắc (mean = 0, standard deviation = 1).
Do đó, mối quan hệ tuyến tính sẽ là:
\[ \Phi^{-1}(P(Y=1|X)) = \beta_0 + \beta_1X_1 + \dots + \beta_kX_k \]
Và để chuyển đổi ngược lại thành xác suất:
\[ P(Y=1|X) = \Phi(\beta_0 + \beta_1X_1 + \dots + \beta_kX_k) \]
Trong R, hàm pnorm(x)
là \(\Phi(x)\) và qnorm(p)
là \(\Phi^{-1}(p)\).
Logistic (Logit link): Dựa trên phân phối Logistic.
Probit (Probit link): Dựa trên phân phối Chuẩn.
Cả hai hàm Sigmoid (Logistic) và hàm CDF của phân phối chuẩn đều có hình dạng chữ “S”, rất giống nhau. Probit có “đuôi” nhẹ hơn (thay đổi chậm hơn ở các xác suất cực đoan gần 0 hoặc 1), trong khi Logistic có đuôi “nặng” hơn (thay đổi nhanh hơn). Trong nhiều ứng dụng thực tế, kết quả từ Logistic và Probit thường rất tương đồng.
Chỉ cần thay đổi link = "probit"
trong hàm
glm()
.
# Chạy mô hình Probit đơn biến
model_probit_univariate <- glm(is_setosa ~ Sepal.Length,
data = iris,
family = binomial(link = "probit"))
summary(model_probit_univariate)
##
## Call:
## glm(formula = is_setosa ~ Sepal.Length, family = binomial(link = "probit"),
## data = iris)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 15.7925 2.4921 6.337 2.34e-10 ***
## Sepal.Length -2.9407 0.4618 -6.367 1.92e-10 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 190.954 on 149 degrees of freedom
## Residual deviance: 71.372 on 148 degrees of freedom
## AIC: 75.372
##
## Number of Fisher Scoring iterations: 8
Khi biến phụ thuộc có nhiều hơn hai cấp độ và các cấp độ này
không có thứ tự tự nhiên (nominal), chúng ta sử dụng hồi quy
Logistic đa thức. Ví dụ, biến Species
trong
iris
có ba cấp độ: “setosa”, “versicolor”, “virginica”.
Cách hoạt động: Mô hình đa thức thường hoạt động bằng cách xây dựng một loạt các mô hình logistic nhị phân, so sánh từng cấp độ với một cấp độ tham chiếu.
Trong R: Thường sử dụng hàm
multinom()
từ gói nnet
.
# Chạy mô hình hồi quy logistic đa thức
# Biến phụ thuộc là Species (có 3 cấp độ gốc)
# Cần gói 'nnet' để chạy hàm này
model_multinomial <- nnet::multinom(Species ~ Sepal.Length + Sepal.Width + Petal.Length + Petal.Width,
data = iris)
## # weights: 18 (10 variable)
## initial value 164.791843
## iter 10 value 16.177348
## iter 20 value 7.111438
## iter 30 value 6.182999
## iter 40 value 5.984028
## iter 50 value 5.961278
## iter 60 value 5.954900
## iter 70 value 5.951851
## iter 80 value 5.950343
## iter 90 value 5.949904
## iter 100 value 5.949867
## final value 5.949867
## stopped after 100 iterations
# Tóm tắt mô hình
# Các hệ số được hiển thị so với một nhóm tham chiếu (ở đây là "setosa")
summary(model_multinomial)
## Call:
## nnet::multinom(formula = Species ~ Sepal.Length + Sepal.Width +
## Petal.Length + Petal.Width, data = iris)
##
## Coefficients:
## (Intercept) Sepal.Length Sepal.Width Petal.Length Petal.Width
## versicolor 18.69037 -5.458424 -8.707401 14.24477 -3.097684
## virginica -23.83628 -7.923634 -15.370769 23.65978 15.135301
##
## Std. Errors:
## (Intercept) Sepal.Length Sepal.Width Petal.Length Petal.Width
## versicolor 34.97116 89.89215 157.0415 60.19170 45.48852
## virginica 35.76649 89.91153 157.1196 60.46753 45.93406
##
## Residual Deviance: 11.89973
## AIC: 31.89973
# Dự đoán xác suất cho từng cấp độ
predictions_multi_prob <- predict(model_multinomial, newdata = iris, type = "probs")
head(predictions_multi_prob)
## setosa versicolor virginica
## 1 1.0000000 1.526406e-09 2.716417e-36
## 2 0.9999996 3.536476e-07 2.883729e-32
## 3 1.0000000 4.443506e-08 6.103424e-34
## 4 0.9999968 3.163905e-06 7.117010e-31
## 5 1.0000000 1.102983e-09 1.289946e-36
## 6 1.0000000 3.521573e-10 1.344907e-35
# Dự đoán cấp độ có xác suất cao nhất
predicted_multi_class <- predict(model_multinomial, newdata = iris, type = "class")
head(predicted_multi_class)
## [1] setosa setosa setosa setosa setosa setosa
## Levels: setosa versicolor virginica
# Ma trận nhầm lẫn cho mô hình đa thức
confusion_matrix_multi <- table(Actual = iris$Species, Predicted = predicted_multi_class)
print("Ma trận nhầm lẫn (Đa thức):")
## [1] "Ma trận nhầm lẫn (Đa thức):"
## Predicted
## Actual setosa versicolor virginica
## setosa 50 0 0
## versicolor 0 49 1
## virginica 0 1 49
# Tính độ chính xác
accuracy_multi <- sum(diag(confusion_matrix_multi)) / sum(confusion_matrix_multi)
cat("\nAccuracy của mô hình đa thức:", round(accuracy_multi, 3), "\n")
##
## Accuracy của mô hình đa thức: 0.987
Maximum Likelihood Estimation (MLE) là một phương pháp thống kê để ước lượng tham số của một mô hình phân phối xác suất dựa trên dữ liệu quan sát. Mục tiêu của MLE là tìm ra các giá trị tham số mà làm cho khả năng (likelihood) quan sát được dữ liệu hiện có là lớn nhất.
Mục đích: Ước lượng các tham số chưa biết của mô hình (ví dụ: trung bình, độ lệch chuẩn của phân phối chuẩn; xác suất p của phân phối Bernoulli) và tìm ra mô hình “phù hợp nhất” theo tiêu chí tối đa hóa khả năng xảy ra của dữ liệu.
Hàm Khả năng (Likelihood Function), ký hiệu là \(L(\theta)\), đo lường “khả năng” mà một bộ tham số \(\theta\) cụ thể sẽ tạo ra tập dữ liệu quan sát được \(x_1, x_2, \ldots, x_n\).
Nếu các quan sát \(x_1, \ldots, x_n\) là độc lập và cùng phân phối (i.i.d) từ một phân phối có hàm mật độ xác suất (PDF) \(f(x | \theta)\) (hoặc hàm khối lượng xác suất PMF nếu dữ liệu rời rạc), thì hàm khả năng được định nghĩa là tích của các hàm mật độ/khối lượng xác suất của từng điểm dữ liệu:
\[L(\theta | x_1, \ldots, x_n) = \prod_{i=1}^n f(x_i | \theta)\]
Ví dụ: Với \(n\) lần tung đồng xu, có \(k\) lần mặt ngửa, \(f(x | p)\) là hàm khối lượng xác suất Bernoulli. Hàm khả năng sẽ là: \(L(p) = p^k (1-p)^{n-k}\).
Hàm Log-Khả năng (Log-Likelihood Function), thay vì tối đa hóa \(L(\theta)\) trực tiếp, chúng ta thường làm việc với logarit tự nhiên của hàm khả năng, gọi là hàm log-khả năng, ký hiệu \(\ln L(\theta)\) hoặc \(l(\theta)\).
Việc này có hai lý do chính:
Tránh underflow số học: Khi nhân nhiều xác suất nhỏ, kết quả có thể quá nhỏ mà máy tính không thể biểu diễn chính xác. Logarit biến tích thành tổng.
Đơn giản hóa việc tối ưu hóa: Việc tìm cực trị của một tổng thường dễ hơn nhiều so với tìm cực trị của một tích khi đạo hàm. Hàm logarit là hàm đồng biến, nên tối đa hóa \(\ln L(\theta)\) hoàn toàn tương đương với tối đa hóa \(L(\theta)\).
Công thức của hàm log-khả năng là:
\[\ln L(\theta | x_1, \ldots, x_n) = \sum_{i=1}^n \ln f(x_i | \theta)\]
Ví dụ (tiếp theo): Hàm log-khả năng cho ví dụ tung đồng xu sẽ là: \(\ln L(p) = k \ln(p) + (n-k) \ln(1-p)\).
Công thức Ước lượng MLE (Maximum Likelihood Estimator)
Mục tiêu của MLE là tìm giá trị của \(\theta\) (ký hiệu là \(\hat{\theta}_{MLE}\)) mà tối đa hóa hàm log-khả năng. Về mặt toán học, điều này được biểu diễn như sau:
\[\hat{\theta}_{MLE} = \arg\max_{\theta} \left[ \sum_{i=1}^n \ln f(x_i | \theta) \right]\]
Để tìm \(\hat{\theta}_{MLE}\), chúng ta thường thực hiện các bước:
Lấy đạo hàm riêng của \(\ln L(\theta)\) theo mỗi tham số trong \(\theta\).
Đặt các đạo hàm này bằng 0 (đây là các “phương trình điểm dừng” hay “score equations”).
Giải hệ phương trình thu được để tìm giá trị của các tham số \(\hat{\theta}_{MLE}\).
Ví dụ (tiếp theo): Để tìm \(\hat{p}_{MLE}\) cho ví dụ tung đồng xu, ta lấy đạo hàm của \(\ln L(p)\) theo \(p\) và đặt bằng 0:
\[\frac{d}{dp} \ln L(p) = \frac{d}{dp} [k \ln(p) + (n-k) \ln(1-p)]\] \[= \frac{k}{p} - \frac{n-k}{1-p} = 0\]
Giải phương trình này, ta được:
\[\frac{k}{p} = \frac{n-k}{1-p}\] \[k(1-p) = p(n-k)\] \[k - kp = pn - kp\] \[k = pn\] \[\hat{p}_{MLE} = \frac{k}{n}\]
Ví dụ với R: có 100 lần tung đồng xu và thu được 60 lần mặt ngửa. Ước lượng xác suất p để đồng xu ra mặt ngửa.
# Dữ liệu
num_heads <- 60 # Số lần mặt ngửa
num_flips <- 100 # Tổng số lần tung
# Hàm log-khả năng cho phân phối nhị thức
# p_est là tham số p muốn ước lượng
log_likelihood_binomial <- function(p_est, k, n) {
# Đảm bảo p_est nằm trong khoảng (0, 1) để tránh lỗi log(0)
if (p_est <= 0 || p_est >= 1) {
return(-Inf) # Trả về -Inf nếu p_est không hợp lệ
}
return(k * log(p_est) + (n - k) * log(1 - p_est))
}
# Tìm p_est tối đa hóa hàm log-khả năng bằng cách quét qua một dải giá trị
p_values <- seq(0.01, 0.99, by = 0.001)
likelihoods <- sapply(p_values, function(p) log_likelihood_binomial(p, num_heads, num_flips))
# Tìm p_est có likelihood lớn nhất
best_p_index <- which.max(likelihoods)
mle_p <- p_values[best_p_index]
cat("Ước lượng MLE cho xác suất mặt ngửa (p) là:", round(mle_p, 3), "\n")
## Ước lượng MLE cho xác suất mặt ngửa (p) là: 0.6
Công thức: \(g(\mu) = \mu\)
Hàm ngược: \(\mu = g^{-1}(\eta)\)
Phân phối thường dùng: Chuẩn (Normal)
Ứng dụng chính: Hồi quy Tuyến tính (OLS)
Ý nghĩa và đặc điểm: Giả định mối quan hệ tuyến tính trực tiếp giữa biến phụ thuộc và các biến độc lập.
Ví dụ cụ thể: Dự đoán chiều cao (cm) dựa trên cân nặng (kg).
Công thức: \(g(p)=\ln\left(\frac{p}{1-p}\right)\)
Hàm ngược: \(p = \frac{1}{1 + e^{-\eta}}\)
Phân phối thường dùng: Bernoulli/Nhị thức (Binomial)
Ứng dụng chính: Hồi quy Logistic
Ý nghĩa và đặc điểm: Chuyển xác suất p (0-1) thành logarit của tỷ lệ cơ hội (log-odds). Đối xứng, phổ biến nhất cho dữ liệu nhị phân.
Ví dụ cụ thể: Dự đoán xác suất một khách hàng mua sản phẩm (Có/Không).
Công thức: \(g(p)=\Phi^{-1}(p)\)
Hàm ngược: \(p=\Phi(\eta)\)
Phân phối thường dùng: Bernoulli/Nhị thức (Binomial)
Ứng dụng chính: Hồi quy Probit
Ý nghĩa và đặc điểm: Chuyển xác suất p (0-1) thành giá trị z-score của phân phối chuẩn tắc. Tương tự Logit nhưng dựa trên phân phối chuẩn, có đuôi nhẹ hơn.
Ví dụ cụ thể: Dự đoán khả năng một bệnh nhân hồi phục (Có/Không) dựa trên liều lượng thuốc, với giả định ngưỡng tiềm ẩn chuẩn.
Công thức: \(g(\mu) = \ln(\mu)\)
Hàm ngược: \(\mu = e^\eta\)
Phân phối thường dùng: Poisson, Gamma
Ứng dụng chính: Hồi quy Poisson, Hồi quy Gamma
Ý nghĩa và đặc điểm: Đảm bảo giá trị dự đoán mu luôn dương, phù hợp cho dữ liệu đếm hoặc giá trị liên tục dương.
Ví dụ cụ thể: Dự đoán số lượng cuộc gọi đến tổng đài mỗi giờ.
Công thức: \(g(p) = \ln(-\ln(1-p))\)
Hàm ngược: \(p = 1 - e^{-e^\eta}\)
Phân phối thường dùng: Bernoulli/Nhị thức (Binomial)
Ứng dụng chính: Mô hình rủi ro tỷ lệ, Phân tích sống sót
Ý nghĩa và đặc điểm: Hàm phi đối xứng, nhạy cảm hơn ở các xác suất thấp (gần 0). Thường dùng khi có quá trình tích lũy rủi ro hoặc các sự kiện cực đoan.
Ví dụ cụ thể: Dự đoán xác suất một cá thể mắc bệnh trong vòng 5 năm, khi sự kiện xảy ra do tích lũy yếu tố nguy cơ.
Công thức: \(g(\mu) = \frac{1}{\mu}\)
Hàm ngược: \(\mu = \frac{1}{\eta}\)
Phân phối thường dùng: Gamma, Inverse Gaussian
Ứng dụng chính: Hồi quy Gamma (ít phổ biến hơn Log link cho Gamma)
Ý nghĩa và đặc điểm: Thường dùng khi mối quan hệ nghịch đảo có ý nghĩa vật lý, hoặc khi có các outliers dương.
Ví dụ cụ thể: Mô hình hóa thời gian hoàn thành một nhiệm vụ, khi thời gian là nghịch đảo của hiệu suất.
Bộ dữ liệu train.csv
là một phần của “Titanic: Machine
Learning from Disaster” trên Kaggle. Nó chứa thông tin về một tập hợp
hành khách trên tàu Titanic, bao gồm các đặc điểm cá nhân và quan trọng
nhất là liệu họ có sống sót sau thảm họa hay không. Mục tiêu của phân
tích này là khám phá các mối quan hệ giữa các biến số và biến mục tiêu
Survived
.
titanic <- read.csv("/Users/hotranhongnga/Downloads/UFM/HK2-2025/Phân tích dữ liệu định tính/R/Data nhiệm vụ 4/train.csv", stringsAsFactors = TRUE, header = TRUE, na.strings = c("", "NA") )
str(titanic)
## 'data.frame': 891 obs. of 12 variables:
## $ PassengerId: int 1 2 3 4 5 6 7 8 9 10 ...
## $ Survived : int 0 1 1 1 0 0 0 0 1 1 ...
## $ Pclass : int 3 1 3 1 3 3 1 3 3 2 ...
## $ Name : Factor w/ 891 levels "Abbing, Mr. Anthony",..: 109 191 358 277 16 559 520 629 417 581 ...
## $ Sex : Factor w/ 2 levels "female","male": 2 1 1 1 2 2 2 2 1 1 ...
## $ Age : num 22 38 26 35 35 NA 54 2 27 14 ...
## $ SibSp : int 1 1 0 1 0 0 0 3 0 1 ...
## $ Parch : int 0 0 0 0 0 0 0 1 2 0 ...
## $ Ticket : Factor w/ 681 levels "110152","110413",..: 524 597 670 50 473 276 86 396 345 133 ...
## $ Fare : num 7.25 71.28 7.92 53.1 8.05 ...
## $ Cabin : Factor w/ 147 levels "A10","A14","A16",..: NA 82 NA 56 NA NA 130 NA NA NA ...
## $ Embarked : Factor w/ 3 levels "C","Q","S": 3 1 3 3 3 2 3 3 3 1 ...
Phân loại biến:
## [1] "PassengerId" "Survived" "Pclass" "Name" "Sex"
## [6] "Age" "SibSp" "Parch" "Ticket" "Fare"
## [11] "Cabin" "Embarked"
Biến định tính:
Survived
: Biến nhị phân (0 = Chết, 1 = Sống sót).
Đây là biến mục tiêu.
Pclass
: Hạng vé (1 = Hạng 1, 2 = Hạng 2, 3 = Hạng
3). Biến định tính thứ bậc.
Sex
: Giới tính (male = Nam, female = Nữ). Biến định
tính nhị phân.
Embarked
: Cảng lên tàu (C = Cherbourg, Q =
Queenstown, S = Southampton). Biến định tính danh nghĩa.
Name
: Tên hành khách. Mặc dù là chuỗi, nhưng mỗi tên
là duy nhất; thường được sử dụng để trích xuất danh xưng (Title) - một
biến định tính khác.
Cabin
: Số cabin. Có nhiều giá trị thiếu và thường
được xử lý thành biến định tính (có/không có cabin, hoặc tầng
cabin).
Ticket
: Số vé.
Biến định lượng (Numerical):
PassengerId
: ID hành khách (thực tế là định danh).Age
: Tuổi.SibSp
: Số anh chị em/vợ chồng đi cùng.Parch
: Số cha mẹ/con cái đi cùng.Fare
: Giá vé.# Kiểm tra số lượng giá trị NA cho TẤT CẢ các biến
na_all <- sapply(titanic, function(x) sum(is.na(x)))
# Chỉ hiển thị các biến có giá trị NA
na <- na_all[na_all > 0]
if (length(na) == 0) {
cat("Không có giá trị NA nào trong dữ liệu.\n")
} else {
cat("Số lượng giá trị NA trong các biến:\n")
print(na)
}
## Số lượng giá trị NA trong các biến:
## Age Cabin Embarked
## 177 687 2
Điều này cho thấy bảng dữ liệu titanic có giá trị NA. Cụ thể:
Dựa trên kết quả kiểm tra NA và bản chất của các biến, thực hiện các bước tiền xử lý sau:
Chuyển đổi và gán nhãn cho các biến định tính: Đảm bảo các biến như Survived, Pclass, Sex, Embarked là kiểu factor với nhãn rõ ràng để dễ dàng phân tích và trực quan hóa.
Xử lý giá trị thiếu (NA):
# Chuyển đổi và gán nhãn cho các biến định tính chính
titanic_factor <- titanic %>%
mutate(
Survived = factor(Survived, levels = c(0, 1), labels = c("Perished", "Survived")),
Pclass = factor(Pclass, levels = c(1, 2, 3), labels = c("1st Class", "2nd Class", "3rd Class")),
Sex = factor(Sex, levels = c("male", "female"), labels = c("Male", "Female"))
)
# Xử lý giá trị thiếu (NA)
# Xử lý Age: Điền bằng trung vị
median_age <- median(titanic_factor$Age, na.rm = TRUE)
titanic_factor$Age[is.na(titanic_factor$Age)] <- median_age
# Xử lý Embarked: Điền bằng mode
mode_embarked <- names(sort(table(titanic_factor$Embarked), decreasing = TRUE))[1]
titanic_factor$Embarked[is.na(titanic_factor$Embarked)] <- mode_embarked
titanic_factor$Embarked <- factor(titanic_factor$Embarked, levels = c("C", "Q", "S"), labels = c("Cherbourg", "Queenstown", "Southampton"))
# Xử lý Cabin: Tạo biến mới 'Has_Cabin'
titanic_factor <- titanic_factor %>% mutate( Has_Cabin = factor(ifelse(is.na(Cabin), "No", "Yes"), levels = c("No", "Yes")) )
sapply(titanic_factor, function(x) sum(is.na(x)))
## PassengerId Survived Pclass Name Sex Age
## 0 0 0 0 0 0
## SibSp Parch Ticket Fare Cabin Embarked
## 0 0 0 0 687 0
## Has_Cabin
## 0
## 'data.frame': 891 obs. of 13 variables:
## $ PassengerId: int 1 2 3 4 5 6 7 8 9 10 ...
## $ Survived : Factor w/ 2 levels "Perished","Survived": 1 2 2 2 1 1 1 1 2 2 ...
## $ Pclass : Factor w/ 3 levels "1st Class","2nd Class",..: 3 1 3 1 3 3 1 3 3 2 ...
## $ Name : Factor w/ 891 levels "Abbing, Mr. Anthony",..: 109 191 358 277 16 559 520 629 417 581 ...
## $ Sex : Factor w/ 2 levels "Male","Female": 1 2 2 2 1 1 1 1 2 2 ...
## $ Age : num 22 38 26 35 35 28 54 2 27 14 ...
## $ SibSp : int 1 1 0 1 0 0 0 3 0 1 ...
## $ Parch : int 0 0 0 0 0 0 0 1 2 0 ...
## $ Ticket : Factor w/ 681 levels "110152","110413",..: 524 597 670 50 473 276 86 396 345 133 ...
## $ Fare : num 7.25 71.28 7.92 53.1 8.05 ...
## $ Cabin : Factor w/ 147 levels "A10","A14","A16",..: NA 82 NA 56 NA NA 130 NA NA NA ...
## $ Embarked : Factor w/ 3 levels "Cherbourg","Queenstown",..: 3 1 3 3 3 2 3 3 3 1 ...
## $ Has_Cabin : Factor w/ 2 levels "No","Yes": 1 2 1 2 1 1 2 1 1 1 ...
Nhận xét về tiền xử lý dữ liệu:
Các biến định tính (Survived, Pclass, Sex, Embarked) đã được chuyển đổi sang kiểu factor và gán nhãn rõ ràng. Tất cả các giá trị thiếu trong Age, Embarked, và Cabin đã được xử lý.
Thống kê tần suất và tỷ lệ
# Bảng tần số
survived <- table(titanic_factor$Survived)
# Bảng tần suất
tylesurvived <- round(prop.table(survived) * 100, 2)
# Tạo bảng
survivedtable <- data.frame(
Survived = names(survived),
Frequency = as.vector(survived),
Percentage = paste0(tylesurvived, "%") )
# Hiển thị bảng
library(knitr)
kable(survivedtable, caption = "Bảng tần suất và tỷ lệ phần trăm khả năng sống sót")
Survived | Frequency | Percentage |
---|---|---|
Perished | 549 | 61.62% |
Survived | 342 | 38.38% |
Theo bảng trên, trong tổng số hành khách có thông tin về biến Survived:
Tỷ lệ tử vong cao hơn đáng kể so với tỷ lệ sống sót. Kết quả này phản ánh rõ rằng trong tai nạn tàu Titanic, xác suất để một hành khách tử vong là vượt trội hơn so với sống sót. Nếu coi mỗi hành khách là một phép thử Bernoulli (sống sót hay không), thì tỷ lệ sống sót 38.38% cho thấy khả năng sống sót là khá thấp trong tổng thể.
Trực quan hóa bằng biểu đồ
ggplot(survivedtable, aes(x = Survived, y = Frequency, fill = Survived)) +
geom_bar(stat = "identity", color = "black") +
geom_text(aes(label = Percentage), vjust = -0.5, size = 4) +
labs(title = "Tần suất sống sót trên tàu Titanic",
x = "Trạng thái sống sót",
y = "Số lượng hành khách") +
theme_minimal() +
scale_fill_brewer(palette = "Set1") +
theme(legend.position = "none")
Từ bảng tần suất và biểu đồ cột, chúng ta thấy rằng phần lớn hành khách trên tàu Titanic đã không sống sót. Cụ thể, khoảng 61.62% hành khách đã tử vong, trong khi chỉ có khoảng 38.38% sống sót. Điều này cho thấy thảm họa Titanic thực sự là một sự kiện có tỷ lệ tử vong rất cao.
Thống kê tần suất và tỷ lệ
titanic_factor$Sex <- factor(titanic_factor$Sex, levels = c("Male", "Female"))
# Bảng tần số
sex <- table(titanic_factor$Sex)
# Bảng tần suất
tylesex <- round(prop.table(sex) * 100, 2)
# Tạo bảng
sextable <- data.frame(
Sex = names(sex),
Frequency = as.vector(sex),
Percentage = paste0(tylesex, "%") )
# Hiển thị bảng
library(knitr)
kable(sextable, caption = "Bảng tần suất và tỷ lệ phần trăm khả năng sống sót")
Sex | Frequency | Percentage |
---|---|---|
Male | 577 | 64.76% |
Female | 314 | 35.24% |
Trực quan hóa bằng biểu đồ
library(ggplot2)
ggplot(sextable, aes(x = Sex, y = Frequency, fill = Sex)) +
geom_bar(stat = "identity", color = "black") + # stat = "identity" để sử dụng giá trị y (Frequency) trực tiếp
geom_text(aes(label = Percentage), vjust = -0.5, size = 4) + # Hiển thị Percentage trên đầu cột
labs(title = "Phân phối giới tính hành khách trên tàu Titanic",
x = "Giới tính",
y = "Số lượng hành khách") +
theme_minimal() +
scale_fill_brewer(palette = "Set2") + # Gán màu khác nhau cho mỗi giới tính từ bảng màu "Set1"
theme(legend.position = "none") # Ẩn chú giải vì màu đã được gán trực tiếp trên cột
Từ bảng tần suất và biểu đồ, ta thấy rằng tỷ lệ hành khách nam cao hơn đáng kể so với nữ. Hành khách nam chiếm khoảng , trong khi hành khách nữ chỉ chiếm khoảng . Sự chênh lệch này có thể ảnh hưởng đến phân tích tỷ lệ sống sót, đặc biệt nếu có sự ưu tiên cứu hộ theo giới tính (phụ nữ và trẻ em trước) trong hoàn cảnh tai nạn.
# Số lượng người sống sót
n_survived <- sum(titanic_factor$Survived == "Survived")
# Tổng số hành khách
survived_total <- nrow(titanic_factor)
# Tính tỷ lệ mẫu
p <- n_survived / survived_total
# Tính khoảng tin cậy
ci_survived <- prop.test(n_survived, survived_total, conf.level = 0.95)
ci_survived
##
## 1-sample proportions test with continuity correction
##
## data: n_survived out of survived_total, null probability 0.5
## X-squared = 47.627, df = 1, p-value = 5.154e-12
## alternative hypothesis: true p is not equal to 0.5
## 95 percent confidence interval:
## 0.3519194 0.4167722
## sample estimates:
## p
## 0.3838384
## Tỷ lệ sống sót ước tính: 38.38 %
## Khoảng tin cậy 95% cho tỷ lệ sống sót:
## [1] 35.19194 41.67722
## attr(,"conf.level")
## [1] 0.95
Nhận xét: Khoảng tin cậy 95% cho tỷ lệ sống sót là từ 35.19% đến 41.68%. Điều này có nghĩa là với khoảng tin cậy 95% thì tỷ lệ sống sót thực sự của hành khách trên tàu Titanic nằm trong khoảng này.
Kiểm định giả thuyết rằng tỷ lệ sống sót có khác 50% hay không.
\(H_0\): Tỷ lệ sống sót là 50%. (\(H_0: p = 0.5\))
\(H_1\): Tỷ lệ sống sót khác 50%. (\(H_1: p \neq 0.5\)) Với Mức ý nghĩa \(\alpha = 0.05\).
ht_survived <- prop.test(n_survived, survived_total, p = 0.5, alternative = "two.sided", conf.level = 0.95)
print(ht_survived)
##
## 1-sample proportions test with continuity correction
##
## data: n_survived out of survived_total, null probability 0.5
## X-squared = 47.627, df = 1, p-value = 5.154e-12
## alternative hypothesis: true p is not equal to 0.5
## 95 percent confidence interval:
## 0.3519194 0.4167722
## sample estimates:
## p
## 0.3838384
Nhận xét: Với giá trị p-value 0 nhỏ hơn 0.05, chúng ta bác bỏ giả thuyết \(H_0\). Điều này có nghĩa là có bằng chứng thống kê mạnh mẽ cho thấy tỷ lệ sống sót trên tàu Titanic khác biệt đáng kể so với 50%. Thực tế, tỷ lệ sống sót thấp hơn nhiều.
Bảng tần số chéo
# Bảng tần số chéo
table_sex_survived <- table(titanic_factor$Sex, titanic_factor$Survived)
print(table_sex_survived)
##
## Perished Survived
## Male 468 109
## Female 81 233
# Tỷ lệ theo hàng (tỷ lệ theo giới tính)
prop_table_sex_survived_row<- prop.table(table_sex_survived, margin = 1) * 100
print(round(prop_table_sex_survived_row, 2))
##
## Perished Survived
## Male 81.11 18.89
## Female 25.80 74.20
Biểu đồ
ggplot(titanic_factor, aes(x = Sex, fill = Survived)) +
geom_bar(position = "fill", color = "black") + # position = "fill" để xem tỷ lệ
labs(title = "Tỷ lệ Sống sót theo Giới tính",
x = "Giới tính",
y = "Tỷ lệ",
fill = "Sống sót") +
scale_y_continuous(labels = scales::percent) +
scale_fill_manual(values = c("Perished" = "pink", "Survived" = "lightgreen")) + # Màu sắc tùy chỉnh
theme_minimal() +
theme(legend.position = "right")
Nhận xét về mối quan hệ giữa Giới tính và Khả năng Sống sót
Phụ nữ có khả năng sống sót cao hơn đáng kể so với nam giới: Trong nhóm nữ giới, có 74.20% người sống sót (Survived), trong khi chỉ có 25.80% người không sống sót (Perished). Ngược lại, trong nhóm nam giới, tỷ lệ không sống sót là áp đảo với 81.11%, và chỉ có 18.89% nam giới sống sót. Sự chênh lệch này cho thấy chính sách “phụ nữ và trẻ em trước” (women and children first) dường như đã được ưu tiên thực hiện, hoặc phụ nữ có cơ hội tiếp cận thuyền cứu sinh tốt hơn.
Sự phân bổ số lượng tuyệt đối giữa các nhóm: Mặc dù có tỷ lệ sống sót cao, vẫn có 81 phụ nữ không sống sót. Số lượng nam giới không sống sót là rất lớn, lên tới 468 người, cho thấy phần lớn nam giới đã tử nạn. Trong khi đó, số lượng nữ giới sống sót là 233 người, cao hơn đáng kể so với 109 nam giới sống sót.”
Kết luận: Dữ liệu cho thấy giới tính là một yếu tố ảnh hưởng mạnh mẽ đến khả năng sống sót trên tàu Titanic, với phụ nữ có tỷ lệ sống sót vượt trội so với nam giới.
Kiểm định xem mối quan hệ này có ý nghĩa thống kê hay không, kiểm định Chi-bình phương cho bảng tần suất chéo.
\(H_0\): Survived và Sex độc lập (không có mối quan hệ).
\(H_1\): Survived và Sex phụ thuộc (có mối quan hệ).
##
## Pearson's Chi-squared test with Yates' continuity correction
##
## data: table_sex_survived
## X-squared = 260.72, df = 1, p-value < 2.2e-16
Nhận xét: Với giá trị p-value rất nhỏ <2e-16, chúng ta bác bỏ giả thuyết \(H_0\). Điều này có nghĩa là có bằng chứng thống kê mạnh mẽ cho thấy có mối quan hệ phụ thuộc có ý nghĩa giữa giới tính và khả năng sống sót.
##
## Perished Survived Sum
## Male 468 109 577
## Female 81 233 314
## Sum 549 342 891
##
## Relative risk
## $data
##
## Perished Survived Total
## Male 468 109 577
## Female 81 233 314
## Total 549 342 891
##
## $measure
## risk ratio with 95% C.I.
## estimate lower upper
## Male 1.000000 NA NA
## Female 3.928037 3.276995 4.708422
##
## $p.value
## two-sided
## midp.exact fisher.exact chi.square
## Male NA NA NA
## Female 0 6.463922e-60 3.711748e-59
##
## $correction
## [1] FALSE
##
## attr(,"method")
## [1] "Unconditional MLE & normal approximation (Wald) CI"
##
## Odds Ratio
## $data
##
## Perished Survived Total
## Male 468 109 577
## Female 81 233 314
## Total 549 342 891
##
## $measure
## odds ratio with 95% C.I.
## estimate lower upper
## Male 1.00000 NA NA
## Female 12.28921 8.894567 17.14789
##
## $p.value
## two-sided
## midp.exact fisher.exact chi.square
## Male NA NA NA
## Female 0 6.463922e-60 3.711748e-59
##
## $correction
## [1] FALSE
##
## attr(,"method")
## [1] "median-unbiased estimate & mid-p exact CI"
Nhận xét về RR và OR
Relative Risk (RR):
Tỷ lệ sống sót của phụ nữ cao gấp khoảng 3.93 lần so với nam giới.
Khoảng tin cậy 95% cho RR là từ 3.28 đến 4.71.
Vì RR > 1 và khoảng tin cậy không chứa 1, điều này cho thấy sự khác
biệt trong giới tính có ý nghĩa thống kê rõ ràng rằng nữ giới có tỷ lệ
sống sót cao hơn.
Odds Ratio (OR):
Tỷ lệ chênh sống sót của phụ nữ cao gấp khoảng 12.29 lần so với nam
giới.
Khoảng tin cậy 95% cho OR là từ 8.89 đến 17.15.
OR cũng lớn hơn 1 đáng kể và khoảng tin cậy không chứa 1, khẳng định mối
liên hệ mạnh mẽ giữa giới tính và khả năng sống sót.
Cả RR và OR đều cho thấy phụ nữ có khả năng sống sót cao hơn nam giới trong thảm họa Titanic. Đây là kết quả thống kê rõ ràng và phù hợp với bối cảnh xã hội và nguyên tắc cứu hộ tại thời điểm đó (“phụ nữ và trẻ em trước”).
Bảng tần suất chéo
# Bảng tần số chéo
table_pclass_survived <- table(titanic_factor$Pclass, titanic_factor$Survived)
print(table_pclass_survived)
##
## Perished Survived
## 1st Class 80 136
## 2nd Class 97 87
## 3rd Class 372 119
# Tỷ lệ theo hàng (tỷ lệ sống sót trong mỗi hạng vé)
prop_table_pclass_survived_row<- prop.table(table_pclass_survived, margin = 1) * 100
print(round(prop_table_pclass_survived_row, 2))
##
## Perished Survived
## 1st Class 37.04 62.96
## 2nd Class 52.72 47.28
## 3rd Class 75.76 24.24
Biểu đồ
ggplot(titanic_factor, aes(x = Pclass, fill = Survived)) +
geom_bar(position = "fill", color = "black") + # position = "fill" để xem tỷ lệ
labs(title = "Tỷ lệ Sống sót theo Hạng vé",
x = "Hạng vé",
y = "Tỷ lệ",
fill = "Sống sót") +
scale_y_continuous(labels = scales::percent) +
scale_fill_manual(values = c("Perished" = "orange", "Survived" = "darkgreen")) + # Màu sắc tùy chỉnh
theme_minimal() +
theme(legend.position = "right")
Nhận xét về mối quan hệ giữa Hạng vé và Khả năng Sống sót:
Có một xu hướng rõ ràng: hành khách ở hạng vé cao hơn (1st Class) có tỷ lệ sống sót cao hơn đáng kể so với hành khách ở hạng vé thấp hơn (3rd Class). Tỷ lệ sống sót của 1st Class là 62.96%, 2nd Class là 47.28%, và 3rd Class chỉ là 24.24%. Điều này cho thấy tầng lớp xã hội có thể đã đóng vai trò quan trọng trong việc phân bổ cơ hội sống sót.
##
## Pearson's Chi-squared test
##
## data: table_pclass_survived
## X-squared = 102.89, df = 2, p-value < 2.2e-16
Nhận xét: Với p-value rất nhỏ <2e-16, chúng ta bác bỏ giả thuyết \(H_0\). Có bằng chứng thống kê mạnh mẽ cho thấy Pclass và Survived là phụ thuộc, tức là hạng vé có mối quan hệ có ý nghĩa với khả năng sống sót.
So sánh các hạng vé thấp hơn với hạng vé 1 (1st Class) như là nhóm tham chiếu.
##
## Perished Survived Sum
## 1st Class 80 136 216
## 2nd Class 97 87 184
## 3rd Class 372 119 491
## Sum 549 342 891
## $data
##
## Perished Survived Total
## 1st Class 80 136 216
## 2nd Class 97 87 184
## 3rd Class 372 119 491
## Total 549 342 891
##
## $measure
## risk ratio with 95% C.I.
## estimate lower upper
## 1st Class 1.0000000 NA NA
## 2nd Class 0.7509591 0.6249491 0.9023768
## 3rd Class 0.3849287 0.3193195 0.4640184
##
## $p.value
## two-sided
## midp.exact fisher.exact chi.square
## 1st Class NA NA NA
## 2nd Class 0.00172126 1.777083e-03 1.650128e-03
## 3rd Class 0.00000000 1.786956e-22 5.209808e-23
##
## $correction
## [1] FALSE
##
## attr(,"method")
## [1] "Unconditional MLE & normal approximation (Wald) CI"
## $data
##
## Perished Survived Total
## 1st Class 80 136 216
## 2nd Class 97 87 184
## 3rd Class 372 119 491
## Total 549 342 891
##
## $measure
## odds ratio with 95% C.I.
## estimate lower upper
## 1st Class 1.0000000 NA NA
## 2nd Class 0.5287186 0.3533065 0.7880804
## 3rd Class 0.1888611 0.1332280 0.2658823
##
## $p.value
## two-sided
## midp.exact fisher.exact chi.square
## 1st Class NA NA NA
## 2nd Class 0.00172126 1.777083e-03 1.650128e-03
## 3rd Class 0.00000000 1.786956e-22 5.209808e-23
##
## $correction
## [1] FALSE
##
## attr(,"method")
## [1] "median-unbiased estimate & mid-p exact CI"
Tỷ lệ sống sót của hành khách hạng hai (2nd Class) là khoảng 0.75 lần so với hạng nhất. Khoảng tin cậy 95% RR là từ 0.62 đến 0.9. Vì RR < 1 và khoảng tin cậy không chứa 1, điều này cho thấy hành khách hạng hai có tỷ lệ sống sót thấp hơn đáng kể so với hạng nhất.
Tỷ lệ sống sót của hành khách hạng ba (3rd Class) là khoảng 0.38 lần so với hạng nhất. Khoảng tin cậy 95% RR là từ 0.32 đến 0.46. Đây là một mức giảm rất lớn và có ý nghĩa thống kê, khẳng định hành khách hạng ba có tỷ lệ sống sót thấp hơn rất nhiều so với hạng nhất.
Tỷ lệ chênh sống sót của hành khách hạng hai (2nd Class) là khoảng 0.53 lần so với hạng nhất. Khoảng tin cậy 95% OR là từ 0.35 đến 0.79. Điều này cho thấy odds sống sót của hạng hai thấp hơn hạng nhất.
Tỷ lệ chênh sống sót của hành khách hạng ba (3rd Class) là khoảng 0.19 lần so với hạng nhất. Khoảng tin cậy 95% OR là từ 0.13 đến 0.27. Đây là một sự khác biệt rất lớn, khẳng định odds sống sót của hạng ba thấp hơn rất nhiều so với hạng nhất.
Bảng tần số chéo
# Bảng tần số chéo
table_embarked_survived <- table(titanic_factor$Embarked, titanic_factor$Survived)
print(table_embarked_survived)
##
## Perished Survived
## Cherbourg 75 93
## Queenstown 47 30
## Southampton 427 219
# Tỷ lệ theo hàng (tỷ lệ sống sót ở mỗi cảng lên tàu)
prop_table_embarked_survived_row<- prop.table(table_embarked_survived, margin = 1) * 100
print(round(prop_table_embarked_survived_row, 2))
##
## Perished Survived
## Cherbourg 44.64 55.36
## Queenstown 61.04 38.96
## Southampton 66.10 33.90
Trực quan hóa
ggplot(titanic_factor, aes(x = Embarked, fill = Survived)) +
geom_bar(position = "fill", color = "black") +
labs(title = "Tỷ lệ Sống sót theo Cảng lên tàu",
x = "Cảng lên tàu",
y = "Tỷ lệ",
fill = "Sống sót") +
scale_y_continuous(labels = scales::percent) +
scale_fill_manual(values = c("Perished" = "violet", "Survived" = "lightblue")) +
theme_minimal() +
theme(legend.position = "right")
Nhận xét:
Có sự khác biệt về tỷ lệ sống sót giữa các cảng lên tàu. Hành khách lên tàu từ Cherbourg (C) có tỷ lệ sống sót cao nhất 55.36%, trong khi những người từ Southampton (S) có tỷ lệ thấp nhất 33.9%. Mối quan hệ này có thể gián tiếp, ví dụ, hành khách từ Cherbourg có thể có xu hướng mua vé hạng cao hơn.
##
## Pearson's Chi-squared test
##
## data: table_embarked_survived
## X-squared = 25.964, df = 2, p-value = 2.301e-06
Nhận xét: Với p-value 2.3e-06 (nhỏ hơn 0.05), chúng ta bác bỏ giả thuyết \(H_0\). Có bằng chứng thống kê mạnh mẽ cho thấy Embarked và Survived là phụ thuộc, tức là cảng lên tàu có mối quan hệ có ý nghĩa với khả năng sống sót.
Lấy cảng Cherbourg (C) làm nhóm tham chiếu vì nó có tỷ lệ sống sót cao nhất.
##
## Perished Survived Sum
## Cherbourg 75 93 168
## Queenstown 47 30 77
## Southampton 427 219 646
## Sum 549 342 891
## $data
##
## Perished Survived Total
## Cherbourg 75 93 168
## Queenstown 47 30 77
## Southampton 427 219 646
## Total 549 342 891
##
## $measure
## risk ratio with 95% C.I.
## estimate lower upper
## Cherbourg 1.0000000 NA NA
## Queenstown 0.7038123 0.5157936 0.9603682
## Southampton 0.6124039 0.5149589 0.7282882
##
## $p.value
## two-sided
## midp.exact fisher.exact chi.square
## Cherbourg NA NA NA
## Queenstown 1.796481e-02 1.954610e-02 1.718059e-02
## Southampton 5.201648e-07 5.233446e-07 3.473864e-07
##
## $correction
## [1] FALSE
##
## attr(,"method")
## [1] "Unconditional MLE & normal approximation (Wald) CI"
## $data
##
## Perished Survived Total
## Cherbourg 75 93 168
## Queenstown 47 30 77
## Southampton 427 219 646
## Total 549 342 891
##
## $measure
## odds ratio with 95% C.I.
## estimate lower upper
## Cherbourg 1.0000000 NA NA
## Queenstown 0.5169647 0.2955153 0.8934725
## Southampton 0.4142557 0.2926246 0.5845844
##
## $p.value
## two-sided
## midp.exact fisher.exact chi.square
## Cherbourg NA NA NA
## Queenstown 1.796481e-02 1.954610e-02 1.718059e-02
## Southampton 5.201648e-07 5.233446e-07 3.473864e-07
##
## $correction
## [1] FALSE
##
## attr(,"method")
## [1] "median-unbiased estimate & mid-p exact CI"
Nhận xét về RR và OR cho Embarked:
Tỷ lệ sống sót của hành khách lên tàu từ Queenstown (Q) là khoảng 0.7 lần so với Cherbourg. Khoảng tin cậy 95% RR là từ 0.52 đến 0.96. Giá trị này nhỏ hơn 1, cho thấy tỷ lệ sống sót từ Queenstown thấp hơn.
Tỷ lệ sống sót của hành khách lên tàu từ Southampton (S) là khoảng0.61 lần so với Cherbourg. Khoảng tin cậy 95% RR là từ 0.51 đến 0.73. Đây là một mức giảm đáng kể, cho thấy tỷ lệ sống sót từ Southampton thấp hơn nhiều so với Cherbourg.
Tỷ lệ chênh sống sót của hành khách lên tàu từ Queenstown (Q) là khoảng 0.52 lần so với Cherbourg. Khoảng tin cậy 95% OR là từ 0.3 đến 0.89. Điều này cho thấy odds sống sót từ Queenstown thấp hơn.
Tỷ lệ chênh sống sót của hành khách lên tàu từ Southampton (S) là khoảng 0.41, lần so với Cherbourg. Khoảng tin cậy 95% OR là từ 0.29 đến 0.58. Đây là một sự khác biệt đáng kể, khẳng định odds sống sót từ Southampton thấp hơn nhiều so với Cherbourg.
Phân tích bộ dữ liệu train.csv của Titanic đã cho thấy một số mối quan hệ quan trọng giữa các biến định tính và khả năng sống sót:
# Đảm bảo biến mục tiêu Survived là kiểu factor và cấp độ "Survived" là thành công (reference level của R)
# Mặc định, cấp độ thứ hai sẽ là thành công. Ở đây, "Survived" là cấp độ thứ 2.
levels(titanic_factor$Survived)
## [1] "Perished" "Survived"
# Chạy mô hình hồi quy Logistic
model_logistic <- glm(Survived ~ Sex + Pclass + Age + SibSp + Parch + Fare + Has_Cabin + Embarked,
data = titanic_factor,
family = binomial(link = "logit"))
# Xem tóm tắt kết quả mô hình
summary(model_logistic)
##
## Call:
## glm(formula = Survived ~ Sex + Pclass + Age + SibSp + Parch +
## Fare + Has_Cabin + Embarked, family = binomial(link = "logit"),
## data = titanic_factor)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 0.612951 0.496488 1.235 0.216990
## SexFemale 2.713225 0.202526 13.397 < 2e-16 ***
## Pclass2nd Class -0.206858 0.388898 -0.532 0.594790
## Pclass3rd Class -1.395748 0.395265 -3.531 0.000414 ***
## Age -0.038564 0.007949 -4.852 1.22e-06 ***
## SibSp -0.316595 0.110341 -2.869 0.004114 **
## Parch -0.116758 0.120276 -0.971 0.331672
## Fare 0.001982 0.002454 0.808 0.419136
## Has_CabinYes 0.991500 0.335240 2.958 0.003101 **
## EmbarkedQueenstown -0.090243 0.382016 -0.236 0.813255
## EmbarkedSouthampton -0.480470 0.241407 -1.990 0.046559 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 1186.66 on 890 degrees of freedom
## Residual deviance: 776.01 on 880 degrees of freedom
## AIC: 798.01
##
## Number of Fisher Scoring iterations: 5
Các hệ số trong mô hình hồi quy Logistic biểu diễn sự thay đổi của log-odds của việc sống sót khi biến độc lập tương ứng tăng thêm một đơn vị (đối với biến định lượng) hoặc thay đổi cấp độ (đối với biến định tính), giữ các biến khác không đổi.
Để diễn giải dễ hiểu hơn, chúng ta thường chuyển đổi các hệ số này thành Odds Ratio (OR) bằng cách lấy hàm mũ của hệ số (exp(beta)). Một OR lớn hơn 1 cho thấy tăng khả năng sống sót, trong khi OR nhỏ hơn 1 cho thấy giảm khả năng sống sót.
# Tính Odds Ratio và khoảng tin cậy 95%
exp_coefficients_logistic <- exp(coef(model_logistic))
conf_int_logistic <- exp(confint(model_logistic))
# Tạo bảng tóm tắt OR
or_table_logistic <- data.frame(
Estimate = exp_coefficients_logistic,
Lower_CI = conf_int_logistic[, 1],
Upper_CI = conf_int_logistic[, 2]
) %>%
round(3)
kable(or_table_logistic, caption = "Odds Ratio và Khoảng tin cậy 95% cho Mô hình Logistic")
Estimate | Lower_CI | Upper_CI | |
---|---|---|---|
(Intercept) | 1.846 | 0.693 | 4.878 |
SexFemale | 15.078 | 10.223 | 22.637 |
Pclass2nd Class | 0.813 | 0.382 | 1.763 |
Pclass3rd Class | 0.248 | 0.115 | 0.543 |
Age | 0.962 | 0.947 | 0.977 |
SibSp | 0.729 | 0.580 | 0.895 |
Parch | 0.890 | 0.698 | 1.122 |
Fare | 1.002 | 0.998 | 1.007 |
Has_CabinYes | 2.695 | 1.408 | 5.256 |
EmbarkedQueenstown | 0.914 | 0.430 | 1.927 |
EmbarkedSouthampton | 0.618 | 0.385 | 0.994 |
Nhận xét về Odds Ratio:
SexFemale: OR là 15.078 (Khoảng tin cậy 95%: 10.223 - 22.637). OR này lớn hơn 1 đáng kể và khoảng tin cậy không chứa 1, cho thấy tỷ lệ chênh sống sót của nữ giới cao gấp khoảng 15.08 lần so với nam giới (nhóm tham chiếu). Đây là một tác động rất mạnh và có ý nghĩa thống kê.
Pclass2nd Class và Pclass3rd Class: OR cho 2nd Class và 3rd Class đều nhỏ hơn 1 (0.813 và 0.248). Điều này cho thấy hành khách ở hạng 2 và hạng 3 có tỷ lệ chênh sống sót thấp hơn đáng kể so với hạng 1 (nhóm tham chiếu). Hạng 3 có OR thấp nhất, cho thấy tác động tiêu cực mạnh nhất.
Age: OR gần 1 (0.962), nhưng nhỏ hơn 1 một chút. Điều này cho thấy với mỗi năm tăng thêm của tuổi, tỷ lệ chênh sống sót hơi giảm nhẹ, tuy nhiên tác động này khá nhỏ.
SibSp và Parch: OR của SibSp (0.729) và Parch (0.89) cho thấy việc có thêm anh chị em/vợ chồng hoặc cha mẹ/con cái đi cùng có xu hướng làm giảm nhẹ tỷ lệ chênh sống sót. Điều này có thể phản ánh việc các gia đình lớn gặp khó khăn hơn trong việc tìm kiếm và lên thuyền cứu sinh.
Fare: OR của Fare (1.002) rất gần 1. Mặc dù có ý nghĩa thống kê, nhưng tác động thực tế của mỗi đơn vị tăng giá vé lên tỷ lệ chênh sống sót là rất nhỏ. Điều này có thể là do Fare đã bị ảnh hưởng bởi Pclass.
Has_CabinYes: OR lớn hơn 1 (2.695). Điều này cho thấy những hành khách có thông tin cabin có tỷ lệ chênh sống sót cao hơn đáng kể so với những người không có thông tin cabin. Đây là một biến quan trọng.
EmbarkedQueenstown và EmbarkedSouthampton: OR cho Queenstown (0.914) và Southampton (0.618) đều nhỏ hơn 1 so với Cherbourg (nhóm tham chiếu). Điều này cho thấy hành khách lên tàu từ các cảng này có tỷ lệ chênh sống sót thấp hơn so với Cherbourg.
# Dự đoán xác suất sống sót
# type = "response" để nhận xác suất P(Y=1|X)
predicted_probs_logistic <- predict(model_logistic, newdata = titanic_factor, type = "response")
# Chuyển xác suất thành dự đoán lớp (ví dụ: ngưỡng 0.5)
# Nếu xác suất >= 0.5 thì dự đoán là "Survived", ngược lại là "Perished"
predicted_class_logistic <- factor(ifelse(predicted_probs_logistic >= 0.5, "Survived", "Perished"),
levels = c("Perished", "Survived"))
# Tạo ma trận nhầm lẫn (Confusion Matrix)
confusion_matrix_logistic <- table(Actual = titanic_factor$Survived, Predicted = predicted_class_logistic)
print("Ma trận nhầm lẫn (Logistic Regression):")
## [1] "Ma trận nhầm lẫn (Logistic Regression):"
## Predicted
## Actual Perished Survived
## Perished 476 73
## Survived 95 247
# Tính độ chính xác (Accuracy)
accuracy_logistic <- sum(diag(confusion_matrix_logistic)) / sum(confusion_matrix_logistic)
cat("\nAccuracy của mô hình Logistic:", round(accuracy_logistic, 3), "\n")
##
## Accuracy của mô hình Logistic: 0.811
# Tính các chỉ số khác (Precision, Recall, F1-score)
# (Giả sử "Survived" là Positive class)
TP_logistic <- confusion_matrix_logistic["Survived", "Survived"]
FP_logistic <- confusion_matrix_logistic["Perished", "Survived"]
FN_logistic <- confusion_matrix_logistic["Survived", "Perished"]
TN_logistic <- confusion_matrix_logistic["Perished", "Perished"]
precision_logistic <- TP_logistic / (TP_logistic + FP_logistic)
recall_logistic <- TP_logistic / (TP_logistic + FN_logistic)
f1_score_logistic <- 2 * (precision_logistic * recall_logistic) / (precision_logistic + recall_logistic)
cat("Precision (Survived):", round(precision_logistic, 3), "\n")
## Precision (Survived): 0.772
## Recall (Survived): 0.722
## F1-Score (Survived): 0.746
Nhận xét về đánh giá mô hình Logistic:
Accuracy: Mô hình đạt độ chính xác khoảng 0.811, cho thấy khoảng 81.1% các dự đoán của mô hình là đúng.
Precision: Precision của lớp “Survived” là 0.772. Điều này có nghĩa là trong số tất cả các trường hợp mô hình dự đoán là “Survived”, 77.2% trong số đó thực sự sống sót.
Recall: Recall của lớp “Survived” là 0.722. Điều này có nghĩa là mô hình đã xác định đúng 72.2% số lượng người thực sự sống sót.
F1-Score: F1-Score là trung bình điều hòa của Precision và Recall, cho biết một thước đo cân bằng hơn về hiệu suất mô hình, đạt 0.746.
Mô hình Logistic cho thấy khả năng phân loại khá tốt, đặc biệt là trong việc xác định những người không sống sót (Perished) với số lượng lớn.
# Chạy mô hình hồi quy Probit
model_probit <- glm(Survived ~ Sex + Pclass + Age + SibSp + Parch + Fare + Has_Cabin + Embarked,
data = titanic_factor,
family = binomial(link = "probit"))
# Xem tóm tắt kết quả mô hình
summary(model_probit)
##
## Call:
## glm(formula = Survived ~ Sex + Pclass + Age + SibSp + Parch +
## Fare + Has_Cabin + Embarked, family = binomial(link = "probit"),
## data = titanic_factor)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 0.335535 0.287032 1.169 0.242410
## SexFemale 1.597684 0.112889 14.153 < 2e-16 ***
## Pclass2nd Class -0.128436 0.224673 -0.572 0.567553
## Pclass3rd Class -0.777869 0.225990 -3.442 0.000577 ***
## Age -0.021866 0.004489 -4.871 1.11e-06 ***
## SibSp -0.183726 0.061488 -2.988 0.002808 **
## Parch -0.078602 0.070569 -1.114 0.265352
## Fare 0.001382 0.001422 0.972 0.331132
## Has_CabinYes 0.555646 0.192991 2.879 0.003988 **
## EmbarkedQueenstown -0.085217 0.217271 -0.392 0.694898
## EmbarkedSouthampton -0.280993 0.137545 -2.043 0.041061 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 1186.66 on 890 degrees of freedom
## Residual deviance: 777.85 on 880 degrees of freedom
## AIC: 799.85
##
## Number of Fisher Scoring iterations: 5
Các hệ số trong mô hình Probit biểu diễn sự thay đổi của giá trị z-score của việc sống sót khi biến độc lập tương ứng thay đổi. Diễn giải trực tiếp các hệ số này khó hơn so với Logistic (vì không thể chuyển trực tiếp thành Odds Ratio). Tuy nhiên, dấu của hệ số vẫn chỉ ra hướng của mối quan hệ (dương nghĩa là tăng khả năng sống sót, âm nghĩa là giảm).
Để hiểu tác động thực tế, thường tính marginal effects (hiệu ứng biên) cho mô hình Probit, nhưng điều đó phức tạp hơn một chút. Về cơ bản, một hệ số dương có nghĩa là khi biến độc lập tăng, xác suất sống sót tăng lên và ngược lại.
# Ví dụ: so sánh hệ số (chỉ để xem hướng và độ lớn tương đối)
# Coefficients Probit
coef_probit <- coef(model_probit)
print(round(coef_probit, 3))
## (Intercept) SexFemale Pclass2nd Class Pclass3rd Class
## 0.336 1.598 -0.128 -0.778
## Age SibSp Parch Fare
## -0.022 -0.184 -0.079 0.001
## Has_CabinYes EmbarkedQueenstown EmbarkedSouthampton
## 0.556 -0.085 -0.281
Nhận xét về hệ số Probit:
Tương tự như Logistic:
SexFemale (1.598): Hệ số này dương và khá lớn, cho thấy nữ giới có xu hướng sống sót cao hơn đáng kể so với nam giới (nhóm tham chiếu). Đây là một tác động mạnh mẽ và gần như có ý nghĩa thống kê.
Pclass2nd Class (-0.128): Hệ số này âm, cho thấy hành khách ở hạng 2 có xu hướng sống sót thấp hơn so với hạng 1 (nhóm tham chiếu). Tuy nhiên, độ lớn của hệ số này khá nhỏ và có thể không có ý nghĩa thống kê.
Pclass3rd Class (-0.778): Hệ số này âm và khá lớn, cho thấy hành khách ở hạng 3 có xu hướng sống sót thấp hơn đáng kể so với hạng 1. Đây là một tác động mạnh mẽ và nhiều khả năng có ý nghĩa thống kê.
Age (-0.022): Hệ số này âm nhưng rất nhỏ, cho thấy mỗi năm tăng thêm của tuổi có xu hướng làm giảm nhẹ khả năng sống sót. Tác động này có thể có hoặc không có ý nghĩa thống kê, tùy thuộc vào p-value cụ thể.
SibSp (-0.184): Hệ số này âm, cho thấy việc có thêm anh chị em/vợ chồng đi cùng có xu hướng giảm nhẹ khả năng sống sót.
Parch (-0.079): Hệ số này âm và rất nhỏ, cho thấy việc có thêm cha mẹ/con cái đi cùng có xu hướng giảm nhẹ khả năng sống sót. Tác động này có thể không có ý nghĩa thống kê.
Fare (0.001): Hệ số này rất gần 0 và dương nhẹ, cho thấy giá vé có tác động rất nhỏ (gần như không đáng kể) đến khả năng sống sót. Rất có thể hệ số này không có ý nghĩa thống kê.
Has_CabinYes (0.556): Hệ số này dương và khá lớn, cho thấy những hành khách có thông tin cabin có xu hướng sống sót cao hơn đáng kể so với những người không có thông tin cabin. Đây là một tác động mạnh mẽ và nhiều khả năng có ý nghĩa thống kê.
EmbarkedQueenstown (-0.085): Hệ số này âm và khá nhỏ, cho thấy hành khách lên tàu từ Queenstown có xu hướng giảm nhẹ khả năng sống sót so với Cherbourg (nhóm tham chiếu). Tác động này có thể không có ý nghĩa thống kê.
EmbarkedSouthampton (-0.281): Hệ số này âm, cho thấy hành khách lên tàu từ Southampton có xu hướng giảm khả năng sống sót so với Cherbourg. Độ lớn trung bình, cần kiểm tra p-value để biết nó có ý nghĩa thống kê hay không.
# Dự đoán xác suất sống sót trên tập dữ liệu huấn luyện
# type = "response" để nhận xác suất P(Y=1|X)
predicted_probs_probit <- predict(model_probit, newdata = titanic_factor, type = "response")
# Chuyển xác suất thành dự đoán lớp (ví dụ: ngưỡng 0.5)
predicted_class_probit <- factor(ifelse(predicted_probs_probit >= 0.5, "Survived", "Perished"),
levels = c("Perished", "Survived"))
# Tạo ma trận nhầm lẫn (Confusion Matrix)
confusion_matrix_probit <- table(Actual = titanic_factor$Survived, Predicted = predicted_class_probit)
print("Ma trận nhầm lẫn (Probit Regression):")
## [1] "Ma trận nhầm lẫn (Probit Regression):"
## Predicted
## Actual Perished Survived
## Perished 474 75
## Survived 95 247
# Tính độ chính xác (Accuracy)
accuracy_probit <- sum(diag(confusion_matrix_probit)) / sum(confusion_matrix_probit)
cat("\nAccuracy của mô hình Probit:", round(accuracy_probit, 3), "\n")
##
## Accuracy của mô hình Probit: 0.809
# Tính các chỉ số khác (Precision, Recall, F1-score)
TP_probit <- confusion_matrix_probit["Survived", "Survived"]
FP_probit <- confusion_matrix_probit["Perished", "Survived"]
FN_probit <- confusion_matrix_probit["Survived", "Perished"]
TN_probit <- confusion_matrix_probit["Perished", "Perished"]
precision_probit <- TP_probit / (TP_probit + FP_probit)
recall_probit <- TP_probit / (TP_probit + FN_probit)
f1_score_probit <- 2 * (precision_probit * recall_probit) / (precision_probit + recall_probit)
cat("Precision (Survived):", round(precision_probit, 3), "\n")
## Precision (Survived): 0.767
## Recall (Survived): 0.722
## F1-Score (Survived): 0.744
Nhận xét về đánh giá mô hình Probit:
Các chỉ số đánh giá
Accuracy (Độ chính xác): 0.809 Mô hình Probit đạt độ chính xác khoảng 80.9%. Điều này có nghĩa là khoảng 80.9% tổng số dự đoán của mô hình (cả đúng người sống sót và đúng người tử vong) là chính xác. Mức độ này rất tương đồng với mô hình Logistic, cho thấy cả hai mô hình đều có khả năng tổng quát hóa khá tốt trên tập dữ liệu này.
Precision (Độ chuẩn xác) cho lớp “Survived”: 0.767 Precision là 76.7%. Điều này có nghĩa là trong số tất cả các hành khách mà mô hình dự đoán là sống sót, có đến 76.7% trong số đó thực sự sống sót. Đây là một chỉ số quan trọng khi chi phí của việc dự đoán sai “sống sót” (False Positive) là cao.
Recall (Độ nhạy/Độ phủ) cho lớp “Survived”: 0.722 Recall là 72.2%. Điều này có nghĩa là mô hình đã xác định đúng 72.2% số lượng người thực sự sống sót trong tổng số những người sống sót. Đây là chỉ số quan trọng khi chi phí của việc bỏ sót người thực sự sống sót (False Negative) là cao.
F1-Score cho lớp “Survived”: 0.744 F1-Score là 74.4%. Đây là trung bình điều hòa của Precision và Recall, cung cấp một thước đo cân bằng hơn về hiệu suất của mô hình khi có sự mất cân bằng giữa các lớp. Giá trị này cho thấy một sự cân bằng khá tốt giữa Precision và Recall của mô hình Probit.
aic_logistic <- AIC(model_logistic)
aic_probit <- AIC(model_probit)
cat("AIC của mô hình Logistic:", round(aic_logistic, 2), "\n")
## AIC của mô hình Logistic: 798.01
## AIC của mô hình Probit: 799.85
Nhận xét về AIC:
Mô hình với AIC thấp hơn được ưu tiên. Ở đây, AIC của mô hình Logistic (798.01) và Probit (799.85) rất gần nhau. Điều này cho thấy cả hai mô hình đều có hiệu suất tương đương trong việc giải thích dữ liệu. Trong trường hợp này, việc lựa chọn mô hình nào có thể phụ thuộc vào mục đích sử dụng hoặc sự ưu tiên trong việc diễn giải (Odds Ratio của Logistic thường trực quan hơn).
# Đọc dữ liệu (stringsAsFactors = FALSE: không chuyển chuỗi thành factor, header = TRUE: dòng đầu tiên là tiêu đề cột)
data <- read.csv("NV1 - Supermarket Transactions.csv", stringsAsFactors = FALSE, header = TRUE)
# Xem cấu trúc tổng quát của dữ liệu: tên biến, kiểu 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 ...
## [1] "X" "PurchaseDate" "CustomerID"
## [4] "Gender" "MaritalStatus" "Homeowner"
## [7] "Children" "AnnualIncome" "City"
## [10] "StateorProvince" "Country" "ProductFamily"
## [13] "ProductDepartment" "ProductCategory" "UnitsSold"
## [16] "Revenue"
Cách xử lý giá trị thiếu:
Nếu có NA:
Nếu số lượng NA nhỏ → loại bỏ dòng chứa NA để tránh nhiễu.
Nếu số lượng NA lớn hoặc phân bố đều → thay thế bằng mode để giữ lại dữ liệu. Việc thay thế bằng mode sẽ giữ được cỡ mẫu, hạn chế mất dữ liệu, đồng thời vẫn phản ánh xu hướng chính của biến.
# Lọc biến định tính
dinhtinh <- c("Gender", "MaritalStatus", "Homeowner", "AnnualIncome",
"City", "StateorProvince", "Country",
"ProductFamily", "ProductDepartment", "ProductCategory")
# Kiểm tra số lượng giá trị NA trong từng biến (sapply(...): áp dụng hàm cho từng cột và trả về vector, data[dinhtinh]: chọn các cột trong data theo tên/thứ tự có trong dinhtinh, function(x) ...: hàm ẩn danh áp dụng cho từng cột, is.na(x): kiểm tra giá trị NA trong cột, sum(is.na(x)): đếm số lượng NA trong cột)
nacounts <- sapply(data[dinhtinh], function(x) sum(is.na(x)))
nacounts
## Gender MaritalStatus Homeowner AnnualIncome
## 0 0 0 0
## City StateorProvince Country ProductFamily
## 0 0 0 0
## ProductDepartment ProductCategory
## 0 0
Điều này cho thấy bảng dữ liệu dinhtinh không có giá trị NA.
factor
(nếu chưa
phải)Trong R, kiểu factor đại diện cho biến định tính (categorical), điều này cực kỳ quan trọng trong phân tích thống kê như:
Tính tần suất
Biểu đồ cột (bar chart)
Kiểm định Chi-square
Mô hình phân loại
# Chuyển tất cả biến định tính đã liệt kê thành kiểu factor (nếu chưa) (lapply(...): áp dụng hàm cho từng cột, trả về list, data[dinhtinh]: chọn các cột cần chuyển đổi, factor: hàm chuyển dữ liệu thành kiểu factor (biến phân loại))
data[dinhtinh] <- lapply(data[dinhtinh], factor)
# Kiểm tra lại để đảm bảo chuyển đổi thành công
str(data[dinhtinh])
## 'data.frame': 14059 obs. of 10 variables:
## $ Gender : Factor w/ 2 levels "F","M": 1 2 1 2 1 1 2 1 2 2 ...
## $ MaritalStatus : Factor w/ 2 levels "M","S": 2 1 1 1 2 1 2 1 1 2 ...
## $ Homeowner : Factor w/ 2 levels "N","Y": 2 2 1 2 2 2 2 2 2 1 ...
## $ AnnualIncome : Factor w/ 8 levels "$10K - $30K",..: 5 7 6 5 3 1 5 4 1 6 ...
## $ City : Factor w/ 23 levels "Acapulco","Bellingham",..: 8 8 4 12 3 3 13 23 2 15 ...
## $ StateorProvince : Factor w/ 10 levels "BC","CA","DF",..: 2 2 8 6 2 2 6 8 8 2 ...
## $ Country : Factor w/ 3 levels "Canada","Mexico",..: 3 3 3 3 3 3 3 3 3 3 ...
## $ ProductFamily : Factor w/ 3 levels "Drink","Food",..: 2 2 2 2 1 2 2 2 3 3 ...
## $ ProductDepartment: Factor w/ 22 levels "Alcoholic Beverages",..: 20 18 20 21 4 11 13 6 15 14 ...
## $ ProductCategory : Factor w/ 45 levels "Baking Goods",..: 42 45 42 7 15 41 5 13 16 35 ...
# Bảng tần số
gender <- table(data$Gender)
# Bảng tần suất
tylegender <- round(prop.table(gender) * 100, 2)
# Tạo bảng trình bày rõ ràng
gendertable <- data.frame(
Gender = names(gender),
Frequency = as.vector(gender),
Percentage = paste0(tylegender, "%") # Hàm paste0() dùng để nối chuỗi ký tự (text) lại với nhau.hàm paste0() tự động bỏ khoảng trắng, còn paste() thì mặc định chèn khoảng trắng giữa các phần.
)
# Hiển thị bảng
library(knitr)
kable(gendertable, caption = "Bảng tần suất và tỷ lệ phần trăm giới tính")
Gender | Frequency | Percentage |
---|---|---|
F | 7170 | 51% |
M | 6889 | 49% |
Bảng thống kê cho thấy biến Gender
gồm có 2 hạng mục.
Trong đó, Female: chiếm 51% với 7170 trường hợp. Và Male: chiếm 49% với
6889 trường hợp.
Nhìn chung, Female chiếm tỷ lệ cao hơn (51%) so với hạng mục còn lại, phản ánh sự mất cân đối nhất định trong phân bố giới tính của khách hàng.
library(ggplot2)
library(dplyr)
# Chuẩn bị dữ liệu để vẽ pie chart
genderplot <- gendertable %>% # %>%: toán tử pipe, truyền dữ liệu sang hàm tiếp theo
mutate( # mutate(...): thêm cột mới vào data frame
Label = paste0(Gender, ": ", Percentage, "") # Gắn nhãn
)
# Vẽ biểu đồ tròn bằng ggplot2
ggplot(genderplot, aes(x = "", y = Frequency, fill = Gender)) +
geom_bar(stat = "identity", width = 1, color = "white") +
coord_polar(theta = "y") + # Chuyển bar chart thành pie chart
theme_void() + # Xoá trục, nền để tạo pie chart
labs(title = "Biểu đồ tròn: Tỷ lệ giới tính") +
geom_text(aes(label = Label), position = position_stack(vjust = 0.5)) +
scale_fill_manual(values = c("lightblue", "pink")) + # Tùy chọn màu
theme(legend.title = element_blank())
Biểu đồ tròn trực quan hóa sự phân bố tỷ lệ giới tính trong tập dữ liệu. Từ biểu đồ, ta dễ dàng nhận thấy phần diện tích tương ứng với Female lớn hơn, củng cố kết luận từ bảng thống kê rằng đây là nhóm khách hàng chủ yếu.
# Bảng tần số
maritalStatus <- table(data$MaritalStatus)
# Bảng tần suất
tylemaritalStatus <- round(prop.table(maritalStatus) * 100, 2)
# Tạo bảng trình bày rõ ràng
maritalStatustable <- data.frame(
MaritalStatus = names(maritalStatus),
Frequency = as.vector(maritalStatus),
Percentage = paste0(tylemaritalStatus, "%")
)
# Hiển thị bảng
library(knitr)
kable(maritalStatustable, caption = "Bảng tần suất và tỷ lệ phần trăm tình trạng hôn nhân")
MaritalStatus | Frequency | Percentage |
---|---|---|
M | 6866 | 48.84% |
S | 7193 | 51.16% |
Bảng thống kê cho thấy biến MaritalStatus
gồm có 2 hạng
mục. Trong đó, Married: chiếm 48.84% với 6866 trường hợp. Và Single:
chiếm 51.16% với 7193 trường hợp.
Nhìn chung, Single chiếm tỷ lệ cao hơn (51.16%) so với hạng mục còn lại, phản ánh sự mất cân đối nhất định về tình trạng hôn nhân của khách hàng.
library(ggplot2)
library(dplyr)
# Chuẩn bị dữ liệu để vẽ pie chart
maritalStatusplot <- maritalStatustable %>%
mutate(
Label = paste0(MaritalStatus, ": ", Percentage, "") # Gắn nhãn
)
# Vẽ biểu đồ tròn bằng ggplot2
ggplot(maritalStatusplot, aes(x = "", y = Frequency, fill = MaritalStatus)) +
geom_bar(stat = "identity", width = 1, color = "white") +
coord_polar(theta = "y") + # Chuyển bar chart thành pie chart
theme_void() + # Xoá trục, nền để tạo pie chart
labs(title = "Biểu đồ tròn: Tỷ lệ tình trạng hôn nhân") +
geom_text(aes(label = Label), position = position_stack(vjust = 0.5)) +
scale_fill_manual(values = c("lightyellow", "violet")) + # Tùy chọn màu
theme(legend.title = element_blank())
Biểu đồ tròn trực quan hóa sự phân bố tỷ lệ tình trạng hôn nhân trong tập dữ liệu. Từ biểu đồ, ta dễ dàng nhận thấy phần diện tích tương ứng với Single lớn hơn, cho thấy sự phân bố không đều giữa hai trạng thái hôn nhận nhưng sự chênh lệch không quá lớn như giới tính.
# Bảng tần số
homeowner <- table(data$Homeowner)
# Bảng tần suất
tylehomeowner <- round(prop.table(homeowner) * 100, 2)
# Tạo bảng trình bày rõ ràng
homeownertable <- data.frame(
Homeowner = names(homeowner),
Frequency = as.vector(homeowner),
Percentage = paste0(tylehomeowner, "%")
)
# Hiển thị bảng
library(knitr)
kable(homeownertable, caption = "Bảng tần suất và tỷ lệ phần trăm về việc sở hữu nhà")
Homeowner | Frequency | Percentage |
---|---|---|
N | 5615 | 39.94% |
Y | 8444 | 60.06% |
Biến Homeowner
gồm 2 hạng mục: Y (Có nhà) chiếm 60.06%
với 8444 khách hàng và N (Không có nhà) chiếm 39.94% với 5615 khách
hàng.
Nhìn chung, nhóm Y chiếm tỷ lệ cao hơn 60.06%, cho thấy phần lớn khách hàng của siêu thị có nhà.
library(ggplot2)
library(dplyr)
# Chuẩn bị dữ liệu để vẽ pie chart
homeownerplot <- homeownertable %>%
mutate(
Label = paste0(Homeowner, ": ", Percentage, "") # Gắn nhãn
)
# Vẽ biểu đồ tròn bằng ggplot2
ggplot(homeownerplot, aes(x = "", y = Frequency, fill = Homeowner)) +
geom_bar(stat = "identity", width = 1, color = "white") +
coord_polar(theta = "y") + # Chuyển bar chart thành pie chart
theme_void() + # Xoá trục, nền để tạo pie chart
labs(title = "Biểu đồ tròn: Tỷ lệ sở hữu nhà") +
geom_text(aes(label = Label), position = position_stack(vjust = 0.5)) +
scale_fill_manual(values = c("green", "grey")) + # Tùy chọn màu
theme(legend.title = element_blank())
Biểu đồ tròn giúp trực quan hóa tỷ lệ sở hữu nhà. Phần lát cắt lớn hơn thuộc về nhóm Y, phù hợp với số liệu thống kê. Sự chênh lệch khá lớn so với giới tính và tình trạng hôn nhân. Vì biến này chỉ có 2 giá trị, biểu đồ tròn rất phù hợp để thể hiện sự khác biệt về tỷ lệ giữa các nhóm khách hàng.
# Bảng tần suất
income <- table(data$AnnualIncome)
# Bảng tần suất
tyleincome <- round(prop.table(income) * 100, 2)
# Tạo bảng trình bày
incometable <- data.frame(
AnnualIncome = names(income),
Frequency = as.vector(income),
Percentage = paste0(tyleincome, "%")
)
# Hiển thị bảng
library(knitr)
kable(incometable, caption = "Bảng tần suất và tỷ lệ phần trăm theo khoảng thu nhập")
AnnualIncome | Frequency | Percentage |
---|---|---|
$10K - $30K | 3090 | 21.98% |
$110K - $130K | 643 | 4.57% |
$130K - $150K | 760 | 5.41% |
$150K + | 273 | 1.94% |
$30K - $50K | 4601 | 32.73% |
$50K - $70K | 2370 | 16.86% |
$70K - $90K | 1709 | 12.16% |
$90K - $110K | 613 | 4.36% |
Biến AnnualIncome
được chia thành 8 khoảng khác nhau.
Trong đó, nhóm thu nhập “$30K - $50K” có số lượng khách hàng cao nhất:
4601 người, tương đương 32.73%. Nhóm thu nhập có tỷ lệ thấp nhất là
“$150k +”, chỉ chiếm 1.94%. Điều này cho thấy tập khách hàng chủ yếu nằm
trong khoảng thu nhập thấp.
library(ggplot2)
# Chuyển về data.frame để ggplot dễ xử lý
incomeplotdf <- as.data.frame(income)
colnames(incomeplotdf) <- c("AnnualIncome", "Frequency")
# Vẽ biểu đồ cột
ggplot(incomeplotdf, aes(x = AnnualIncome, y = Frequency)) +
geom_bar(stat = "identity", fill = "steelblue") +
labs(
title = "Phân bố khách hàng theo khoảng thu nhập",
x = "Khoảng thu nhập hằng năm",
y = "Số lượng khách hàng"
) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Biểu đồ cột thể hiện rõ sự phân hóa theo thu nhập. Các cột có chiều cao không đồng đều, cho thấy sự mất cân đối giữa các khoảng thu nhập. Nhóm thu nhập “$30K - $50K” nổi bật hơn hẳn các nhóm còn lại, cho thấy đây có thể là nhóm khách hàng mục tiêu tiềm năng nhất của siêu thị.
# Bảng tần suất
city <- table(data$City)
# Bảng tần suất
tylecity <- round(prop.table(city) * 100, 2)
# Tạo bảng trình bày
citytable <- data.frame(
City = names(city),
Frequency = as.vector(city),
Percentage = paste0(tylecity, "%")
)
# Hiển thị bảng
library(knitr)
kable(citytable, caption = "Bảng tần suất và tỷ lệ phần trăm theo thành phố")
City | Frequency | Percentage |
---|---|---|
Acapulco | 383 | 2.72% |
Bellingham | 143 | 1.02% |
Beverly Hills | 811 | 5.77% |
Bremerton | 834 | 5.93% |
Camacho | 452 | 3.22% |
Guadalajara | 75 | 0.53% |
Hidalgo | 845 | 6.01% |
Los Angeles | 926 | 6.59% |
Merida | 654 | 4.65% |
Mexico City | 194 | 1.38% |
Orizaba | 464 | 3.3% |
Portland | 876 | 6.23% |
Salem | 1386 | 9.86% |
San Andres | 621 | 4.42% |
San Diego | 866 | 6.16% |
San Francisco | 130 | 0.92% |
Seattle | 922 | 6.56% |
Spokane | 875 | 6.22% |
Tacoma | 1257 | 8.94% |
Vancouver | 633 | 4.5% |
Victoria | 176 | 1.25% |
Walla Walla | 160 | 1.14% |
Yakima | 376 | 2.67% |
Biến City
có 23 thành phố khác nhau. Trong đó, thành phố
có số lượng giao dịch nhiều nhất là Salem, chiếm 9.86% với 1386 lượt).
Nhiều thành phố chỉ chiếm tỷ lệ nhỏ dưới 5%. Điều này phản ánh sự tập
trung khách hàng tại một số thành phố lớn, là thị trường trọng điểm cho
doanh nghiệp.
# Đổi sang data frame cho ggplot2
cityplotdf <- as.data.frame(city)
colnames(cityplotdf) <- c("City", "Frequency")
# Vẽ biểu đồ cột
library(ggplot2)
ggplot(cityplotdf, aes(x = reorder(City, -Frequency), y = Frequency)) +
geom_bar(stat = "identity", fill = "steelblue") +
labs(
title = "Phân bố khách hàng theo thành phố",
x = "Thành phố",
y = "Số lượng khách hàng"
) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 90, hjust = 1))
Biểu đồ cột thể hiện rõ sự chênh lệch về số lượng khách hàng giữa các thành phố. Các cột có chiều cao không đồng đều và thấp dần, đặc biệt thành phố Salem có cột vượt trội, chứng tỏ đây là trung tâm mua sắm chính trong dữ liệu.
# Bảng tần suất
state <- table(data$StateorProvince)
# Bảng tần suất
tylestate <- round(prop.table(state) * 100, 2)
# Tạo bảng trình bày
statetable <- data.frame(
StateorProvince = names(state),
Frequency = as.vector(state),
Percentage = paste0(tylestate, "%")
)
# Hiển thị bảng
library(knitr)
kable(statetable, caption = "Bảng tần suất và tỷ lệ phần trăm theo tỉnh/bang")
StateorProvince | Frequency | Percentage |
---|---|---|
BC | 809 | 5.75% |
CA | 2733 | 19.44% |
DF | 815 | 5.8% |
Guerrero | 383 | 2.72% |
Jalisco | 75 | 0.53% |
OR | 2262 | 16.09% |
Veracruz | 464 | 3.3% |
WA | 4567 | 32.48% |
Yucatan | 654 | 4.65% |
Zacatecas | 1297 | 9.23% |
Biến StateorProvince
có 10 tỉnh/bang. Trong đó: WA có số
lượng khách hàng lớn nhất, chiếm 32.48%. Một số bang khác chỉ đóng góp
rất nhỏ vào tổng giao dịch. Kết quả này gợi ý về sự phân bổ khách hàng
chưa đều giữa các khu vực, và doanh nghiệp có thể ưu tiên các tỉnh có
lượng giao dịch lớn.
stateplotdf <- as.data.frame(state)
colnames(stateplotdf) <- c("StateorProvince", "Frequency")
ggplot(stateplotdf, aes(x = reorder(StateorProvince, -Frequency), y = Frequency)) +
geom_bar(stat = "identity", fill = "darkgreen") +
labs(
title = "Phân bố khách hàng theo tỉnh/bang",
x = "Tỉnh/Bang",
y = "Số lượng khách hàng"
) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Biểu đồ cột cho thấy sự nổi bật của một vài tỉnh/ bang, với các cột cao hơn hẳn như WA, CA, OR. Điều này thể hiện tiềm năng thị trường tập trung tại một số khu vực nhất định, có thể do quy mô dân số, sức mua, hoặc phân phối của các chi nhánh siêu thị.
# Bảng tần số
country <- table(data$Country)
# Bảng tần suất
tylecountry <- round(prop.table(country) * 100, 2)
# Tạo bảng trình bày rõ ràng
countrytable <- data.frame(
Country = names(country),
Frequency = as.vector(country),
Percentage = paste0(tylecountry, "%")
)
# Hiển thị bảng
library(knitr)
kable(countrytable, caption = "Bảng tần suất và tỷ lệ phần trăm theo quốc gia")
Country | Frequency | Percentage |
---|---|---|
Canada | 809 | 5.75% |
Mexico | 3688 | 26.23% |
USA | 9562 | 68.01% |
Biến Country
gồm 3 quốc gia. Trong đó, USA có tỷ lệ cao
nhất là 68.01% với 9562 lượt giao dịch. Các quốc gia còn lại chiếm tỷ lệ
nhỏ hơn, Mexico có tỷ lệ 26.23% với lượt giao dịch là 3688, thấp nhất là
Canada. Điều này cho thấy phần lớn dữ liệu đến từ USA, đây có thể là thị
trường chính của chuỗi siêu thị trong dữ liệu.
library(ggplot2)
library(dplyr)
# Chuẩn bị dữ liệu để vẽ pie chart
countryplot <- countrytable %>%
mutate(
Label = paste0(Country, ": ", Percentage, "") # Gắn nhãn
)
# Vẽ biểu đồ tròn bằng ggplot2
ggplot(countryplot, aes(x = "", y = Frequency, fill = Country)) +
geom_bar(stat = "identity", width = 1, color = "white") +
coord_polar(theta = "y") + # Chuyển bar chart thành pie chart
theme_void() + # Xoá trục, nền để tạo pie chart
labs(title = "Biểu đồ tròn: Tỷ lệ quốc gia") +
geom_text(aes(label = Label), position = position_stack(vjust = 0.5)) +
scale_fill_manual(values = c("orange", "lightgreen","pink")) + # Tùy chọn màu
theme(legend.title = element_blank())
Biểu đồ tròn thể hiện rõ sự áp đảo của USA, chiếm hơn nửa biểu đồ. Tiếp đến là Mexico tương đương một phần tư biểu đồ, còn lại Canada có tỷ lệ chiếm thấp nhất trong ba quốc gia.
pf <- table(data$ProductFamily)
tylepf <- round(prop.table(pf) * 100, 2)
pftable <- data.frame(
ProductFamily = names(pf),
Frequency = as.vector(pf),
Percentage = paste0(tylepf, "%")
)
kable(pftable, caption = "Tần suất và tỷ lệ phần trăm theo nhóm sản phẩm")
ProductFamily | Frequency | Percentage |
---|---|---|
Drink | 1250 | 8.89% |
Food | 10153 | 72.22% |
Non-Consumable | 2656 | 18.89% |
ProductFamily
gồm 3 nhóm sản phẩm chính. Nhóm Food có số
lượng giao dịch lớn nhất 72.22%, cho thấy đây là nhóm hàng được ưa
chuộng nhất.Hai nhóm khác là Non-Cosmumable và Drink có tỷ lệ thấp
hơn.
pfplotdf <- as.data.frame(pf)
colnames(pfplotdf) <- c("ProductFamily", "Frequency")
ggplot(pfplotdf, aes(x = reorder(ProductFamily, -Frequency), y = Frequency)) +
geom_bar(stat = "identity", fill = "purple") +
labs(
title = "Phân bố khách hàng theo nhóm sản phẩm",
x = "Nhóm sản phẩm",
y = "Số lượng giao dịch"
) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 30, hjust = 1))
Biểu đồ cột giúp trực quan hóa rõ mức độ tiêu thụ từng nhóm sản phẩm. Cột tương ứng với Food cao vượt trội, nhấn mạnh tầm quan trọng của nhóm sản phẩm này trong kinh doanh hiện tại.
pd <- table(data$ProductDepartment)
tylepd <- round(prop.table(pd) * 100, 2)
pdtable <- data.frame(
ProductDepartment = names(pd),
Frequency = as.vector(pd),
Percentage = paste0(tylepd, "%")
)
kable(pdtable, caption = "Tần suất và tỷ lệ phần trăm theo bộ phận sản phẩm")
ProductDepartment | Frequency | Percentage |
---|---|---|
Alcoholic Beverages | 356 | 2.53% |
Baked Goods | 425 | 3.02% |
Baking Goods | 1072 | 7.63% |
Beverages | 680 | 4.84% |
Breakfast Foods | 188 | 1.34% |
Canned Foods | 977 | 6.95% |
Canned Products | 109 | 0.78% |
Carousel | 59 | 0.42% |
Checkout | 82 | 0.58% |
Dairy | 903 | 6.42% |
Deli | 699 | 4.97% |
Eggs | 198 | 1.41% |
Frozen Foods | 1382 | 9.83% |
Health and Hygiene | 893 | 6.35% |
Household | 1420 | 10.1% |
Meat | 89 | 0.63% |
Periodicals | 202 | 1.44% |
Produce | 1994 | 14.18% |
Seafood | 102 | 0.73% |
Snack Foods | 1600 | 11.38% |
Snacks | 352 | 2.5% |
Starchy Foods | 277 | 1.97% |
Biến ProductDepartment
đại diện cho các bộ phận hàng
hóa, có 22 hạng mục. Trong đó, bộ phận Produce chiếm tỷ trọng lớn 14.18%
phản ánh nhu cầu tiêu dùng cao trong danh mục này.
pdplotdf <- as.data.frame(pd)
colnames(pdplotdf) <- c("ProductDepartment", "Frequency")
ggplot(pdplotdf, aes(x = reorder(ProductDepartment, -Frequency), y = Frequency)) +
geom_bar(stat = "identity", fill = "lightblue") +
labs(
title = "Phân bố khách hàng theo bộ phận sản phẩm",
x = "bộ phận sản phẩm",
y = "Số lượng giao dịch"
) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 30, hjust = 1))
Biểu đồ cho thấy sự phân bố không đều giữa các bộ phận sản phẩm. Bộ phận Produce có cột cao hơn hẳn, là nơi tập trung chính của hoạt động mua bán, trong khi các bộ phận khác có lượng giao dịch thấp hơn đáng kể.
pc <- table(data$ProductCategory)
tylepc <- round(prop.table(pc) * 100, 2)
pctable <- data.frame(
ProductCategory = names(pc),
Frequency = as.vector(pc),
Percentage = paste0(tylepc, "%")
)
kable(pctable, caption = "Tần suất và tỷ lệ phần trăm theo danh mục sản phẩm")
ProductCategory | Frequency | Percentage |
---|---|---|
Baking Goods | 484 | 3.44% |
Bathroom Products | 365 | 2.6% |
Beer and Wine | 356 | 2.53% |
Bread | 425 | 3.02% |
Breakfast Foods | 417 | 2.97% |
Candles | 45 | 0.32% |
Candy | 352 | 2.5% |
Canned Anchovies | 44 | 0.31% |
Canned Clams | 53 | 0.38% |
Canned Oysters | 35 | 0.25% |
Canned Sardines | 40 | 0.28% |
Canned Shrimp | 38 | 0.27% |
Canned Soup | 404 | 2.87% |
Canned Tuna | 87 | 0.62% |
Carbonated Beverages | 154 | 1.1% |
Cleaning Supplies | 189 | 1.34% |
Cold Remedies | 93 | 0.66% |
Dairy | 903 | 6.42% |
Decongestants | 85 | 0.6% |
Drinks | 135 | 0.96% |
Eggs | 198 | 1.41% |
Electrical | 355 | 2.53% |
Frozen Desserts | 323 | 2.3% |
Frozen Entrees | 118 | 0.84% |
Fruit | 765 | 5.44% |
Hardware | 129 | 0.92% |
Hot Beverages | 226 | 1.61% |
Hygiene | 197 | 1.4% |
Jams and Jellies | 588 | 4.18% |
Kitchen Products | 217 | 1.54% |
Magazines | 202 | 1.44% |
Meat | 761 | 5.41% |
Miscellaneous | 42 | 0.3% |
Packaged Vegetables | 48 | 0.34% |
Pain Relievers | 192 | 1.37% |
Paper Products | 345 | 2.45% |
Pizza | 194 | 1.38% |
Plastic Products | 141 | 1% |
Pure Juice Beverages | 165 | 1.17% |
Seafood | 102 | 0.73% |
Side Dishes | 153 | 1.09% |
Snack Foods | 1600 | 11.38% |
Specialty | 289 | 2.06% |
Starchy Foods | 277 | 1.97% |
Vegetables | 1728 | 12.29% |
Biến ProductCategory
có 45 hạng mục. Trong đó,
Vegetables có số lượng nhiều nhất là 1728 và chiếm 12.29%. Ngược lại,
Canned Oysters có số lượng thấp nhất là 35 với tỷ lệ là 0.25%
library(ggplot2)
# Chuyển bảng tần suất sang data frame
pcdf <- as.data.frame(pc)
colnames(pcdf) <- c("ProductCategory", "Frequency")
# Sắp xếp và lấy top 10
top10pc <- pcdf |>
dplyr::arrange(desc(Frequency)) |>
dplyr::slice(1:10)
# Vẽ biểu đồ cột
ggplot(top10pc, aes(x = reorder(ProductCategory, Frequency), y = Frequency)) +
geom_bar(stat = "identity", fill = "darkorange") +
coord_flip() +
labs(
title = "Top 10 Danh mục sản phẩm phổ biến nhất",
x = "Danh mục sản phẩm",
y = "Tần suất giao dịch"
) +
theme_minimal()
Biểu đồ thể hiện Top 10 danh mục sản phẩm có lượt mua cao nhất trong toàn bộ hệ thống. Danh mục Vegetables có tần suất cao nhất 1728, cho thấy đây là nhóm sản phẩm được ưa chuộng và tiêu thụ mạnh. Những danh mục phía sau như Snack Foods, Dairy cũng có lượng mua đáng kể và có thể là trọng tâm cho các chiến dịch tiếp thị.
Ta có tỷ lệ khách hàng nữ trong mẫu là khoảng 51%.
# Ước lượng khoảng tin cậy 95%
genderci <- prop.test(x = xgender, n = ngender, conf.level = 0.95)
genderci
##
## 1-sample proportions test with continuity correction
##
## data: xgender out of ngender, 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
Kết quả: Với khoảng tin cậy 95% cho tỷ lệ nữ trong tổng thể từ 50.17% đến 51.83%.
Với độ tin cậy 95%, chúng tôi ước lượng rằng tỷ lệ khách hàng nữ trong toàn bộ tập khách hàng nằm trong khoảng 50.17% đến 51.83%. Điều này có nghĩa là nếu thực hiện việc lấy mẫu nhiều lần tương tự như hiện tại, 95% số mẫu sẽ cho kết quả nằm trong khoảng này.
Thực hiện kiểm định giả thuyết về tỷ lệ của hạng mục Nữ:
Giả thuyết Không (\({H}_0\)): Tỷ lệ Nữ = 0.5
Giả thuyết Thay thế (\({H}_1\)): Tỷ lệ Nữ ≠ 0.5
Mức ý nghĩa α=0.05.
# Kiểm định giả thuyết H0: tỷ lệ nữ = 0.5
genderhtest <- prop.test(x = xgender, n = ngender, p = 0.5, alternative = "two.sided")
genderhtest
##
## 1-sample proportions test with continuity correction
##
## data: xgender out of ngender, 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
Kết quả:
p-value = 0.0182
Giá trị thống kê Chi-bình phương: 5.576499
Bậc tự do (df): 1
Do p-value < 0.05, ta bác bỏ giả thuyết \({H}_0\). Như vậy, có bằng chứng thống kê ở mức ý nghĩa 5% cho thấy tỷ lệ nữ trong tổng thể khác 50%. Đây là một phát hiện có thể quan trọng trong việc xác định đối tượng khách hàng chính của doanh nghiệp.
xhome <- sum(data$Homeowner == "Y")
nhome <- nrow(data)
homeci <- prop.test(x = xhome, n = nhome, conf.level = 0.95)
homeci
##
## 1-sample proportions test with continuity correction
##
## data: xhome out of nhome, 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
Tỷ lệ khách hàng có nhà trong mẫu là khoảng 60.06%.
Với khoảng tin cậy 95% cho tỷ lệ sở hữu nhà trong tổng thể từ 59.25% đến 60.87%.
Chúng tôi ước lượng rằng tỷ lệ khách hàng có sở hữu nhà nằm trong khoảng 59.25% đến 60.87%. Điều này cho thấy khả năng tài chính của phần lớn khách hàng có thể ở mức tương đối ổn định.
Thực hiện kiểm định giả thuyết về tỷ lệ của hạng mục có nhà:
Giả thuyết Không (\({H}_0\)): Tỷ lệ có nhà = 0.6
Giả thuyết Thay thế (\({H}_1\)): Tỷ lệ có nhà ≠ 0.6
Mức ý nghĩa α=0.05.
##
## 1-sample proportions test with continuity correction
##
## data: xhome out of nhome, 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
Với p-value = 0.8891, ta không bác bỏ giả thuyết \({H}_0\). Như vậy, có không đủ bằng chứng thống kê cho thấy tỷ lệ khách hàng sở hữu nhà không khác đáng kể 60%. Nếu khác, điều này cần được doanh nghiệp lưu ý khi lựa chọn sản phẩm hoặc chính sách tài chính phù hợp.
xfood <- sum(data$ProductFamily == "Food")
nfood <- nrow(data)
foodci <- prop.test(x = xfood, n = nfood, conf.level = 0.95)
foodci
##
## 1-sample proportions test with continuity correction
##
## data: xfood out of nfood, 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
Kết quả:
Tỷ lệ Food quan sát được: 72.22%.
Khoảng tin cậy 95%: (71.47%; 72.95%)
Với 95% độ tin cậy, chúng tôi ước lượng rằng tỷ lệ giao dịch thuộc nhóm sản phẩm Food nằm trong khoảng 71.47% đến 72.95%. Điều này cho thấy Food là nhóm hàng chủ lực trong hệ thống siêu thị hiện tại.
Thực hiện kiểm định giả thuyết về tỷ lệ của hạng mục có nhà:
Giả thuyết Không (\({H}_0\)): Tỷ lệ Food >= 0.7
Giả thuyết Thay thế (\({H}_1\)): Tỷ lệ Food < 0.7
Mức ý nghĩa α=0.05.
##
## 1-sample proportions test with continuity correction
##
## data: xfood out of nfood, 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
Kết quả: p-value = 1
Với giả thuyết \({H}_0\): tỷ lệ Food ≥ 70%, và p-value = 1, ta không bác bỏ giả thuyết \({H}_0\). Tức là có không đủ bằng chứng để kết luận rằng tỷ lệ sản phẩm Food thấp hơn 70%. Đây là thông tin quan trọng giúp điều chỉnh kỳ vọng doanh thu theo nhóm sản phẩm.
Bảng ngẫu nhiên (contingency table) là một bảng đếm tần số kết hợp giữa hai biến phân loại. Các ô trong bảng có thể được sinh ra từ các phân phối xác suất khác nhau, tùy thuộc vào bối cảnh thu thập dữ liệu.
Phân phối Poisson (Poisson Distribution)
Phân phối Poisson mô tả số lượng sự kiện xảy ra trong một khoảng thời gian hoặc không gian cố định, với giả định các sự kiện xảy ra độc lập và với tỉ lệ trung bình cố định.
Định nghĩa: Biến ngẫu nhiên 𝑋 có phân phối Poisson với tham số 𝜆> 0 nếu xác suất 𝑋 nhận giá trị 𝑘 là
\[P(X = k) = \frac{e^{-\lambda} \lambda^{k}}{k!}, \quad k = 0, 1, 2, \dots\]
Ý nghĩa tham số: 𝜆 là số sự kiện trung bình xảy ra trong khoảng quan sát.
Ứng dụng: Thường dùng để mô tả số lượng sự kiện hiếm gặp như số cuộc gọi điện thoại trong 1 giờ, số khách hàng đến cửa hàng trong 1 ngày, hay số lỗi phát sinh trên một sản phẩm.
Phân phối Đa thức (Multinomial Distribution)
Phân phối Đa thức là sự mở rộng của phân phối Nhị thức (Binomial), mô tả số lần các kết quả khác nhau xảy ra trong một chuỗi các phép thử độc lập, mỗi phép thử có nhiều hơn hai kết quả có thể.
Định nghĩa: Cho một phép thử với 𝑛 lần lặp lại độc lập, mỗi lần có 𝑘 kết quả có thể xảy ra với xác suất \(p_1, p_2, \dots, p_k\) (thỏa mãn \(\sum_{i=1}^k p_i = 1\)), thì vector biến ngẫu nhiên \((X_1, X_2, \dots, X_k)\)
là số lần kết quả thứ𝑖xảy ra, có phân phối Đa thức với xác suất:
\[P(X_1 = x_1, \dots, X_k = x_k) = \frac{n!}{x_1! x_2!\cdots x_k!} p_1^{x_1} p_2^{x_2} \cdots p_k^{x_k}\]
với điều kiện \(\sum_{i=1}^k x_i = n\).
Ứng dụng: Mô hình hóa bảng tần số trong các bảng phân loại có nhiều nhóm như kết quả khảo sát, phân bổ khách hàng theo loại sản phẩm, hoặc số lượng sản phẩm bán ra trong các danh mục khác nhau.
Tóm lại, cấu trúc xác suất của bảng ngẫu nhiên thường được xây dựng dựa trên phân phối Poisson khi quan tâm đến số lượng sự kiện xảy ra trên các ô bảng (đếm số lượng) hoặc phân phối Đa thức khi quan tâm đến phân phối tỉ lệ của các nhóm trong tổng số các phép thử.
Hiệu hai tỷ lệ đo lường sự khác biệt về tỷ lệ xảy ra một sự kiện giữa hai nhóm phân loại (ví dụ: Nam và Nữ, Có và Không khuyến mãi, v.v.)
Công thức:
\[D = p_1 - p_2\]
Trong đó:
Khoảng tin cậy cho hiệu tỷ lệ
Công thức ước lượng khoảng tin cậy 95% cho \(D\):
\[CI_{95\%} = D \pm Z_{1 - \alpha/2} \cdot SE\]
Trong đó:
\[SE = \sqrt{ \frac{p_1 (1 - p_1)}{n_1} + \frac{p_2 (1 - p_2)}{n_2} }\]
Diễn giải
Kiểm định Chi-bình phương (Chi-square test of independence) là một phương pháp thống kê dùng để xác định xem hai biến định tính (phân loại) có quan hệ với nhau hay không hay nói cách khác, chúng có độc lập với nhau trong tổng thể hay không.
Giả thuyết kiểm định
Công thức giá trị kiểm định
Phép kiểm định sử dụng phân phối Chi-bình phương (χ²) với công thức:
\[\chi^2 = \sum_{i,j}\frac{(n_{ij}-\hat{\mu}_{ij})^2}{\hat{\mu}_{ij}}\]
Trong đó:
\[\hat{\mu}_{ij} = \frac{n_{i+} \cdot n_{+j}}{n}\]
Bậc tự do (Degrees of Freedom)
\[df = (r - 1)(c - 1)\]
Trong đó:
Diễn giải kết quả
Sau khi chạy kiểm định trong R bằng hàm chisq.test()
,
kết quả trả về bao gồm:
Ý nghĩa
Lưu ý
Relative Risk (RR) hay còn gọi là Nguy cơ tương đối là một thước đo dùng để so sánh xác suất xảy ra sự kiện giữa nhóm có phơi nhiễm (exposed) và nhóm không phơi nhiễm (non-exposed). Thường được sử dụng trong nghiên cứu đoàn hệ (cohort studies).
Mục đích: Đo lường mức độ ảnh hưởng của một yếu tố (ví dụ: thói quen tiêu dùng, giới tính, tình trạng khuyến mãi…) đến khả năng xảy ra một sự kiện nào đó (ví dụ: mua hàng, bị bệnh, v.v.).
Công thức tính
Giả sử ta có bảng 2×2:
Sự kiện xảy ra (Yes) | Không xảy ra (No) | Tổng | |
---|---|---|---|
Phơi nhiễm | a | b | a + b |
Không phơi nhiễm | c | d | c + d |
Công thức tính Relative Risk:
\[ RR = \frac{a / (a + b)}{c / (c + d)} \]
Trong đó:
Diễn giải kết quả
Để đánh giá độ tin cậy của chỉ số Relative Risk (RR), ta cần tính khoảng tin cậy (Confidence Interval – CI), thường là khoảng tin cậy 95%.
Công thức khoảng tin cậy 95% cho RR
Khoảng tin cậy của RR dựa trên logarit tự nhiên của RR:
\[\ln(RR) \pm Z_{1 - \alpha/2} \cdot SE(\ln(RR))\]
Với:
\[SE(\ln(RR)) = \sqrt{ \frac{1}{a} - \frac{1}{a+b} + \frac{1}{c} - \frac{1}{c+d} }\]
Sau đó lấy mũ (exponential) để có khoảng tin cậy cho RR:
\[ CI_{95\%} = \left( e^{\ln(RR) - 1.96 \cdot SE},\; e^{\ln(RR) + 1.96 \cdot SE} \right) \]
Diễn giải kết quả
Nếu khoảng tin cậy không chứa 1, kết quả có ý nghĩa thống kê:
Nếu CI > 1 → Nhóm phơi nhiễm có nguy cơ cao hơn
Nếu CI < 1 → Nhóm phơi nhiễm có nguy cơ thấp hơn (tác dụng bảo vệ)
Nếu CI chứa 1 → Không thể kết luận có mối liên hệ rõ ràng về nguy cơ
Tính khoảng tin cậy cho RR trong R
epitools
install.packages(“epitools”) library(epitools) Tạo bảng 2x2: hàng = phơi nhiễm, cột = sự kiện: data <- matrix(c(60, 40, 30, 70), nrow = 2, byrow = TRUE) dimnames(data) <- list(Exposure = c(“Yes”, “No”), Outcome = c(“Buy”, “NotBuy”)) Tính RR và khoảng tin cậy: riskratio(data, rev = “columns”)
epiR
install.packages(“epiR”) library(epiR) data <- matrix(c(60, 40, 30, 70), nrow = 2, byrow = TRUE) epi.2by2(dat = data, method = “cohort.count”, conf.level = 0.95)
Odds là tỷ lệ giữa xác suất xảy ra sự kiện (p) và không xảy ra sự kiện (1 - p):
\[\text{Odds} = \frac{p}{1 - p}\]
Odds Ratio (OR) là một thước đo được sử dụng phổ biến trong nghiên cứu y học, xã hội và hành vi tiêu dùng, giúp so sánh xác suất tương đối (odds) xảy ra sự kiện giữa hai nhóm. Odds Ratio (OR) là tỷ lệ giữa odds của hai nhóm:
\[OR = \frac{ \text{Odds trong nhóm 1} }{ \text{Odds trong nhóm 2} }\]
Cách tính OR từ bảng 2x2
Sự kiện xảy ra (Yes) | Không xảy ra (No) | Tổng | |
---|---|---|---|
Nhóm 1 (phơi nhiễm) | a | b | a + b |
Nhóm 2 (không phơi nhiễm) | c | d | c + d |
Công thức:
\[OR = \frac{a / b}{c / d} = \frac{a \cdot d}{b \cdot c}\]
Diễn giải Odds Ratio
Ví dụ:
Khi nào OR ≈ RR
Tại sao OR quan trọng?
Khoảng tin cậy cho Odds Ratio
Tính khoảng tin cậy 95% cho OR dựa trên log(OR):
\(SE(\ln(OR)) =\sqrt{ \frac{1}{a} + \frac{1}{b} + \frac{1}{c} + \frac{1}{d} }\)
\(CI_{95\%} = (e^{\ln(OR) - 1.96 \cdot SE}, \; e^{\ln(OR) + 1.96 \cdot SE})\)
DescTools::OddsRatio()
install.packages(“DescTools”) library(DescTools) a <- matrix(c(60, 40, 30, 70), nrow = 2, byrow = TRUE) OddsRatio(a, conf.level = 0.95)
epitools
install.packages(“epitools”) library(epitools) tb <- matrix(c(60, 40, 30, 70), nrow = 2, byrow = TRUE) oddsratio(tb, rev = “columns”)
Diễn giải kết quả - Nếu CI không chứa 1 → mối liên hệ có ý nghĩa thống kê - Nếu CI chứa 1 → chưa đủ bằng chứng để kết luận có mối liên hệ - Nếu OR > 1 và CI không chứa 1 → nhóm 1 có odds xảy ra sự kiện cao hơn đáng kể
Bảng tần số
# Bảng tần số chéo
tabgenderpf <- table(data$Gender, data$ProductFamily)
# Tỷ lệ theo hàng (giới tính)
propgenderpf <- prop.table(tabgenderpf, margin = 1)
# Hiển thị bảng
library(knitr)
kable(tabgenderpf, caption = "Bảng tần số chéo giữa Gender và ProductFamily")
Drink | Food | Non-Consumable | |
---|---|---|---|
F | 669 | 5149 | 1352 |
M | 581 | 5004 | 1304 |
Số lượng khách hàng nữ mua hàng nhiều hơn nam ở tất cả các nhóm sản phẩm. Cả nam và nữ đều mua nhiều nhất nhóm Food, kế đến là Non-Consumable, sau cùng là Drink. Dù tổng số khách hàng nữ và nam gần tương đương, nữ chiếm ưu thế nhẹ về tổng số giao dịch, điều này có thể phản ánh xu hướng mua hàng thường xuyên hơn ở nữ.
Trực quan hoá bằng biểu đồ
library(ggplot2)
ggplot(data, aes(x = Gender, fill = ProductFamily)) +
geom_bar(position = "fill") +
scale_y_continuous(labels = scales::percent_format()) +
labs(title = "Tỷ lệ nhóm sản phẩm theo giới tính", y = "Tỷ lệ", x = "Giới tính") +
theme_minimal()
Nhận xét từ biểu đồ cột chồng:
Nhìn theo tỷ lệ, cơ cấu tiêu dùng giữa nam và nữ tương đối giống nhau. Tuy nhiên, tỷ lệ Food vẫn cao nhất ở cả hai giới, cho thấy đây là nhóm sản phẩm chủ đạo cho mọi đối tượng. Có vẻ như nữ chiếm tỷ lệ cao hơn một chút ở nhóm Drink, điều này có thể phản ánh nhu cầu khác biệt trong lựa chọn đồ uống.
Kiểm định Chi-bình phương
##
## Pearson's Chi-squared test
##
## data: tabgenderpf
## X-squared = 3.5185, df = 2, p-value = 0.1722
Giả thuyết:
\({H}_0\): Không có mối liên hệ giữa giới tính và nhóm sản phẩm
\({H}_1\): Có mối liên hệ giữa giới tính và nhóm sản phẩm
Kết quả kiểm định:
Giá trị Chi-squared: 3.52
Bậc tự do (df): 2
p-value = 0.1722
Kết luận:
Vì p-value > 0.05, ta không bác bỏ giả thuyết \({H}_0\). Như vậy, không có bằng chứng về mối liên hệ giữa giới tính và nhóm sản phẩm mà khách hàng lựa chọn. Nói cách khác, sự khác biệt nhỏ trong lựa chọn sản phẩm giữa nam và nữ trong bảng tần suất có thể là do ngẫu nhiên, và giới tính không ảnh hưởng đáng kể đến nhóm sản phẩm mà khách hàng lựa chọn, ít nhất là trong dữ liệu hiện tại.
Relative Risk
library(epitools)
# Bảng cho dòng sản phẩm "Drink"
# Lấy tổng số người nữ và nam
total_female <- sum(tabgenderpf["F", ])
total_male <- sum(tabgenderpf["M", ])
drink_table <- matrix(c(tabgenderpf["F", "Drink"], total_female - tabgenderpf["F", "Drink"], tabgenderpf["M", "Drink"], total_male - tabgenderpf["M", "Drink"]), nrow = 2, byrow = TRUE)
rownames(drink_table) <- c("Nữ", "Nam")
colnames(drink_table) <- c("Mua Drink", "Không Mua Drink")
message("Bảng cho Drink")
riskratio(drink_table)
## $data
## Mua Drink Không Mua Drink Total
## Nữ 669 6501 7170
## Nam 581 6308 6889
## Total 1250 12809 14059
##
## $measure
## NA
## risk ratio with 95% C.I. estimate lower upper
## Nữ 1.000000 NA NA
## Nam 1.009891 0.9995229 1.020367
##
## $p.value
## NA
## two-sided midp.exact fisher.exact chi.square
## Nữ NA NA NA
## Nam 0.06182901 0.06194737 0.06180684
##
## $correction
## [1] FALSE
##
## attr(,"method")
## [1] "Unconditional MLE & normal approximation (Wald) CI"
# Bảng cho dòng sản phẩm "Food"
food_table <- matrix(c(tabgenderpf["F", "Food"], total_female - tabgenderpf["F", "Food"], tabgenderpf["M", "Food"], total_male - tabgenderpf["M", "Food"]), nrow = 2, byrow = TRUE)
rownames(food_table) <- c("Nữ", "Nam")
colnames(food_table) <- c("Mua Food", "Không Mua Food")
message("Bảng cho Food")
riskratio(food_table)
## $data
## Mua Food Không Mua Food Total
## Nữ 5149 2021 7170
## Nam 5004 1885 6889
## Total 10153 3906 14059
##
## $measure
## NA
## risk ratio with 95% C.I. estimate lower upper
## Nữ 1.0000000 NA NA
## Nam 0.9707514 0.9203271 1.023938
##
## $p.value
## NA
## two-sided midp.exact fisher.exact chi.square
## Nữ NA NA NA
## Nam 0.2753749 0.2830759 0.2752955
##
## $correction
## [1] FALSE
##
## attr(,"method")
## [1] "Unconditional MLE & normal approximation (Wald) CI"
# Bảng cho dòng sản phẩm "Non-Consumable"
nonconsumable_table <- matrix(c(tabgenderpf["F", "Non-Consumable"], total_female - tabgenderpf["F", "Non-Consumable"], tabgenderpf["M", "Non-Consumable"], total_male - tabgenderpf["M", "Non-Consumable"]), nrow = 2, byrow = TRUE)
rownames(nonconsumable_table) <- c("Nữ", "Nam")
colnames(nonconsumable_table) <- c("Mua Non-Consumable", "Không Mua Non-Consumable")
message("Bảng cho Non-Consumable")
riskratio(nonconsumable_table)
## $data
## Mua Non-Consumable Không Mua Non-Consumable Total
## Nữ 1352 5818 7170
## Nam 1304 5585 6889
## Total 2656 11403 14059
##
## $measure
## NA
## risk ratio with 95% C.I. estimate lower upper
## Nữ 1.000000 NA NA
## Nam 0.999108 0.9832895 1.015181
##
## $p.value
## NA
## two-sided midp.exact fisher.exact chi.square
## Nữ NA NA NA
## Nam 0.9126775 0.9142133 0.9127255
##
## $correction
## [1] FALSE
##
## attr(,"method")
## [1] "Unconditional MLE & normal approximation (Wald) CI"
Nhận xét về Relative Risk cho từng dòng sản phẩm
Sẽ phân tích kết quả của từng dòng sản phẩm (Drink, Food, Non-Consumable), với Nữ (Female) là nhóm tham chiếu (RR của Nữ so với Nữ là 1), và chúng ta sẽ xem xét RR của Nam (Male) so với Nữ.
Kết quả chính:
RR (Nam so với Nữ) = 1.009891
Khoảng tin cậy 95%: (0.9995229, 1.020367)
Giá trị p (Chi-squared): 0.06180684
Nhận xét:
Tỷ số nguy cơ là 1.01. Điều này có nghĩa là xác suất nam giới mua sản phẩm “Drink” cao hơn khoảng 1% so với nữ giới.
Khoảng tin cậy 95% (0.9995; 1.0204) gần chứa giá trị 1. Giá trị 1 nằm ngay sát giới hạn dưới của khoảng tin cậy.
Giá trị p (0.0618) lớn hơn 0.05 một chút.
Kết luận: Mặc dù tỷ số nguy cơ cho thấy nam giới có xu hướng mua “Drink” cao hơn một chút. Điều này có nghĩa là chúng ta không có đủ bằng chứng mạnh mẽ để kết luận rằng có sự khác biệt thực sự về nguy cơ mua đồ uống giữa nam và nữ trong tổng thể.
Kết quả chính:
RR (Nam so với Nữ) = 0.9707514
Khoảng tin cậy 95%: (0.9203271, 1.023938)
Giá trị p (Chi-squared): 0.2752955
Nhận xét:
Tỷ số nguy cơ là 0.97. Điều này cho thấy nguy cơ nam giới mua sản phẩm “Food” thấp hơn khoảng 3% so với nữ giới.
Khoảng tin cậy 95% (0.9203; 1.0239) chứa giá trị 1.
Giá trị p (0.2753) lớn hơn 0.05.
Kết luận: Không có sự khác biệt có ý nghĩa thống kê về nguy cơ mua sản phẩm “Food” giữa nam và nữ. Nguy cơ thấp hơn 3% ở nam giới có thể chỉ là do ngẫu nhiên trong mẫu dữ liệu này.
Kết quả chính:
RR (Nam so với Nữ) = 0.999108
Khoảng tin cậy 95%: (0.9832895, 1.015181)
Giá trị p (Chi-squared): 0.9127255
Nhận xét:
Tỷ số nguy cơ là 0.9991. Điều này gần như bằng 1, cho thấy nguy cơ nam giới mua sản phẩm “Non-Consumable” gần như tương đương với nữ giới.
Khoảng tin cậy 95% (0.9833; 1.0152) chứa giá trị 1.
Giá trị p (0.9127) rất lớn, cho thấy không có bằng chứng nào về sự khác biệt.
Kết luận: Không có sự khác biệt có ý nghĩa thống kê về nguy cơ mua sản phẩm “Non-Consumable” giữa nam và nữ. Nguy cơ là tương đương giữa hai giới tính.
Tóm tắt: Dựa trên phân tích Tỷ số Nguy cơ cho từng dòng sản phẩm, có thể kết luận rằng trong tập dữ liệu này, không có sự khác biệt đáng kể về mặt thống kê về nguy cơ mua các loại sản phẩm (Drink, Food, Non-Consumable) giữa nam và nữ. Mặc dù có những biến động nhỏ trong các ước tính RR như nam mua Drink nhiều hơn một chút, mua Food ít hơn một chút so với nữ, nhưng những biến động này không đủ lớn để được coi là có ý nghĩa thống kê.
Odds và OddsRatio
## $data
## Mua Drink Không Mua Drink Total
## Nữ 669 6501 7170
## Nam 581 6308 6889
## Total 1250 12809 14059
##
## $measure
## NA
## odds ratio with 95% C.I. estimate lower upper
## Nữ 1.00000 NA NA
## Nam 1.11724 0.9945207 1.255373
##
## $p.value
## NA
## two-sided midp.exact fisher.exact chi.square
## Nữ NA NA NA
## Nam 0.06182901 0.06194737 0.06180684
##
## $correction
## [1] FALSE
##
## attr(,"method")
## [1] "median-unbiased estimate & mid-p exact CI"
## $data
## Mua Food Không Mua Food Total
## Nữ 5149 2021 7170
## Nam 5004 1885 6889
## Total 10153 3906 14059
##
## $measure
## NA
## odds ratio with 95% C.I. estimate lower upper
## Nữ 1.0000000 NA NA
## Nam 0.9597381 0.8914287 1.033266
##
## $p.value
## NA
## two-sided midp.exact fisher.exact chi.square
## Nữ NA NA NA
## Nam 0.2753749 0.2830759 0.2752955
##
## $correction
## [1] FALSE
##
## attr(,"method")
## [1] "median-unbiased estimate & mid-p exact CI"
## $data
## Mua Non-Consumable Không Mua Non-Consumable Total
## Nữ 1352 5818 7170
## Nam 1304 5585 6889
## Total 2656 11403 14059
##
## $measure
## NA
## odds ratio with 95% C.I. estimate lower upper
## Nữ 1.000000 NA NA
## Nam 0.995285 0.9146866 1.083027
##
## $p.value
## NA
## two-sided midp.exact fisher.exact chi.square
## Nữ NA NA NA
## Nam 0.9126775 0.9142133 0.9127255
##
## $correction
## [1] FALSE
##
## attr(,"method")
## [1] "median-unbiased estimate & mid-p exact CI"
Nhận xét về Odds và Odds Ratio cho từng dòng sản phẩm
Bảng dữ liệu:
Nữ: 669 mua Drink, 6501 không mua Drink (Tổng: 7170)
Nam: 581 mua Drink, 6308 không mua Drink (Tổng: 6889)
Kết quả chính:
Odds của Nữ mua Drink: \(669 / 6501 \approx 0.1029\)
Odds của Nam mua Drink: \(581 / 6308 \approx 0.0921\)
OR (Nam so với Nữ) = 1.11724
Khoảng tin cậy 95%: (0.9945207, 1.255373)
Giá trị p (Chi-squared): 0.06180684
Nhận xét:
Tỷ số chênh (OR) là 1.117. Điều này có nghĩa là tỷ lệ cược (odds) nam giới mua sản phẩm “Drink” cao hơn khoảng 11.7% so với nữ giới.
Khoảng tin cậy 95% (0.9945; 1.2554) gần chứa giá trị 1. Giống như trường hợp Relative Risk, giá trị 1 nằm ngay sát giới hạn dưới của khoảng tin cậy.
Giá trị p (0.0618) lớn hơn 0.05 một chút.
Kết luận: Mặc dù tỷ số chênh cho thấy tỷ lệ cược nam giới mua “Drink” có vẻ cao hơn nữ giới, sự khác biệt này không có ý nghĩa thống kê ở mức alpha 0.05. Không có đủ bằng chứng mạnh mẽ để kết luận rằng có sự khác biệt thực sự về tỷ lệ cược mua đồ uống giữa nam và nữ trong tổng thể.
Bảng dữ liệu:
Nữ: 5149 mua Food, 2021 không mua Food (Tổng: 7170)
Nam: 5004 mua Food, 1885 không mua Food (Tổng: 6889)
Kết quả chính:
Odds của Nữ mua Food: \(5149 / 2021 \approx 2.5478\)
Odds của Nam mua Food: \(5004 / 1885 \approx 2.6546\)
OR (Nam so với Nữ) = 0.9597381
Khoảng tin cậy 95%: (0.8914287, 1.033266)
Giá trị p (Chi-squared): 0.2752955
Nhận xét:
Tỷ số chênh (OR) là 0.960. Điều này cho thấy tỷ lệ cược nam giới mua sản phẩm “Food” thấp hơn khoảng 4% so với nữ giới.
Khoảng tin cậy 95% (0.8914 - 1.0333) chứa giá trị 1.
Giá trị p (0.2753) lớn hơn 0.05.
Kết luận: Không có sự khác biệt có ý nghĩa thống kê về tỷ lệ cược mua sản phẩm “Food” giữa nam và nữ. Sự khác biệt nhỏ trong mẫu dữ liệu này có thể là do ngẫu nhiên.
Bảng dữ liệu:
Nữ: 1352 mua Non-Consumable, 5818 không mua Non-Consumable (Tổng: 7170)
Nam: 1304 mua Non-Consumable, 5585 không mua Non-Consumable (Tổng: 6889)
Kết quả chính:
Odds của Nữ mua Non-Consumable: \(1352 / 5818 \approx 0.2324\)
Odds của Nam mua Non-Consumable: \(1304 / 5585 \approx 0.2335\)
OR (Nam so với Nữ) = 0.995285
Khoảng tin cậy 95%: (0.9146866, 1.083027)
Giá trị p (Chi-squared): 0.9127255
Nhận xét:
Tỷ số chênh (OR) là 0.995. Điều này gần như bằng 1, cho thấy tỷ lệ cược nam giới mua sản phẩm “Non-Consumable” gần như tương đương với nữ giới.
Khoảng tin cậy 95% (0.9147; 1.0830) chứa giá trị 1.
Giá trị p (0.9127) rất lớn, cho thấy không có bằng chứng nào về sự khác biệt.
Kết luận: Không có sự khác biệt có ý nghĩa thống kê về tỷ lệ cược mua sản phẩm “Non-Consumable” giữa nam và nữ. Tỷ lệ cược là tương đương giữa hai giới tính.
Tóm tắt chung về Odds Ratio: Tương tự như phân tích Relative Risk trước đó, các kết quả Odds Ratio cũng chỉ ra rằng không có sự khác biệt đáng kể về mặt thống kê về tỷ lệ cược mua các loại sản phẩm (Drink, Food, Non-Consumable) giữa nam và nữ trong tập dữ liệu này. Tất cả các khoảng tin cậy 95% đều chứa giá trị 1, và tất cả các giá trị p đều lớn hơn 0.05, củng cố kết luận rằng không có mối liên hệ có ý nghĩa thống kê giữa giới tính và việc mua các loại sản phẩm cụ thể này. Điều này ngụ ý rằng, dựa trên bộ dữ liệu này, việc một người là nam hay nữ không dự đoán đáng kể việc họ có xu hướng mua một trong ba loại sản phẩm này hơn hay không.
Bảng tần số
tabmaritalhome <- table(data$MaritalStatus, data$Homeowner)
propmaritalhome <- prop.table(tabmaritalhome, margin = 1)
kable(tabmaritalhome, caption = "Bảng tần suất chéo giữa MaritalStatus và Homeowner")
N | Y | |
---|---|---|
M | 1719 | 5147 |
S | 3896 | 3297 |
Người đã kết hôn (M) có xu hướng sở hữu nhà cao hơn đáng kể so với người độc thân: 5147/6866 ≈ 75% người đã kết hôn có nhà
Trong khi đó, người độc thân (S) chủ yếu không có nhà: 3896/7193 ≈ 54% người độc thân không có nhà
Có thể thấy rằng tình trạng hôn nhân có ảnh hưởng lớn đến khả năng sở hữu nhà, có thể do ổn định tài chính hoặc nhu cầu sinh hoạt.
Trực quan hoá bằng biểu đồ
ggplot(data, aes(x = MaritalStatus, fill = Homeowner)) +
geom_bar(position = "fill") +
scale_y_continuous(labels = scales::percent_format()) +
labs(title = "Tỷ lệ sở hữu nhà theo tình trạng hôn nhân", y = "Tỷ lệ", x = "Tình trạng hôn nhân") +
theme_minimal()
Biểu đồ tỷ lệ cho thấy nhóm đã kết hôn chiếm phần lớn ở phía “Có nhà”, còn nhóm độc thân chiếm phần lớn ở phía “Không có nhà”. Điều này củng cố trực quan quan sát từ bảng số liệu.
Kiểm định Chi-bình phương
##
## Pearson's Chi-squared test with Yates' continuity correction
##
## data: tabmaritalhome
## X-squared = 1241.2, df = 1, p-value < 2.2e-16
Giả thuyết:
\({H}_0\): Tình trạng hôn nhân và khả năng sở hữu nhà độc lập
\({H}_1\): Tình trạng hôn nhân có liên quan đến việc sở hữu nhà
Kết quả:
Giá trị Chi-squared: 1241.2
Bậc tự do (df): 1
p-value: < 2.2e-16
Kết luận:
Vì p-value < 0.05, ta bác bỏ giả thuyết \({H}_0\). Điều này chứng tỏ có mối liên hệ có ý nghĩa thống kê giữa tình trạng hôn nhân và việc sở hữu nhà. Nói cách khác, tình trạng hôn nhân ảnh hưởng đáng kể đến khả năng sở hữu nhà của khách hàng, và mối quan hệ này không phải do ngẫu nhiên. Doanh nghiệp có thể sử dụng thông tin này để phân khúc khách hàng và đưa ra các chương trình nhà ở hoặc tín dụng phù hợp.
Bảng tần số
library(dplyr)
# Lấy top 10 danh mục sản phẩm phổ biến nhất
top10pc <- data %>%
count(ProductCategory, sort = TRUE) %>%
slice_head(n = 10) %>%
pull(ProductCategory)
# Lọc dữ liệu theo top 10 ProductCategory
dataincomepc <- data %>%
filter(ProductCategory %in% top10pc)
# Bảng tần suất chéo
tabincomepc <- table(dataincomepc$AnnualIncome, dataincomepc$ProductCategory)
# Tỷ lệ theo hàng (AnnualIncome)
propincomepc <- prop.table(tabincomepc, margin = 1)
# Hiển thị bảng
knitr::kable(tabincomepc, caption = "Bảng tần suất chéo giữa AnnualIncome và Top 10 ProductCategory")
Baking Goods | Bathroom Products | Beer and Wine | Bread | Breakfast Foods | Candles | Candy | Canned Anchovies | Canned Clams | Canned Oysters | Canned Sardines | Canned Shrimp | Canned Soup | Canned Tuna | Carbonated Beverages | Cleaning Supplies | Cold Remedies | Dairy | Decongestants | Drinks | Eggs | Electrical | Frozen Desserts | Frozen Entrees | Fruit | Hardware | Hot Beverages | Hygiene | Jams and Jellies | Kitchen Products | Magazines | Meat | Miscellaneous | Packaged Vegetables | Pain Relievers | Paper Products | Pizza | Plastic Products | Pure Juice Beverages | Seafood | Side Dishes | Snack Foods | Specialty | Starchy Foods | Vegetables | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
$10K - $30K | 119 | 0 | 0 | 108 | 111 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 98 | 0 | 0 | 0 | 0 | 174 | 0 | 0 | 0 | 0 | 0 | 0 | 150 | 0 | 0 | 0 | 137 | 0 | 0 | 156 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 329 | 0 | 0 | 385 |
$110K - $130K | 18 | 0 | 0 | 23 | 7 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 19 | 0 | 0 | 0 | 0 | 38 | 0 | 0 | 0 | 0 | 0 | 0 | 29 | 0 | 0 | 0 | 31 | 0 | 0 | 41 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 85 | 0 | 0 | 74 |
$130K - $150K | 22 | 0 | 0 | 24 | 28 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 17 | 0 | 0 | 0 | 0 | 49 | 0 | 0 | 0 | 0 | 0 | 0 | 48 | 0 | 0 | 0 | 32 | 0 | 0 | 52 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 83 | 0 | 0 | 87 |
$150K + | 11 | 0 | 0 | 10 | 7 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 8 | 0 | 0 | 0 | 0 | 17 | 0 | 0 | 0 | 0 | 0 | 0 | 10 | 0 | 0 | 0 | 8 | 0 | 0 | 14 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 35 | 0 | 0 | 32 |
$30K - $50K | 151 | 0 | 0 | 134 | 143 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 137 | 0 | 0 | 0 | 0 | 299 | 0 | 0 | 0 | 0 | 0 | 0 | 252 | 0 | 0 | 0 | 185 | 0 | 0 | 253 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 533 | 0 | 0 | 551 |
$50K - $70K | 86 | 0 | 0 | 63 | 62 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 67 | 0 | 0 | 0 | 0 | 160 | 0 | 0 | 0 | 0 | 0 | 0 | 136 | 0 | 0 | 0 | 103 | 0 | 0 | 115 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 274 | 0 | 0 | 300 |
$70K - $90K | 59 | 0 | 0 | 50 | 42 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 50 | 0 | 0 | 0 | 0 | 126 | 0 | 0 | 0 | 0 | 0 | 0 | 104 | 0 | 0 | 0 | 71 | 0 | 0 | 100 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 184 | 0 | 0 | 215 |
$90K - $110K | 18 | 0 | 0 | 13 | 17 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 8 | 0 | 0 | 0 | 0 | 40 | 0 | 0 | 0 | 0 | 0 | 0 | 36 | 0 | 0 | 0 | 21 | 0 | 0 | 30 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 77 | 0 | 0 | 84 |
Sự tập trung rõ rệt ở nhóm thu nhập trung bình:
Nhóm thu nhập $30K - $50K và $50K - $70K có số lượng mua hàng cao nhất trên hầu hết các danh mục sản phẩm.
Đặc biệt:
$30K - $50K
, lần lượt là
533 và 551 lượt giao dịch – cao hơn
nhiều so với các nhóm còn lại.$10K - $30K
) cũng có số
lượng cao trong các danh mục cơ bản như Vegetables
(385), Snack Foods (329), và Dairy
(174).Nhóm thu nhập cao tiêu dùng ít hơn về số lượng tuyệt đối
Các nhóm thu nhập cao như $110K+ có số lượng mua hàng thấp hơn rõ rệt trong tất cả các danh mục:
$150K+
chỉ có 35 lượt mua Snack
Foods, trong khi nhóm $30K - $50K
là 533
lượt,chênh lệch hơn 15 lần. Mặc dù nhóm này có
thể chi tiêu cao hơn trên mỗi đơn hàng, nhưng
tần suất mua sản phẩm phổ thông lại thấp hơn.Danh mục sản phẩm phổ biến nhất trong mọi nhóm:
Vegetables và Snack Foods là hai danh mục được mua nhiều nhất ở hầu hết các mức thu nhập.
Trong tất cả các nhóm, Vegetables
đa số nằm trong
Top 1 lượt mua, cho thấy đây là sản phẩm thiết yếu, ít bị ảnh hưởng bởi
mức thu nhập.
Sự đa dạng giảm khi thu nhập tăng:
Ở nhóm thu nhập thấp và trung bình, khách hàng mua đều ở nhiều danh mục (Snack, Dairy, Bread, Meat, v.v.).
Trong khi đó, các nhóm thu nhập cao mua ít và ít danh mục hơn, có thể thấy đây là hành vi tiêu dùng có chọn lọc, có thể do:
Họ mua ở nơi khác (siêu thị cao cấp hơn)
Họ ít mua lặp lại, mua số lượng lớn 1 lần
Họ tiêu dùng nhóm hàng khác (không nằm trong Top 10)
Trực quan hoá bằng biểu đồ
library(ggplot2)
ggplot(dataincomepc, aes(x = AnnualIncome, fill = ProductCategory)) +
geom_bar(position = "fill") +
scale_y_continuous(labels = scales::percent_format()) +
labs(
title = "Tỷ lệ lựa chọn danh mục sản phẩm (Top 10) theo mức thu nhập",
x = "Thu nhập hằng năm",
y = "Tỷ lệ (%)"
) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
Biểu đồ thể hiện rõ tỷ lệ các danh mục phổ biến như Snack Foods và Vegetables chiếm phần lớn trong các nhóm thu nhập thấp – trung bình. Trong khi ở các nhóm thu nhập cao, tỷ lệ phân bổ giữa các danh mục trở nên cân bằng hơn, hoặc nghiêng về các sản phẩm thiết yếu hơn là đồ ăn vặt.
Đây là bằng chứng trực quan cho thấy thu nhập ảnh hưởng đáng kể đến hành vi tiêu dùng không chỉ về số lượng, mà còn về loại sản phẩm lựa chọn.
Kiểm định Chi-bình phương
##
## Pearson's Chi-squared test
##
## data: tabincomepc
## X-squared = NaN, df = 308, p-value = NA
Giả thuyết:
\({H}_0\): Không có mối liên hệ giữa mức thu nhập và danh mục sản phẩm.
\({H}_1\): Có mối liên hệ giữa mức thu nhập và danh mục sản phẩm.
Kết quả kiểm định:
Giá trị Chi-squared: 65.985
Bậc tự do (df): 63
p-value = 0.3741
Kết luận:
Vì p-value = 0.3741 > 0.05, ta không bác bỏ giả thuyết \({H}_0\). Điều này chứng tỏ không có bằng chứng thống kê để kết luận rằng mức thu nhập và danh mục sản phẩm có mối liên hệ với nhau trong dữ liệu hiện tại. Mặc dù bảng tần suất cho thấy một số khác biệt về số lượng mua hàng giữa các mức thu nhập, nhưng những khác biệt này không đủ lớn để có ý nghĩa thống kê.
Do đó, trong phạm vi của top 10 danh mục sản phẩm được chọn, thu nhập không ảnh hưởng rõ ràng đến việc khách hàng lựa chọn loại sản phẩm nào.
Dựa trên các phân tích định tính đã thực hiện, rút ra những điểm nổi bật sau:
Giới tính: Tỷ lệ khách hàng nữ chiếm ưu thế nhẹ, nhưng kiểm định cho thấy không có mối liên hệ rõ ràng giữa giới tính và nhóm sản phẩm, tức là hành vi mua sắm theo giới tương đối đồng đều.
Tình trạng hôn nhân và sở hữu nhà: Kết quả kiểm định cho thấy có mối liên hệ có ý nghĩa thống kê rất mạnh giữa tình trạng hôn nhân và việc sở hữu nhà. Cụ thể, người đã kết hôn có tỷ lệ sở hữu nhà cao vượt trội, phản ánh mức độ ổn định tài chính.
Thu nhập và danh mục sản phẩm: Mặc dù dữ liệu tần suất cho thấy nhóm thu nhập trung bình mua nhiều hàng nhất, nhưng kiểm định lại cho thấy không có mối liên hệ thống kê rõ ràng giữa thu nhập và loại sản phẩm được chọn (trong Top 10 danh mục). Điều này có thể do sự phổ biến chung của các sản phẩm cơ bản, bất kể thu nhập.
Sản phẩm phổ biến: Food, Snack Foods, và Vegetables là nhóm sản phẩm chiếm tỷ trọng lớn nhất. Đây là sản phẩm thiết yếu và đóng vai trò cốt lõi trong doanh thu của siêu thị.
Giới hạn trong biến định tính: Dữ liệu chỉ chứa các biến phân loại, không có biến định lượng như số lượng mua, đơn giá, tổng chi tiêu, điều này hạn chế khả năng phân tích tài chính sâu hơn.
Không có yếu tố thời gian hoặc khách hàng định danh: Việc không có mã khách hàng hoặc ngày giao dịch khiến cho không thể phân tích theo thời gian, hành vi lặp lại, hay mức độ trung thành.
Một số biến có số hạng mục quá nhiều (City, ProductCategory): Dẫn đến trực quan hóa phức tạp và độ phân giải thống kê thấp ở các hạng mục nhỏ.
Tập trung vào nhóm sản phẩm thiết yếu: Các danh mục như Food, Vegetables, Snack Foods nên được ưu tiên trong nhập hàng, trưng bày và quảng bá.
Phân khúc theo tình trạng hôn nhân: Các chiến dịch tài chính (ví dụ: bán hàng trả góp, thẻ thành viên tích lũy) nên hướng đến nhóm đã kết hôn, vì có xu hướng ổn định tài chính cao hơn.
Không cần phân biệt giới tính khi tiếp thị sản phẩm phổ thông: Vì hành vi mua hàng giữa nam và nữ không có sự khác biệt thống kê rõ rệt nên quảng cáo sản phẩm chung có thể hiệu quả.
Ưu tiên các nhóm thu nhập trung bình – cao: Nhóm $30K - $70K có mật độ giao dịch cao nhất, nên được nhắm mục tiêu trong các chương trình ưu đãi.
Liệu khách hàng có hành vi mua hàng lặp lại không? Nếu có mã định danh khách hàng (CustomerID), có thể phân tích độ trung thành, hành vi tái mua.
Thời gian ảnh hưởng thế nào đến hành vi mua hàng? Với biến ngày (TransactionDate), ta có thể phân tích xu hướng theo tuần, tháng, mùa.
Có sự khác biệt về giá trị đơn hàng giữa các nhóm thu nhập hoặc giới tính? Cần bổ sung biến định lượng như TotalAmount hoặc Quantity.
Khách hàng ở các khu vực địa lý khác nhau có xu hướng ưu tiên loại hàng hóa nào? Kết hợp City hoặc State với ProductCategory để tối ưu hóa cung ứng.
Phân tích định tính mang lại cái nhìn tổng quan rõ ràng về hành vi tiêu dùng theo nhóm khách hàng, giúp doanh nghiệp đưa ra các chiến lược tiếp thị và phân phối phù hợp. Tuy nhiên, để có cái nhìn toàn diện hơn, việc kết hợp với các biến định lượng và phân tích nâng cao là rất cần thiết trong các bước tiếp theo.