## Warning: package 'readxl' was built under R version 4.5.1
dim(data)
## [1] 100000 17
Kết luận
Bộ dữ liệu gồm 100.000 quan sát và 17 biến
names(data)
## [1] "year" "gender" "age"
## [4] "location" "race:AfricanAmerican" "race:Asian"
## [7] "race:Caucasian" "race:Hispanic" "race:Other"
## [10] "hypertension" "heart_disease" "smoking_history"
## [13] "bmi" "hbA1c_level" "blood_glucose_level"
## [16] "diabetes" "clinical_notes"
sum(sapply(data, is.numeric))
## [1] 13
sum(sapply(data, is.character) | sapply(data, is.factor))
## [1] 4
Kết luận
Có 13 biến định lượng (numeric) và 4 biến định tính (categorical). Điều này cho thấy bộ dữ liệu có cấu trúc khá đa dạng
sum(duplicated(data))
## [1] 14
data <- data[!duplicated(data), ]
sum(duplicated(data))
## [1] 0
Kết luận
Kết quả sum(duplicated(data)) = 14 cho thấy bộ dữ liệu có 14 dòng trùng lặp.Sau khi nhận thấy dữ liệu trùng lặp ta tiến hành loại bỏ trùng lặp.
any(is.na(data)) # Có NA không
## [1] FALSE
colSums(is.na(data)) # Đếm số NA từng cột
## year gender age
## 0 0 0
## location race:AfricanAmerican race:Asian
## 0 0 0
## race:Caucasian race:Hispanic race:Other
## 0 0 0
## hypertension heart_disease smoking_history
## 0 0 0
## bmi hbA1c_level blood_glucose_level
## 0 0 0
## diabetes clinical_notes
## 0 0
Kết luận
Kết quả kiểm tra cho thấy không có giá trị bị thiếu (NA) trong toàn bộ bộ dữ liệu. Cụ thể, hàm any(is.na(data)) trả về FALSE, và tổng số giá trị NA ở tất cả các cột (colSums(is.na(data))) đều bằng 0.
library(knitr)
variable_meaning <- data.frame(
Tên_biến = c(
"year", "gender", "age", "location",
"race:AfricanAmerican", "race:Asian", "race:Caucasian", "race:Hispanic", "race:Other",
"hypertension", "heart_disease", "smoking_history",
"bmi", "hbA1c_level", "blood_glucose_level", "diabetes", "clinical_notes"
),
Ý_nghĩa = c(
"Năm ghi nhận dữ liệu của bệnh nhân",
"Giới tính của người tham gia (Nam/Nữ)",
"Tuổi của người tham gia (năm)",
"Khu vực sinh sống hoặc nơi khám bệnh",
"Thuộc nhóm chủng tộc Người Mỹ gốc Phi (1: Có, 0: Không)",
"Thuộc nhóm chủng tộc Châu Á (1: Có, 0: Không)",
"Thuộc nhóm chủng tộc Da trắng (1: Có, 0: Không)",
"Thuộc nhóm chủng tộc gốc Tây Ban Nha (1: Có, 0: Không)",
"Thuộc nhóm chủng tộc khác (1: Có, 0: Không)",
"Tình trạng tăng huyết áp (1: Có, 0: Không)",
"Bệnh tim mạch (1: Có, 0: Không)",
"Tiền sử hút thuốc (Never, Former, Current, Unknown)",
"Chỉ số khối cơ thể (BMI) - đo độ béo cơ thể (kg/m²)",
"Chỉ số HbA1c - phản ánh đường huyết trung bình 3 tháng gần nhất (%)",
"Nồng độ đường huyết hiện tại (mg/dL)",
"Tình trạng tiểu đường (1: Có, 0: Không)",
"Ghi chú lâm sàng hoặc nhận xét của bác sĩ"
),
stringsAsFactors = FALSE
)
kable(variable_meaning, col.names = c("Tên biến", "Ý nghĩa"))
| Tên biến | Ý nghĩa |
|---|---|
| year | Năm ghi nhận dữ liệu của bệnh nhân |
| gender | Giới tính của người tham gia (Nam/Nữ) |
| age | Tuổi của người tham gia (năm) |
| location | Khu vực sinh sống hoặc nơi khám bệnh |
| race:AfricanAmerican | Thuộc nhóm chủng tộc Người Mỹ gốc Phi (1: Có, 0: Không) |
| race:Asian | Thuộc nhóm chủng tộc Châu Á (1: Có, 0: Không) |
| race:Caucasian | Thuộc nhóm chủng tộc Da trắng (1: Có, 0: Không) |
| race:Hispanic | Thuộc nhóm chủng tộc gốc Tây Ban Nha (1: Có, 0: Không) |
| race:Other | Thuộc nhóm chủng tộc khác (1: Có, 0: Không) |
| hypertension | Tình trạng tăng huyết áp (1: Có, 0: Không) |
| heart_disease | Bệnh tim mạch (1: Có, 0: Không) |
| smoking_history | Tiền sử hút thuốc (Never, Former, Current, Unknown) |
| bmi | Chỉ số khối cơ thể (BMI) - đo độ béo cơ thể (kg/m²) |
| hbA1c_level | Chỉ số HbA1c - phản ánh đường huyết trung bình 3 tháng gần nhất (%) |
| blood_glucose_level | Nồng độ đường huyết hiện tại (mg/dL) |
| diabetes | Tình trạng tiểu đường (1: Có, 0: Không) |
| clinical_notes | Ghi chú lâm sàng hoặc nhận xét của bác sĩ |
# Xuất tóm tắt sau khi phân tổ
summary(data)
## year gender age location
## Min. :2015 Length:99986 Min. : 0.08 Length:99986
## 1st Qu.:2019 Class :character 1st Qu.:24.00 Class :character
## Median :2019 Mode :character Median :43.00 Mode :character
## Mean :2018 Mean :41.89
## 3rd Qu.:2019 3rd Qu.:60.00
## Max. :2022 Max. :80.00
## race:AfricanAmerican race:Asian race:Caucasian race:Hispanic
## Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.0000
## 1st Qu.:0.0000 1st Qu.:0.0000 1st Qu.:0.0000 1st Qu.:0.0000
## Median :0.0000 Median :0.0000 Median :0.0000 Median :0.0000
## Mean :0.2022 Mean :0.2001 Mean :0.1988 Mean :0.1989
## 3rd Qu.:0.0000 3rd Qu.:0.0000 3rd Qu.:0.0000 3rd Qu.:0.0000
## Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.0000
## race:Other hypertension heart_disease smoking_history
## Min. :0.0 Min. :0.00000 Min. :0.00000 Length:99986
## 1st Qu.:0.0 1st Qu.:0.00000 1st Qu.:0.00000 Class :character
## Median :0.0 Median :0.00000 Median :0.00000 Mode :character
## Mean :0.2 Mean :0.07486 Mean :0.03943
## 3rd Qu.:0.0 3rd Qu.:0.00000 3rd Qu.:0.00000
## Max. :1.0 Max. :1.00000 Max. :1.00000
## bmi hbA1c_level blood_glucose_level diabetes
## Min. :10.01 Min. :3.500 Min. : 80.0 Min. :0.00000
## 1st Qu.:23.63 1st Qu.:4.800 1st Qu.:100.0 1st Qu.:0.00000
## Median :27.32 Median :5.800 Median :140.0 Median :0.00000
## Mean :27.32 Mean :5.528 Mean :138.1 Mean :0.08501
## 3rd Qu.:29.58 3rd Qu.:6.200 3rd Qu.:159.0 3rd Qu.:0.00000
## Max. :95.69 Max. :9.000 Max. :300.0 Max. :1.00000
## clinical_notes
## Length:99986
## Class :character
## Mode :character
##
##
##
Kết luận
Hàm summary() cung cấp các chỉ số mô tả cơ bản (min, max, mean, median, quartiles) cho từng biến. Từ đó, ta phát hiện được:
Biến nào có giá trị bất thường (outlier).
Biến nào có thể cần chuẩn hóa trước khi mô hình hóa.
Các đặc điểm như tuổi trung bình, chỉ số BMI trung bình, HbA1c trung bình, v.v.
Hàm cut() giúp biến đổi dữ liệu liên tục thành dữ liệu phân loại (categorical), từ đó dễ dàng thống kê và vẽ biểu đồ. Các ngưỡng chia được chọn dựa trên chuẩn y khoa:
Tuổi: chia 3 nhóm (Trẻ, Trung niên, Cao tuổi).
BMI: chuẩn của Tổ chức Y tế Thế giới (WHO).
HbA1c và Glucose: phân loại theo mức bình thường, tiền tiểu đường và tiểu đường.
Việc phân tổ giúp so sánh tỷ lệ, mô tả và nhận diện nguy cơ trong từng nhóm cụ thể.
library(dplyr)
## Warning: package 'dplyr' was built under R version 4.5.1
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(scales)
library(knitr)
df2 <- data
if("age" %in% names(df2)){
df2 <- df2 %>%
mutate(age_group = cut(age,
breaks = c(-Inf, 17, 29, 44, 59, 74, Inf),
labels = c("0-17", "18-29", "30-44", "45-59", "60-74", "75+")))
age_table <- df2 %>%
group_by(age_group) %>%
summarise(Tan_so = n()) %>%
mutate(Ty_le = percent(Tan_so / sum(Tan_so)))
kable(age_table, caption = "Bảng 2.1. Phân bố mẫu theo nhóm tuổi")
}
| age_group | Tan_so | Ty_le |
|---|---|---|
| 0-17 | 17215 | 17.22% |
| 18-29 | 15214 | 15.22% |
| 30-44 | 19972 | 19.97% |
| 45-59 | 22535 | 22.54% |
| 60-74 | 15944 | 15.95% |
| 75+ | 9106 | 9.11% |
if("bmi" %in% names(df2)){
df2 <- df2 %>%
mutate(bmi_group = case_when(
is.na(bmi) ~ NA_character_,
bmi < 18.5 ~ "Gầy",
bmi < 25 ~ "Bình thường",
bmi < 30 ~ "Thừa cân",
TRUE ~ "Béo phì"))
bmi_table <- df2 %>%
group_by(bmi_group) %>%
summarise(Tan_so = n()) %>%
mutate(Ty_le = percent(Tan_so / sum(Tan_so)))
kable(bmi_table, caption = "Bảng 2.2. Phân bố mẫu theo nhóm BMI")
}
| bmi_group | Tan_so | Ty_le |
|---|---|---|
| Béo phì | 23536 | 23.5% |
| Bình thường | 22219 | 22.2% |
| Gầy | 8494 | 8.5% |
| Thừa cân | 45737 | 45.7% |
# 2.3. Phân nhóm theo mức đường huyết --------------------------------
if("blood_glucose_level" %in% names(df2)){
q <- quantile(df2$blood_glucose_level, probs = c(0, 1/3, 2/3, 1), na.rm = TRUE)
df2 <- df2 %>%
mutate(glucose_level_group = cut(blood_glucose_level,
breaks = unique(q),
include.lowest = TRUE,
labels = c("Thấp", "Trung bình", "Cao")))
glucose_table <- df2 %>%
group_by(glucose_level_group) %>%
summarise(Tan_so = n()) %>%
mutate(Ty_le = percent(Tan_so / sum(Tan_so)))
kable(glucose_table, caption = "Bảng 2.3. Phân bố mẫu theo mức đường huyết")
}
| glucose_level_group | Tan_so | Ty_le |
|---|---|---|
| Thấp | 35838 | 35.8% |
| Trung bình | 37803 | 37.8% |
| Cao | 26345 | 26.3% |
if("hbA1c_level" %in% names(df2)){
df2 <- df2 %>%
mutate(hba1c_group = cut(hbA1c_level,
breaks = c(-Inf, 5.6, 6.4, Inf),
labels = c("Bình thường", "Tiền tiểu đường", "Tiểu đường")))
hba1c_table <- df2 %>%
group_by(hba1c_group) %>%
summarise(Tan_so = n()) %>%
mutate(Ty_le = percent(Tan_so / sum(Tan_so)))
kable(hba1c_table, caption = "Bảng 2.4. Phân bố mẫu theo chỉ số HbA1c")
}
| hba1c_group | Tan_so | Ty_le |
|---|---|---|
| Bình thường | 37851 | 37.9% |
| Tiền tiểu đường | 41343 | 41.3% |
| Tiểu đường | 20792 | 20.8% |
if("gender" %in% names(df2)){
gender_table <- df2 %>%
group_by(gender) %>%
summarise(Tan_so = n()) %>%
mutate(Ty_le = percent(Tan_so / sum(Tan_so)))
kable(gender_table, caption = "Bảng 2.5. Phân bố mẫu theo giới tính")
}
| gender | Tan_so | Ty_le |
|---|---|---|
| Female | 58546 | 59% |
| Male | 41422 | 41% |
| Other | 18 | 0% |
summary(data$age)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.08 24.00 43.00 41.89 60.00 80.00
hist(data$age,
main = "Phân bố độ tuổi của người tham gia",
xlab = "Tuổi",
col = "lightblue",
border = "white")
Kết luận
Biểu đồ histogram cho thấy độ tuổi phân bố rộng, tập trung nhiều ở nhóm 30–60 tuổi.
Đây là độ tuổi có nguy cơ cao xuất hiện tiểu đường loại 2 do lối sống và chuyển hóa.
summary(data$bmi)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 10.01 23.63 27.32 27.32 29.58 95.69
hist(data$bmi,
main = "Phân bố chỉ số BMI",
xlab = "Giá trị BMI",
col = "lightgreen",
border = "white")
# Nếu chưa có cột nhóm tuổi thì tạo nhóm
data$age_group <- cut(data$age,
breaks = c(0, 30, 50, 70, 100),
labels = c("Dưới 30", "30-50", "50-70", "Trên 70"),
include.lowest = TRUE)
# Tính trung bình BMI theo nhóm tuổi
aggregate(bmi ~ age_group, data = data, FUN = mean)
## age_group bmi
## 1 Dưới 30 24.02343
## 2 30-50 29.11149
## 3 50-70 29.52905
## 4 Trên 70 27.62677
Kết luận
BMI trung bình dao động quanh mức 25, tức là thừa cân nhẹ.
Kết quả của aggregate() cho thấy BMI tăng dần theo nhóm tuổi, phản ánh thực tế rằng người lớn tuổi có xu hướng tích tụ mỡ và giảm vận động.
summary(data$hbA1c_level)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 3.500 4.800 5.800 5.528 6.200 9.000
# Tạo nhóm HbA1c (theo mức nguy cơ)
data$hba1c_group <- cut(data$hbA1c_level,
breaks = c(0, 5.6, 6.4, 12),
labels = c("Bình thường", "Tiền tiểu đường", "Tiểu đường"),
include.lowest = TRUE)
# Vẽ boxplot HbA1c theo nhóm
boxplot(data$hbA1c_level ~ data$hba1c_group,
main = "Phân bố chỉ số HbA1c theo nhóm chẩn đoán",
xlab = "Nhóm HbA1c",
ylab = "Giá trị HbA1c",
col = c("skyblue", "orange", "red"))
Biểu đồ boxplot cho thấy rõ ràng ba nhóm HbA1c có khoảng giá trị khác biệt rõ rệt.
Kết luận
Nhóm “Tiểu đường” có giá trị HbA1c trung bình cao nhất, vượt ngưỡng 6.5%.
Nhóm “Tiền tiểu đường” có độ phân tán rộng, thể hiện giai đoạn chuyển tiếp nguy cơ cao cần can thiệp sớm.