## Warning: package 'readxl' was built under R version 4.5.1

1. Thông tin cơ bản về dữ liệu

1.1 Kích thước bộ dữ liệu

dim(data)
## [1] 100000     17

Kết luận

Bộ dữ liệu gồm 100.000 quan sát và 17 biến

1.2 Tên các 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"

1.3 Số lượng biến định tính và định lượng

Số biến định lượng

sum(sapply(data, is.numeric)) 
## [1] 13

Số biến định tính

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

1.4 Kiểm tra dòng trùng lặp

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.

1.5 Kiểm tra giá trị bị thiếu

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.

1.6 Ý nghĩa của các biến

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ĩ

1.7 TÓM TẮT VÀ KIỂM TRA TỔNG QUAN DỮ LIỆU**

# 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.

2. Phân tổ các biến

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:

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

2.1. Phân nhóm theo độ tuổi

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")
}
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%

2.2 Phân tổ theo nhóm BMI

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")
}
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

# 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")
}
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%

2.4 Phân nhóm theo chỉ số HbA1c

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")
}
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%

2.5 Phân nhóm theo giới tính

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")
}
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%

3. Phân tích các biến

3.1 Phân tích biến Tuổi (Age)

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.

3.2 Phân tích biến BMI

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.

3.3 Phân tích biến HbA1c (đường huyết trung bình 3 thá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.