data <- read.csv ("C:/Users/ADMIN/Desktop/PTDL DINH TINH/Thyroid_Diff.csv", header = T)
str(data)
## 'data.frame': 383 obs. of 13 variables:
## $ Age : int 27 34 30 62 62 52 41 46 51 40 ...
## $ Gender : chr "F" "F" "F" "F" ...
## $ Smoking : chr "No" "No" "No" "No" ...
## $ Hx.Smoking : chr "No" "Yes" "No" "No" ...
## $ Hx.Radiothreapy : chr "No" "No" "No" "No" ...
## $ Thyroid.Function : chr "Euthyroid" "Euthyroid" "Euthyroid" "Euthyroid" ...
## $ Physical.Examination: chr "Single nodular goiter-left" "Multinodular goiter" "Single nodular goiter-right" "Single nodular goiter-right" ...
## $ Adenopathy : chr "No" "No" "No" "No" ...
## $ Pathology : chr "Micropapillary" "Micropapillary" "Micropapillary" "Micropapillary" ...
## $ Focality : chr "Uni-Focal" "Uni-Focal" "Uni-Focal" "Uni-Focal" ...
## $ Risk : chr "Low" "Low" "Low" "Low" ...
## $ Response : chr "Indeterminate" "Excellent" "Excellent" "Excellent" ...
## $ Recurred : chr "No" "No" "No" "No" ...
# Chọn các biến định tính
dldt <- c("Age", "Gender", "Smoking", "Hx.Smoking", "Hx.Radiothreapy", "Thyroid.Function", "Focality", "Response", "Recurred")
# Tạo bộ dữ liệu mới chỉ chứa định tính
dat <- data[, dldt]
str(dat)
## 'data.frame': 383 obs. of 9 variables:
## $ Age : int 27 34 30 62 62 52 41 46 51 40 ...
## $ Gender : chr "F" "F" "F" "F" ...
## $ Smoking : chr "No" "No" "No" "No" ...
## $ Hx.Smoking : chr "No" "Yes" "No" "No" ...
## $ Hx.Radiothreapy : chr "No" "No" "No" "No" ...
## $ Thyroid.Function: chr "Euthyroid" "Euthyroid" "Euthyroid" "Euthyroid" ...
## $ Focality : chr "Uni-Focal" "Uni-Focal" "Uni-Focal" "Uni-Focal" ...
## $ Response : chr "Indeterminate" "Excellent" "Excellent" "Excellent" ...
## $ Recurred : chr "No" "No" "No" "No" ...
Bộ dữ liệu này bao gồm thông tin lâm sàng và tiền sử y tế của 383 bệnh nhân được chẩn đoán và điều trị ung thư tuyến giáp biệt hóa tốt (well-differentiated thyroid cancer). Mục tiêu chính của việc thu thập và phân tích dữ liệu là để tìm hiểu các yếu tố có thể liên quan đến nguy cơ tái phát bệnh sau điều trị, từ đó hỗ trợ bác sĩ đưa ra quyết định điều trị và theo dõi phù hợp hơn cho từng bệnh nhân.
Trong quá trình phân tích, chỉ 9 biến được lựa chọn, bao gồm: tuổi (Age), giới tính (Gender), tình trạng hút thuốc hiện tại (Smoking), tiền sử hút thuốc (Hx.Smoking), tiền sử xạ trị (Hx.Radiothreapy), tình trạng chức năng tuyến giáp (Thyroid.Function), tình trạng ổ khối u (Focality), mức độ đáp ứng điều trị (Response) và biến mục tiêu là khả năng tái phát (Recurred). Đây đều là những yếu tố quan trọng có thể ảnh hưởng đến diễn tiến bệnh và được ghi nhận theo dạng số hoặc dạng phân loại (danh mục).
Biến mục tiêu Recurred cho biết liệu bệnh nhân có bị tái phát ung thư tuyến giáp sau điều trị hay không, với hai giá trị là “Yes” (tái phát) và “No” (không tái phát). Các biến còn lại được xem là đặc trưng đầu vào dùng để phân tích
Cấu trúc bộ dữ liệu Supermarket Transactions
Nhưng với yêu cầu chỉ sử dụng các biến định tính để phân tính nên chúng ta chỉ quan tâm đến các biến như sau :
# Kiểm tra số lượng NA trong mỗi cột định tính
na_counts <- sapply(dat, function(x) sum(is.na(x)))
na_counts
## Age Gender Smoking Hx.Smoking
## 0 0 0 0
## Hx.Radiothreapy Thyroid.Function Focality Response
## 0 0 0 0
## Recurred
## 0
Nhận xét - Kết quả thu được cho thấy rằng tất cả các cột định tính đều không có giá trị thiếu (NA) vì số lượng NA trong các cột đều hiển thị là 0
# Chuyển các biến định tính sang factor
dat$Age <- as.factor(dat$Age)
dat$Gender <- as.factor(dat$Gender)
dat$Smoking <- as.factor(dat$Smoking)
dat$Hx.Radiothreapy <- as.factor(dat$Hx.Smoking)
dat$Hx.Radiothreapy <- as.factor(dat$Hx.Radiothreapy)
dat$Thyroid.Function <- as.factor(dat$Thyroid.Function)
dat$Focality <- as.factor(dat$Focality)
dat$Response <- as.factor(dat$Response)
dat$Recurred <- as.factor(dat$Recurred)
Kiểm tra lại sau khi chuyển đổi
Giải thích:
sapply(dat, class): Áp dụng hàm class() cho từng cột để kiểm tra kiểu dữ liệu. Kết quả trả về là một vector hiển thị tên cột và loại dữ liệu tương ứng.
Nếu kết quả là “factor” cho tất cả các cột → chuyển đổi thành công.
sapply(dat, class)
## Age Gender Smoking Hx.Smoking
## "factor" "factor" "factor" "character"
## Hx.Radiothreapy Thyroid.Function Focality Response
## "factor" "factor" "factor" "factor"
## Recurred
## "factor"
Nhận xét : Kết quả thu được toàn bộ các biến đã được chuyển đổi thành dạng factor.
install.packages("ggplot2", repos = "https://cloud.r-project.org")
## Installing package into 'C:/Users/ADMIN/AppData/Local/R/win-library/4.3'
## (as 'lib' is unspecified)
##
## There is a binary version available but the source version is later:
## binary source needs_compilation
## ggplot2 3.5.1 3.5.2 FALSE
## installing the source package 'ggplot2'
library(ggplot2)
tab_gender <- table(dat$Gender)
tab_gender
##
## F M
## 312 71
table(dat$Gender)/sum(nrow(dat))
##
## F M
## 0.8146214 0.1853786
# Tạo dataframe từ bảng tần số
gender_freq <- as.data.frame(tab_gender)
colnames(gender_freq) <- c("Gender", "Frequency")
# Tính phần trăm
gender_freq$Percentage <- gender_freq$Frequency / sum(gender_freq$Frequency) * 100
# Tạo nhãn chỉ gồm phần trăm
gender_freq$Label <- paste0(round(gender_freq$Percentage, 1), "%")
# Vẽ biểu đồ tròn với nhãn chỉ phần trăm
ggplot(gender_freq, aes(x = "", y = Percentage, fill = Gender)) +
geom_col(width = 1, color = "white") +
coord_polar(theta = "y") +
theme_void() +
geom_text(aes(label = Label), position = position_stack(vjust = 0.5), size = 5) +
labs(title = "Phân bố giới tính của các bệnh nhân") +
scale_fill_brewer(palette = "Pastel1")
tab_smoking <- table(dat$Smoking)
tab_smoking
##
## No Yes
## 334 49
table(dat$Smoking)/sum(nrow(dat))
##
## No Yes
## 0.8720627 0.1279373
# Tạo bảng tần số
tab_smoking <- table(dat$Smoking)
# Chuyển sang data frame
smoking_freq <- as.data.frame(tab_smoking)
colnames(smoking_freq) <- c("Smoking", "Frequency")
# Tính phần trăm
smoking_freq$Percentage <- smoking_freq$Frequency / sum(smoking_freq$Frequency) * 100
# Tạo nhãn chỉ gồm phần trăm
smoking_freq$Label <- paste0(round(smoking_freq$Percentage, 1), "%")
# Vẽ biểu đồ tròn
ggplot(smoking_freq, aes(x = "", y = Percentage, fill = Smoking)) +
geom_col(width = 1, color = "white") +
coord_polar(theta = "y") +
theme_void() +
geom_text(aes(label = Label),
position = position_stack(vjust = 0.5), size = 4) +
labs(title = "Phân bố tình trạng hút thuốc của người tham gia") +
scale_fill_brewer(palette = "Set3")
tab_xatri <- table(dat$Hx.Radiothreapy)
tab_xatri
##
## No Yes
## 355 28
table(dat$Hx.Radiothreapy)/sum(nrow(dat))
##
## No Yes
## 0.92689295 0.07310705
# Tạo bảng tần số
tab_xatri <- table(dat$Hx.Radiothreapy)
# Chuyển sang data frame
xatri_freq <- as.data.frame(tab_xatri)
colnames(xatri_freq) <- c("Hx.Radiothreapy", "Frequency")
# Tính phần trăm
xatri_freq$Percentage <- xatri_freq$Frequency / nrow(dat) * 100
# Tạo nhãn phần trăm
xatri_freq$Label <- paste0(round(xatri_freq$Percentage, 1), "%")
# Vẽ biểu đồ tròn
ggplot(xatri_freq, aes(x = "", y = Percentage, fill = Hx.Radiothreapy)) +
geom_col(width = 1, color = "white") +
coord_polar(theta = "y") +
theme_void() +
geom_text(aes(label = Label),
position = position_stack(vjust = 0.5), size = 4) +
labs(title = "Phân bố tiền sử xạ trị của người tham gia") +
scale_fill_brewer(palette = "Set2")
tab_tuyengiap <- table(dat$Thyroid.Function)
tab_tuyengiap
##
## Clinical Hyperthyroidism Clinical Hypothyroidism
## 20 12
## Euthyroid Subclinical Hyperthyroidism
## 332 5
## Subclinical Hypothyroidism
## 14
table(dat$Thyroid.Function)/sum(nrow(dat))
##
## Clinical Hyperthyroidism Clinical Hypothyroidism
## 0.05221932 0.03133159
## Euthyroid Subclinical Hyperthyroidism
## 0.86684073 0.01305483
## Subclinical Hypothyroidism
## 0.03655352
install.packages("dplyr", repos = "https://cloud.r-project.org")
## Installing package into 'C:/Users/ADMIN/AppData/Local/R/win-library/4.3'
## (as 'lib' is unspecified)
## package 'dplyr' successfully unpacked and MD5 sums checked
## Warning: cannot remove prior installation of package 'dplyr'
## Warning in file.copy(savedcopy, lib, recursive = TRUE): problem copying
## C:\Users\ADMIN\AppData\Local\R\win-library\4.3\00LOCK\dplyr\libs\x64\dplyr.dll
## to C:\Users\ADMIN\AppData\Local\R\win-library\4.3\dplyr\libs\x64\dplyr.dll:
## Permission denied
## Warning: restored 'dplyr'
##
## The downloaded binary packages are in
## C:\Users\ADMIN\AppData\Local\Temp\RtmpUzsJ2Y\downloaded_packages
library(dplyr)
## Warning: package 'dplyr' was built under R version 4.3.3
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
install.packages("ggplot2")
## Warning: package 'ggplot2' is in use and will not be installed
library(ggplot2)
# Tạo bảng tần số
tab_tuyengiap <- table(dat$Thyroid.Function)
# Chuyển sang data frame
tuyengiap_freq <- as.data.frame(tab_tuyengiap)
colnames(tuyengiap_freq) <- c("Thyroid.Function", "Frequency")
# Tính phần trăm
tuyengiap_freq$Percentage <- tuyengiap_freq$Frequency / nrow(dat) * 100
# Đổi tên các mức sang tiếng Việt
tuyengiap_freq$Thyroid.Function <- recode(tuyengiap_freq$Thyroid.Function,
"Euthyroid" = "Tuyến giáp bình thường",
"Clinical Hyperthyroidism" = "Cường giáp lâm sàng",
"Clinical Hypothyroidism" = "Suy giáp lâm sàng",
"Subclinical Hyperthyroidism" = "Cường giáp cận lâm sàng",
"Subclinical Hypothyroidism" = "Suy giáp cận lâm sàng"
)
# Vẽ biểu đồ cột
ggplot(tuyengiap_freq, aes(x = Thyroid.Function, y = Frequency, fill = Thyroid.Function)) +
geom_col(width = 0.6) +
geom_text(aes(label = paste0(round(Percentage, 1), "%")),
vjust = -0.5, size = 4) +
labs(title = "Tần số theo tình trạng chức năng tuyến giáp",
x = "Chức năng tuyến giáp (dịch sang tiếng Việt)",
y = "Số lượng") +
theme_minimal() +
theme(legend.position = "none") +
scale_fill_brewer(palette = "Set2")
tab_khoiu <- table(dat$Focality)
tab_khoiu
##
## Multi-Focal Uni-Focal
## 136 247
table(dat$Focality)/sum(nrow(dat))
##
## Multi-Focal Uni-Focal
## 0.3550914 0.6449086
# Bước 1: Tạo bảng tần số
tab_khoiu <- table(dat$Focality)
# Bước 2: Chuyển sang data frame
khoiu_freq <- as.data.frame(tab_khoiu)
colnames(khoiu_freq) <- c("Focality", "Frequency")
# Bước 3: Tính phần trăm
khoiu_freq$Percentage <- khoiu_freq$Frequency / sum(khoiu_freq$Frequency) * 100
# Bước 4: Tạo nhãn phần trăm
khoiu_freq$Label <- paste0(round(khoiu_freq$Percentage, 1), "%")
# Đổi tên các thành phần
khoiu_freq$Focality <- recode(khoiu_freq$Focality,
"Multi-Focal" = "Đa ổ",
"Uni-Focal" = "Đơn ổ")
ggplot(khoiu_freq, aes(x = "", y = Percentage, fill = Focality)) +
geom_col(width = 1, color = "white") +
coord_polar(theta = "y") +
geom_text(aes(label = Label), position = position_stack(vjust = 0.5), size = 5) +
labs(title = "Phân bố theo đặc điểm khối u (Focality)") +
theme_void() +
scale_fill_brewer(palette = "Pastel2")
tab_dapung <- table(dat$Response)
tab_dapung
##
## Biochemical Incomplete Excellent Indeterminate
## 23 208 61
## Structural Incomplete
## 91
table(dat$Response)/sum(nrow(dat))
##
## Biochemical Incomplete Excellent Indeterminate
## 0.06005222 0.54308094 0.15926893
## Structural Incomplete
## 0.23759791
# Tạo bảng tần số
tab_dapung <- table(dat$Response)
# Chuyển sang data frame
dapung_freq <- as.data.frame(tab_dapung)
colnames(dapung_freq) <- c("Response", "Frequency")
# Tính phần trăm
dapung_freq$Percentage <- dapung_freq$Frequency / sum(dapung_freq$Frequency) * 100
# Vẽ biểu đồ cột
ggplot(dapung_freq, aes(x = Response, y = Frequency, fill = Response)) +
geom_bar(stat = "identity", width = 0.6) +
geom_text(aes(label = Frequency), vjust = -0.5) +
labs(title = "Phân bố mức độ đáp ứng điều trị", x = "Đáp ứng", y = "Tần số") +
theme_minimal() +
theme(legend.position = "none") +
scale_fill_brewer(palette = "Set2")
tab_taiphat <- table(dat$Recurred)
tab_taiphat
##
## No Yes
## 275 108
table(dat$Recurred)/sum(nrow(dat))
##
## No Yes
## 0.7180157 0.2819843
# Tạo bảng tần số
tab_taiphat <- table(dat$Recurred)
# Chuyển sang data frame
taiphat_freq <- as.data.frame(tab_taiphat)
colnames(taiphat_freq) <- c("Recurred", "Frequency")
# Tính phần trăm
taiphat_freq$Percentage <- taiphat_freq$Frequency / sum(taiphat_freq$Frequency) * 100
# Tạo nhãn hiển thị phần trăm
taiphat_freq$Label <- paste0(round(taiphat_freq$Percentage, 1), "%")
ggplot(taiphat_freq, aes(x = "", y = Percentage, fill = Recurred)) +
geom_col(width = 1, color = "white") +
coord_polar(theta = "y") +
theme_void() +
geom_text(aes(label = Label), position = position_stack(vjust = 0.5), size = 4) +
labs(title = "Tỷ lệ tái phát sau điều trị") +
scale_fill_brewer(palette = "Pastel1")
prop.test(x = sum(dat$Gender == "Male"),
n = nrow(dat),
p = 0.5, # Giả thuyết H0: p = 0.5
conf.level = 0.95)
##
## 1-sample proportions test with continuity correction
##
## data: sum(dat$Gender == "Male") out of nrow(dat), null probability 0.5
## X-squared = 381, df = 1, p-value < 2.2e-16
## alternative hypothesis: true p is not equal to 0.5
## 95 percent confidence interval:
## 0.00000000 0.01237905
## sample estimates:
## p
## 0
Giả thuyết:
Nhận xét:
prop.test(x = sum(dat$Thyroid.Function == "Euthyroid"),
n = nrow(dat),
p = 0.8, # Giả thuyết: 80% người bệnh có tuyến giáp bình thường
conf.level = 0.95)
##
## 1-sample proportions test with continuity correction
##
## data: sum(dat$Thyroid.Function == "Euthyroid") out of nrow(dat), null probability 0.8
## X-squared = 10.281, df = 1, p-value = 0.001344
## alternative hypothesis: true p is not equal to 0.8
## 95 percent confidence interval:
## 0.8277161 0.8984031
## sample estimates:
## p
## 0.8668407
Giả thuyết kiểm định:
Nhận xét
prop.test(
x = sum(dat$Focality == "Uni-Focal"),
n = nrow(dat),
p = 0.8,
alternative = "greater", # Một phía: kiểm định p > 0.8
conf.level = 0.95
)
##
## 1-sample proportions test with continuity correction
##
## data: sum(dat$Focality == "Uni-Focal") out of nrow(dat), null probability 0.8
## X-squared = 56.612, df = 1, p-value = 1
## alternative hypothesis: true p is greater than 0.8
## 95 percent confidence interval:
## 0.6024711 1.0000000
## sample estimates:
## p
## 0.6449086
prop.test(
x = sum(dat$Response == "Excellent"),
n = nrow(dat),
p = 0.5,
alternative = "less", # Một phía: kiểm định p < 0.5
conf.level = 0.95
)
##
## 1-sample proportions test with continuity correction
##
## data: sum(dat$Response == "Excellent") out of nrow(dat), null probability 0.5
## X-squared = 2.6736, df = 1, p-value = 0.949
## alternative hypothesis: true p is less than 0.5
## 95 percent confidence interval:
## 0.0000000 0.5857873
## sample estimates:
## p
## 0.5430809
prop.test(
x = sum(dat$Recurred == "No"),
n = nrow(dat),
p = 0.7,
alternative = "greater", # Một phía: kiểm định p > 0.7
conf.level = 0.95
)
##
## 1-sample proportions test with continuity correction
##
## data: sum(dat$Recurred == "No") out of nrow(dat), null probability 0.7
## X-squared = 0.50926, df = 1, p-value = 0.2377
## alternative hypothesis: true p is greater than 0.7
## 95 percent confidence interval:
## 0.6774208 1.0000000
## sample estimates:
## p
## 0.7180157
table_gender_smoking <- table(dat$Gender, dat$Smoking)
table_gender_smoking
##
## No Yes
## F 303 9
## M 31 40
# Bảng chéo số lượng
cross_gs <- table(dat$Gender, dat$Smoking)
# Chuyển sang tỷ lệ phần trăm theo từng giới tính (hàng)
prop_gs <- prop.table(cross_gs, margin = 1)
# Chuyển thành data frame
cross_dfgs <- as.data.frame(prop_gs)
colnames(cross_dfgs) <- c("Gender", "Smoking", "Percentage")
# Nhân 100 để biểu diễn phần trăm
cross_dfgs$Percentage <- cross_dfgs$Percentage * 100
# Biểu đồ
ggplot(cross_dfgs, aes(x = Smoking, y = Percentage, fill = Gender)) +
geom_col(position = "dodge") +
labs(title = "Tỷ lệ hút thuốc theo giới tính",
x = "Tình trạng hút thuốc",
y = "Tỷ lệ (%)") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 0, hjust = 0.5)) +
scale_fill_brewer(palette = "Set2")
# Bảng tần số giữa Gender và Smoking
tbl_gs <- table(dat$Gender, dat$Smoking)
# Kiểm định Chi bình phương
chisq.test(tbl_gs)
##
## Pearson's Chi-squared test with Yates' continuity correction
##
## data: tbl_gs
## X-squared = 143.37, df = 1, p-value < 2.2e-16
Các giả thuyết
Giả thuyết không (H₀): Giới tính và tình trạng đang hút thuốc của
bệnh nhân độc lập với nhau
Giả thuyết đối (H₁): Giới tính và tình trạng đang hút thuốc của bệnh nhân có mối quan hệ phụ thuộc.
Kết quả kiểm định
Kiểu kiểm định: Pearson’s Chi-squared test (với hiệu chỉnh Yates)
Giá trị thống kê Chi-squared (X-squared): 143.37
Bậc tự do (df): 1
Giá trị p (p-value): < 2.2e-16 (rất nhỏ, gần bằng 0)
Kết luận
install.packages("epitools", repos = "https://cloud.r-project.org")
## Installing package into 'C:/Users/ADMIN/AppData/Local/R/win-library/4.3'
## (as 'lib' is unspecified)
## package 'epitools' successfully unpacked and MD5 sums checked
##
## The downloaded binary packages are in
## C:\Users\ADMIN\AppData\Local\Temp\RtmpUzsJ2Y\downloaded_packages
library(epitools)
# Tạo bảng chéo
table_gender_smoking <- table(dat$Gender, dat$Smoking)
riskratio(table_gender_smoking)
## $data
##
## No Yes Total
## F 303 9 312
## M 31 40 71
## Total 334 49 383
##
## $measure
## risk ratio with 95% C.I.
## estimate lower upper
## F 1.00000 NA NA
## M 19.53052 9.938032 38.38195
##
## $p.value
## two-sided
## midp.exact fisher.exact chi.square
## F NA NA NA
## M 0 3.555162e-26 4.460945e-34
##
## $correction
## [1] FALSE
##
## attr(,"method")
## [1] "Unconditional MLE & normal approximation (Wald) CI"
Nhận xét
Kết luận
# Tính Odds Ratio
oddsratio(table_gender_smoking)
## $data
##
## No Yes Total
## F 303 9 312
## M 31 40 71
## Total 334 49 383
##
## $measure
## odds ratio with 95% C.I.
## estimate lower upper
## F 1.0000 NA NA
## M 42.0625 19.39216 100.7353
##
## $p.value
## two-sided
## midp.exact fisher.exact chi.square
## F NA NA NA
## M 0 3.555162e-26 4.460945e-34
##
## $correction
## [1] FALSE
##
## attr(,"method")
## [1] "median-unbiased estimate & mid-p exact CI"
Nhận xét
Kết luận
Kết quả phân tích cho thấy nam giới có xu hướng hút thuốc cao hơn nữ giới một cách đáng kể. Cụ thể, Odds Ratio (OR) = 42.06, nghĩa là: Xác suất hút thuốc của nam giới cao hơn khoảng 42 lần so với nữ giới.
Khoảng tin cậy 95% cho OR là từ 19.39 đến 100.74, cho thấy ước lượng này có độ chính xác cao và khác biệt là có ý nghĩa thống kê. Giá trị p-value rất nhỏ (< 0.001) củng cố kết luận rằng mối liên hệ giữa giới tính và hành vi hút thuốc là có ý nghĩa thống kê.