PHẦN 1: GIỚI THIỆU & THIẾT LẬP MÔI TRƯỜNG Chào mừng các bạn đến với Buổi 2! Hôm nay chúng ta sẽ học cách làm việc với dữ liệu lâm sàng thực tế. Dữ liệu thực tế thường không hề hoàn hảo: nó chứa đầy lỗi nhập liệu, giá trị khuyết (missing data), và những con số phi lý. Nhiệm vụ của chúng ta là dùng ngôn ngữ R để “khám bệnh” cho bộ dữ liệu này và “chữa” nó trước khi chạy bất kỳ mô hình thống kê nào. Trong nghiên cứu y khoa chuẩn Q1, tính tái lập (Reproducibility) là bắt buộc . File R Markdown này chính là một báo cáo tái lập: nó lưu lại mọi bước tư duy và thao tác của bạn. Cài đặt và Gọi thư viện (Packages) Thay vì dùng hàm library() lặp đi lặp lại cho từng gói, chúng ta sẽ dùng siêu công cụ pacman để tự động cài đặt (nếu chưa có) và tải các gói cần thiết chỉ với 1 dòng lệnh
# Cài đặt pacman nếu máy chưa có
if (!require("pacman")) install.packages("pacman")
# pacman::p_load sẽ tự động tìm, cài và gọi các packages
pacman::p_load(
tidyverse, # Chứa dplyr, ggplot2 để xử lý và vẽ biểu đồ
rio, # Siêu công cụ nhập dữ liệu (Excel, csv, SPSS)
gtsummary, # Lập bảng Table 1 chuẩn y khoa
naniar # Thăm dò dữ liệu khuyết (NA)
)
| PHẦN 2: IMPORT DỮ LIỆU VÀO R Thay vì dùng nhiều hàm khác nhau cho từng loại đuôi file (.csv, .xlsx, .sav), chúng ta sử dụng hàm import() từ package rio. Hàm này đủ thông minh để tự nhận diện đuôi file và đọc chính xác dữ liệu . Mẹo kỹ thuật: Đường dẫn trong Windows dùng dấu , nhưng trong R, bạn phải đổi thành / hoặc \ để R hiểu đó là đường dẫn |
|---|
| PHẦN 3: KHÁM PHÁ DỮ LIỆU (EXPLORATORY DATA ANALYSIS)
Trước khi can thiệp, bác sĩ phải khám bệnh. Trước khi xử lý (wrangling),
nhà khoa học dữ liệu phải thăm dò (exploration). 3.1. Nhìn tổng quan cấu
trúc Chúng ta dùng hàm glimpse() (nhìn lướt qua) để xem máy tính đang
“hiểu” dữ liệu của chúng ta ra sao. glimpse(df_raw) Nhận xét lâm sàng:
Biến Gender đang được máy tính hiểu là số ( |
PHẦN 4: LÀM SẠCH VÀ NHÀO NẶN DỮ LIỆU (DATA WRANGLING) Đây là lúc chúng ta dùng hệ sinh thái dplyr với toán tử đường ống %>% (đọc là “và sau đó…”). Cấu trúc này giúp code R đọc mượt mà như một câu tiếng Anh , . Chúng ta sẽ dùng hàm mutate() để thay đổi và tạo mới biến số.
df_clean <- df_raw %>%
mutate(
# 1. Ép kiểu biến Gender từ 0/1 sang biến phân loại (Factor)
Gender = factor(Gender,
levels = c(0, 1),
labels = c("Male", "Female")),
Treatment = as.factor(Treatment),
Outcome = factor(Outcome, levels=c(0,1), labels=c("Fail", "Success")),
# 2. Xử lý Outlier sinh học: Nếu Age == 999 thì biến thành NA, ngược lại giữ nguyên Age
Age = ifelse(Age == 999, NA, Age),
# 3. Xử lý Outlier BMI: Chỉ chấp nhận BMI từ 10 đến 60, nằm ngoài khoảng này cho thành NA
BMI = ifelse(BMI < 10 | BMI > 60, NA, BMI)
)
# Kiểm tra lại sau khi dọn dẹp
summary(df_clean)
## Patient_ID Age Gender BMI
## Length:1000 Min. :18.00 Male :520 Min. :10.30
## Class :character 1st Qu.:47.00 Female:480 1st Qu.:22.50
## Mode :character Median :55.00 Median :25.00
## Mean :55.16 Mean :24.96
## 3rd Qu.:62.00 3rd Qu.:27.50
## Max. :96.00 Max. :35.10
## NA's :60
## Cholesterol Treatment Outcome
## Min. :1.860 Drug_A:498 Fail :405
## 1st Qu.:4.350 Drug_B:502 Success:595
## Median :5.090
## Mean :5.114
## 3rd Qu.:5.860
## Max. :8.390
## NA's :80
Ghi chú: Bạn có thể thấy các giá trị 999 và 150.5 đã biến mất và được chuyển thành NA (Missing).
| PHẦN 5: BẢN CHẤT VÀ CÁCH XỬ LÝ DỮ LIỆU KHUYẾT (MISSING DATA) Dữ liệu khuyết không chỉ đơn thuần là những ô trống. Theo lý thuyết dịch tễ học, chúng được chia làm 3 loại cơ chế: MCAR (Missing Completely At Random): Mất ngẫu nhiên hoàn toàn. Ví dụ: Rớt vỡ ống nghiệm sinh hóa. MAR (Missing At Random): Mất ngẫu nhiên có điều kiện. Ví dụ: Phụ nữ thường ngại khai báo cân nặng hơn nam giới (phụ thuộc vào biến Gender). MNAR (Missing Not At Random): Mất không ngẫu nhiên. Ví dụ: Bệnh nhân béo phì giấu chỉ số BMI của mình. 5.1 Xử lý bằng cách Xóa (Listwise Deletion) Nếu tỉ lệ Missing < 5% và cơ chế là MCAR, chúng ta có thể an tâm xóa bỏ các dòng chứa NA bằng lệnh drop_na(). |
r # Tạo một tập data mới bằng cách loại bỏ những bệnh nhân thiếu Cholesterol df_complete <- df_clean %>% drop_na(Cholesterol) # Từ 1000 bệnh nhân, ta chỉ còn lại bao nhiêu? nrow(df_complete) |
## [1] 920 |
| 5.2 Xử lý bằng cách Điền khuyết (Imputation) CẢNH BÁO HỌC THUẬT QUAN TRỌNG: Dưới đây tôi hướng dẫn các bạn cách dùng giá trị Trung bình (Mean Imputation) để điền vào ô trống. Tuy nhiên, trong các bài báo Q1, TUYỆT ĐỐI KHÔNG DÙNG CÁCH NÀY. Việc điền số trung bình sẽ bóp méo phương sai và phá vỡ mối tương quan sinh học thật sự của dữ liệu. Thay vào đó, giới hàn lâm bắt buộc dùng Multiple Imputation (ví dụ gói mice). |
| ``` r df_imputed <- df_clean %>% mutate( # Nếu BMI bị khuyết, điền số trung bình của toàn bộ cột BMI vào, nếu không thì giữ nguyên BMI_filled = ifelse(is.na(BMI), mean(BMI, na.rm = TRUE), BMI) ) |
| # So sánh sự thay đổi của cột BMI gốc và BMI đã điền khuyết summary(df_imputed %>% select(BMI, BMI_filled)) ``` |
## BMI BMI_filled ## Min. :10.30 Min. :10.30 ## 1st Qu.:22.50 1st Qu.:22.70 ## Median :25.00 Median :24.96 ## Mean :24.96 Mean :24.96 ## 3rd Qu.:27.50 3rd Qu.:27.30 ## Max. :35.10 Max. :35.10 ## NA's :60 |
PHẦN 6: BÁO CÁO KẾT QUẢ ĐẦU RA (TABLE 1) Sau khi bộ dữ liệu đã được làm sạch chuẩn mực, phần thưởng của chúng ta là bảng Table 1 (Baseline Characteristics). Với package gtsummary, chúng ta chỉ cần 2 dòng lệnh thay vì mất hàng giờ ngồi đếm thủ công trên SPSS hay Excel.
df_clean %>%
# Bỏ cột Patient_ID vì không có ý nghĩa thống kê
select(-Patient_ID) %>%
# Tạo bảng tóm tắt, phân tầng theo kết cục điều trị (Outcome)
tbl_summary(
by = Outcome,
missing = "no", # Ẩn các dòng hiển thị số NA để bảng gọn hơn
statistic = list(all_continuous() ~ "{mean} ({sd})") # Chỉnh định dạng Mean (SD)
) %>%
add_p() %>% # Tự động chạy T-test hoặc Chi-square để so sánh 2 nhóm
bold_labels() %>%
modify_caption("**Bảng 1: Đặc điểm lâm sàng nền của bệnh nhân theo kết cục điều trị**")
| Characteristic | Fail N = 4051 |
Success N = 5951 |
p-value2 |
|---|---|---|---|
| Age | 55 (12) | 55 (12) | 0.5 |
| Gender | 0.8 | ||
| Male | 209 (52%) | 311 (52%) | |
| Female | 196 (48%) | 284 (48%) | |
| BMI | 24.9 (3.7) | 25.0 (3.9) | 0.7 |
| Cholesterol | 5.00 (1.04) | 5.19 (1.15) | 0.005 |
| Treatment | 0.2 | ||
| Drug_A | 211 (52%) | 287 (48%) | |
| Drug_B | 194 (48%) | 308 (52%) | |
| 1 Mean (SD); n (%) | |||
| 2 Wilcoxon rank sum test; Pearson’s Chi-squared test | |||