Diabetes merupakan penyakit metabolik kronis yang ditandai dengan peningkatan kadar glukosa darah akibat gangguan sekresi insulin maupun resistensi insulin. Deteksi dini terhadap risiko diabetes sangat penting guna mencegah komplikasi jangka panjang seperti penyakit kardiovaskular, gagal ginjal, dan neuropati.
Analisis ini bertujuan untuk membangun model klasifikasi menggunakan regresi logistik dalam memprediksi status diabetes berdasarkan variabel indikator kesehatan. Dataset yang digunakan dalam analisis ini diperoleh dari platform Kaggle yang dapat diakses melalui tautan berikut: https://www.kaggle.com/datasets/lara311/diabetes-dataset-using-many-medical-metrics.
Adapun deskripsi dari variabel yang digunakan adalah sebagai berikut:
library(readxl)
data <- read_excel("C:/Users/HP HP/OneDrive/Documents/College/SMT 6/Komputasi Lanjut/diabetes.xlsx")
str(data)## tibble [768 × 9] (S3: tbl_df/tbl/data.frame)
## $ Pregnancies : num [1:768] 6 1 8 1 0 5 3 10 2 8 ...
## $ Glucose : num [1:768] 148 85 183 89 137 116 78 115 197 125 ...
## $ BloodPressure : num [1:768] 72 66 64 66 40 74 50 0 70 96 ...
## $ SkinThickness : num [1:768] 35 29 0 23 35 0 32 0 45 0 ...
## $ Insulin : num [1:768] 0 0 0 94 168 0 88 0 543 0 ...
## $ BMI : chr [1:768] "33.6" "26.6" "23.3" "28.1" ...
## $ DiabetesPedigreeFunction: chr [1:768] "0.627" "0.351" "0.672" "0.167" ...
## $ Age : num [1:768] 50 31 32 21 33 30 26 29 53 54 ...
## $ Outcome : num [1:768] 1 0 1 0 1 0 1 0 1 1 ...
## Pregnancies Glucose BloodPressure SkinThickness
## Min. : 0.000 Min. : 0.0 Min. : 0.00 Min. : 0.00
## 1st Qu.: 1.000 1st Qu.: 99.0 1st Qu.: 62.00 1st Qu.: 0.00
## Median : 3.000 Median :117.0 Median : 72.00 Median :23.00
## Mean : 3.845 Mean :120.9 Mean : 69.11 Mean :20.54
## 3rd Qu.: 6.000 3rd Qu.:140.2 3rd Qu.: 80.00 3rd Qu.:32.00
## Max. :17.000 Max. :199.0 Max. :122.00 Max. :99.00
## Insulin BMI DiabetesPedigreeFunction Age
## Min. : 0.0 Length:768 Length:768 Min. :21.00
## 1st Qu.: 0.0 Class :character Class :character 1st Qu.:24.00
## Median : 30.5 Mode :character Mode :character Median :29.00
## Mean : 79.8 Mean :33.24
## 3rd Qu.:127.2 3rd Qu.:41.00
## Max. :846.0 Max. :81.00
## Outcome
## Min. :0.000
## 1st Qu.:0.000
## Median :0.000
## Mean :0.349
## 3rd Qu.:1.000
## Max. :1.000
Berdasarkan hasil eksplorasi awal menggunakan fungsi
str() dan summary(), dataset terdiri atas 768
observasi dan 9 variabel. Struktur data menunjukkan bahwa sebagian besar
variabel prediktor bertipe numerik, yaitu Pregnancies, Glucose,
BloodPressure, SkinThickness, Insulin, dan Age.
Namun demikian, variabel BMI dan DiabetesPedigreeFunction masih terbaca sebagai tipe karakter (character), sehingga memerlukan konversi ke numerik sebelum dilakukan analisis lebih lanjut. Sementara itu, variabel Outcome merupakan variabel biner yang merepresentasikan status diabetes.
Rata-rata jumlah kehamilan pada responden adalah sebesar 3.85 dengan nilai maksimum mencapai 17. Variabel Glucose memiliki rata-rata sebesar 120.89 dengan rentang nilai antara 0 hingga 199. Tekanan darah (BloodPressure) memiliki nilai rata-rata sebesar 69.11.
Variabel Glucose, BloodPressure, SkinThickness, dan Insulin menunjukkan nilai minimum sebesar 0. Keberadaan nilai nol pada variabel-variabel medis tersebut secara klinis kurang realistis, sehingga perlu dilakukan penanganan khusus (zero-handling) pada tahap preprocessing.
Rata-rata usia responden adalah 33.24 tahun dengan rentang usia antara 21 hingga 81 tahun. Hal ini menunjukkan bahwa dataset mencakup populasi usia dewasa yang cukup luas.
Berdasarkan variabel Outcome, jumlah responden dengan status positif diabetes (1) adalah sebanyak 268 orang, sedangkan yang tidak menderita diabetes (0) sebanyak 500 orang.
Proporsi responden yang positif diabetes adalah sebesar 34.9%. Hal ini menunjukkan bahwa dataset cenderung tidak didominasi oleh kasus positif, sehingga potensi ketidakseimbangan kelas relatif tidak terlalu signifikan.
data$BMI <- as.numeric(data$BMI)
data$DiabetesPedigreeFunction <- as.numeric(data$DiabetesPedigreeFunction)
str(data)## tibble [768 × 9] (S3: tbl_df/tbl/data.frame)
## $ Pregnancies : num [1:768] 6 1 8 1 0 5 3 10 2 8 ...
## $ Glucose : num [1:768] 148 85 183 89 137 116 78 115 197 125 ...
## $ BloodPressure : num [1:768] 72 66 64 66 40 74 50 0 70 96 ...
## $ SkinThickness : num [1:768] 35 29 0 23 35 0 32 0 45 0 ...
## $ Insulin : num [1:768] 0 0 0 94 168 0 88 0 543 0 ...
## $ BMI : num [1:768] 33.6 26.6 23.3 28.1 43.1 25.6 31 35.3 30.5 0 ...
## $ DiabetesPedigreeFunction: num [1:768] 0.627 0.351 0.672 0.167 2288 ...
## $ Age : num [1:768] 50 31 32 21 33 30 26 29 53 54 ...
## $ Outcome : num [1:768] 1 0 1 0 1 0 1 0 1 1 ...
Berdasarkan hasil pemeriksaan struktur data setelah dilakukan
konversi tipe data menggunakan fungsi as.numeric(),
variabel BMI dan DiabetesPedigreeFunction sebelumnya terbaca sebagai
tipe karakter (character), telah dikenali sebagai tipe numerik, sehingga
dapat digunakan dalam analisis kuantitatif.
Secara keseluruhan, dataset tetap terdiri dari 768 observasi dan 9 variabel, dengan komposisi:
Hasil ini menunjukkan bahwa seluruh variabel dalam dataset telah berada dalam format yang sesuai untuk proses pemodelan selanjutnya.
missing_summary <- data.frame(
Variabel = colnames(data),
Jumlah_Missing = colSums(is.na(data)),
Persentase = round(colSums(is.na(data)) / nrow(data) * 100, 2)
)
missing_summary## Variabel Jumlah_Missing Persentase
## Pregnancies Pregnancies 0 0
## Glucose Glucose 0 0
## BloodPressure BloodPressure 0 0
## SkinThickness SkinThickness 0 0
## Insulin Insulin 0 0
## BMI BMI 0 0
## DiabetesPedigreeFunction DiabetesPedigreeFunction 0 0
## Age Age 0 0
## Outcome Outcome 0 0
Berdasarkan output, total missing value pada dataset adalah sebanyak 0 observasi dari total 768 data.
Secara umum, seluruh variabel menunjukkan jumlah missing value sebesar 0, yang berarti tidak terdapat missing value secara eksplisit dalam dataset. Dengan demikian, dataset secara teknis lengkap dan dapat langsung digunakan untuk tahap analisis berikutnya.
cols_zero <- c("Glucose","BloodPressure","SkinThickness","Insulin","BMI")
zero_count <- sapply(data[cols_zero], function(x) sum(x == 0, na.rm = TRUE))
zero_summary <- data.frame(
Variabel = cols_zero,
Jumlah_Nilai_0 = zero_count
)
zero_summary## Variabel Jumlah_Nilai_0
## Glucose Glucose 5
## BloodPressure BloodPressure 35
## SkinThickness SkinThickness 227
## Insulin Insulin 374
## BMI BMI 11
Pada tahap eksplorasi awal, ditemukan bahwa beberapa variabel medis seperti Glucose, BloodPressure, SkinThickness, Insulin, dan BMI memiliki nilai minimum sebesar 0. Secara klinis, nilai nol pada variabel-variabel tersebut tidak realistis karena:
Karena nilai nol tersebut tidak mencerminkan kondisi biologis yang valid, maka nilai-nilai tersebut diasumsikan sebagai implicit missing values dan selanjutnya perlu dilakukan penanganan.
cols_zero <- c("Glucose","BloodPressure","SkinThickness","Insulin","BMI")
# Ganti 0 menjadi NA
for(col in cols_zero){
data[[col]][data[[col]] == 0] <- NA
}
# Imputasi dengan median
for(col in cols_zero){
data[[col]][is.na(data[[col]])] <- median(data[[col]], na.rm = TRUE)
}Setelah nilai nol dikonversi menjadi NA, dilakukan imputasi menggunakan median dari masing-masing variabel. Pemilihan median sebagai metode imputasi didasarkan pada sifatnya yang robust terhadap outlier dan distribusi data yang cenderung tidak simetris.
Secara statistik, penggunaan median membantu mempertahankan distribusi pusat data tanpa terlalu dipengaruhi oleh nilai ekstrem. Dengan demikian, proses imputasi ini diharapkan dapat meningkatkan kualitas data tanpa menimbulkan distorsi signifikan terhadap pola distribusi aslinya.
cols_zero <- c("Glucose","BloodPressure","SkinThickness","Insulin","BMI")
zero_count <- sapply(data[cols_zero], function(x) sum(x == 0, na.rm = TRUE))
zero_summary <- data.frame(
Variabel = cols_zero,
Jumlah_Nilai_0 = zero_count
)
zero_summary## Variabel Jumlah_Nilai_0
## Glucose Glucose 0
## BloodPressure BloodPressure 0
## SkinThickness SkinThickness 0
## Insulin Insulin 0
## BMI BMI 0
Setelah dilakukan penanganan terhadap nilai nol sebagai missing value terselubung dan proses imputasi, dilakukan pemeriksaan ulang terhadap variabel-variabel terkait. Hasil verifikasi menunjukkan bahwa jumlah nilai nol pada variabel Glucose, BloodPressure, SkinThickness, Insulin, dan BMI telah menjadi 0, yang mengindikasikan bahwa proses pembersihan data telah berhasil dilakukan.
# Daftar variabel numerik prediktor
predictors <- c("Pregnancies","Glucose","BloodPressure",
"SkinThickness","Insulin","BMI",
"DiabetesPedigreeFunction","Age")
# Fungsi untuk menghitung jumlah outlier metode IQR
check_outlier <- function(x){
Q1 <- quantile(x, 0.25, na.rm = TRUE)
Q3 <- quantile(x, 0.75, na.rm = TRUE)
IQR_val <- Q3 - Q1
lower_bound <- Q1 - 1.5 * IQR_val
upper_bound <- Q3 + 1.5 * IQR_val
sum(x < lower_bound | x > upper_bound, na.rm = TRUE)
}
# Hitung jumlah outlier tiap variabel
outlier_count <- sapply(data[predictors], check_outlier)
outlier_count## Pregnancies Glucose BloodPressure
## 4 0 14
## SkinThickness Insulin BMI
## 87 346 8
## DiabetesPedigreeFunction Age
## 51 9
Berdasarkan metode Interquartile Range (IQR), jumlah observasi yang teridentifikasi sebagai outlier pada masing-masing variabel adalah sebagai berikut:
Variabel dengan jumlah outlier terbesar adalah Insulin sebanyak 346 observasi.
Keberadaan outlier terutama pada variabel seperti Insulin dan DiabetesPedigreeFunction dapat disebabkan oleh distribusi data yang cenderung skewed. Dalam konteks medis, nilai ekstrem tidak selalu mencerminkan kesalahan pencatatan, melainkan dapat merepresentasikan kondisi klinis tertentu.
Penanganan outlier dilakukan menggunakan pendekatan winsorizing agar tidak menghilangkan informasi penting dari observasi yang tersedia.
# Fungsi winsorizing berdasarkan IQR
winsorize_iqr <- function(x){
Q1 <- quantile(x, 0.25, na.rm = TRUE)
Q3 <- quantile(x, 0.75, na.rm = TRUE)
IQR_val <- Q3 - Q1
lower_bound <- Q1 - 1.5 * IQR_val
upper_bound <- Q3 + 1.5 * IQR_val
x[x < lower_bound] <- lower_bound
x[x > upper_bound] <- upper_bound
return(x)
}
# Terapkan ke semua predictor
data_winsor <- data
for(col in predictors){
data_winsor[[col]] <- winsorize_iqr(data[[col]])
}## Pregnancies Glucose BloodPressure
## 0 0 0
## SkinThickness Insulin BMI
## 0 0 0
## DiabetesPedigreeFunction Age
## 0 0
Setelah dilakukan winsorizing berbasis IQR, jumlah outlier pada masing-masing variabel menjadi:
Hasil tersebut menunjukkan bahwa nilai ekstrem telah dibatasi pada rentang yang lebih wajar tanpa menghilangkan observasi. Pendekatan ini mempertahankan ukuran sampel tetap 768 observasi, sehingga stabilitas model tetap terjaga.
Pendekatan winsorizing dipilih dibandingkan penghapusan data karena pada dataset medis, nilai ekstrem sering kali mencerminkan variasi biologis nyata dan bukan kesalahan pencatatan. Dengan demikian, proses ini membantu meningkatkan robustitas model tanpa mengurangi representativitas data.
## # A tibble: 768 × 9
## Pregnancies Glucose BloodPressure SkinThickness Insulin BMI
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 6 148 72 35 125 33.6
## 2 1 85 66 29 125 26.6
## 3 8 183 64 29 125 23.3
## 4 1 89 66 23 113. 28.1
## 5 0 137 40 35 136. 43.1
## 6 5 116 74 29 125 25.6
## 7 3 78 50 32 113. 31
## 8 10 115 72 29 125 35.3
## 9 2 197 70 42.5 136. 30.5
## 10 8 125 96 29 125 32.3
## # ℹ 758 more rows
## # ℹ 3 more variables: DiabetesPedigreeFunction <dbl>, Age <dbl>, Outcome <dbl>
set.seed(123)
n <- nrow(data)
index <- sample(1:n, size = 0.7*n)
train <- data[index, ]
test <- data[-index, ]
cat("Jumlah data training:", nrow(train), "\n")## Jumlah data training: 537
## Jumlah data testing : 231
Data dibagi menjadi data training sebanyak 537 observasi (70%) dan data testing sebanyak 231 observasi (30%). Pembagian ini bertujuan untuk mengevaluasi kemampuan generalisasi model terhadap data yang tidak digunakan dalam proses pelatihan.
normalize <- function(x, min_val, max_val){
(x - min_val) / (max_val - min_val)
}
predictors <- c("Pregnancies","Glucose","BloodPressure",
"SkinThickness","Insulin","BMI",
"DiabetesPedigreeFunction","Age")
train_norm <- train
test_norm <- test
for(col in predictors){
min_val <- min(train[[col]])
max_val <- max(train[[col]])
train_norm[[col]] <- normalize(train[[col]], min_val, max_val)
test_norm[[col]] <- normalize(test[[col]], min_val, max_val)
}Normalisasi dilakukan menggunakan metode Min-Max Scaling pada rentang [0,1] berdasarkan parameter data training. Pendekatan ini digunakan untuk memastikan seluruh variabel berada pada skala yang sebanding sehingga mencegah dominasi variabel dengan skala besar dalam proses estimasi model.
Secara umum, model regresi logistik dinyatakan dalam bentuk fungsi
logit sebagai berikut: \[
\ln \left(\frac{p}{1-p}\right) = \beta_0 + \beta_1 X_1 + \beta_2 X_2 +
\cdots + \beta_k X_k
\] dengan: - \(p\) :
probabilitas kejadian (Outcome = 1)
- \(\beta_0\) : intersep
- \(\beta_k\) : koefisien regresi
variabel ke-k
- \(X_k\) : variabel prediktor ke-k
model_log <- glm(Outcome ~ Pregnancies + Glucose + BloodPressure +
SkinThickness + Insulin + BMI +
DiabetesPedigreeFunction + Age,
data = train_norm,
family = binomial)
summary(model_log)##
## Call:
## glm(formula = Outcome ~ Pregnancies + Glucose + BloodPressure +
## SkinThickness + Insulin + BMI + DiabetesPedigreeFunction +
## Age, family = binomial, data = train_norm)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -5.5241 0.5273 -10.475 < 2e-16 ***
## Pregnancies 1.3504 0.5231 2.581 0.00984 **
## Glucose 5.1473 0.6684 7.701 1.35e-14 ***
## BloodPressure -0.4135 0.6720 -0.615 0.53831
## SkinThickness 0.3086 0.5141 0.600 0.54826
## Insulin 0.2490 0.3564 0.699 0.48475
## BMI 2.9714 0.7039 4.221 2.43e-05 ***
## DiabetesPedigreeFunction 0.9110 0.4207 2.166 0.03035 *
## Age 0.6624 0.5303 1.249 0.21163
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 694.17 on 536 degrees of freedom
## Residual deviance: 494.29 on 528 degrees of freedom
## AIC: 512.29
##
## Number of Fisher Scoring iterations: 5
Berdasarkan hasil estimasi, diperoleh model regresi logistik sebagai berikut: \[ \ln \left(\frac{p}{1-p}\right) = -5.5241 + 1.3504 Pregnancies + 5.1473 Glucose -0.4135 BloodPressure + 0.3086 SkinThickness + 0.249 Insulin + 2.9714 BMI + 0.911 DiabetesPedigreeFunction + 0.6624 Age \] Berdasarkan hasil estimasi regresi logistik, model menghasilkan koefisien untuk masing-masing variabel prediktor. Nilai intercept sebesar -5.5241 merepresentasikan log-odds kejadian diabetes ketika seluruh variabel prediktor bernilai nol. Variabel yang memiliki pengaruh signifikan dapat diidentifikasi berdasarkan nilai p-value (< 0.05). Jumlah variabel signifikan dalam model ini adalah sebanyak 4 variabel. Secara umum, koefisien bernilai positif menunjukkan bahwa peningkatan variabel tersebut meningkatkan peluang terjadinya diabetes, sedangkan koefisien negatif menunjukkan hubungan sebaliknya.
library(ggplot2)
pred_prob <- predict(model_log, newdata = test_norm, type = "response")
pred_class <- ifelse(pred_prob > 0.5, 1, 0)
pred_class <- factor(pred_class, levels = c(0,1))
MC <- table(Aktual = test_norm$Outcome, Prediksi = pred_class)
cm_df <- as.data.frame(MC)
colnames(cm_df) <- c("Aktual", "Prediksi", "Jumlah")
ggplot(cm_df, aes(x = Prediksi, y = Aktual, fill = Jumlah)) +
geom_tile(color = "white", linewidth = 0.7) +
geom_text(aes(label = Jumlah), size = 6, fontface = "bold") +
scale_fill_gradient(low = "#c6dbef", high = "#08306b") +
labs(title = "Confusion Matrix Regresi Logistik",
x = "Kelas Prediksi",
y = "Kelas Aktual",
fill = "Frekuensi") +
theme_minimal(base_size = 14) +
theme(plot.title = element_text(hjust = 0.5, face = "bold"))
Berdasarkan confusion matrix, model menghasilkan:
True Positif (TP) : 132 pasien yang diperkirakan tidak terkena diabetes (normal) terbukti dapat diprediksi dengan benar.
True Negatif (TN) : 45 pasien yang diperkirakan terkena diabetes terbukti dapat diprediksi dengan benar.
False Positif (FP) : Terjadi kesalahan prediksi, yaitu sebanyak 36 pasien yang diperkirakan tidak terkena diabetes (normal), tetapi sebenarnya terkena diabetes.
False Negatif (FN) : Terjadi kesalahan prediksi, yaitu sebanyak 18 pasien yang diperkirakan terkena diabetes, tetapi sebenarnya tidak terkena diabetes (normal).
TP <- MC["0","0"]
TN <- MC["1","1"]
FP <- MC["1","0"]
FN <- MC["0","1"]
accuracy <- (TP + TN) / sum(MC)
precision <- TP / (TP + FP)
recall <- TP / (TP + FN)
f1_score <- 2 * (precision * recall) / (precision + recall)
cat("Accuracy :", round(accuracy*100,2), "%\n")## Accuracy : 76.62 %
## Precision : 78.57 %
## Recall : 88 %
## F1-Score : 83.02 %
Evaluasi kinerja model dilakukan menggunakan metrik accuracy, precision, recall, dan F1-score berdasarkan confusion matrix yang diperoleh. Hasil evaluasi menunjukkan bahwa model memiliki nilai accuracy sebesar 76.62%, yang menunjukkan proporsi prediksi yang benar terhadap keseluruhan data uji.
Nilai precision sebesar 78.57% menunjukkan bahwa sebagian besar prediksi positif yang dihasilkan model merupakan kasus positif yang benar. Sementara itu, nilai recall sebesar 88% menunjukkan kemampuan model dalam mengidentifikasi kasus positif secara benar dari seluruh kasus positif yang ada.
Nilai F1-score sebesar 83.02% menggambarkan keseimbangan antara precision dan recall dalam kinerja model klasifikasi.
## Warning: package 'pROC' was built under R version 4.3.3
## Type 'citation("pROC")' for a citation.
##
## Attaching package: 'pROC'
## The following objects are masked from 'package:stats':
##
## cov, smooth, var
## Setting levels: control = 0, case = 1
## Setting direction: controls < cases
plot(roc_obj,
col = "#08306b",
lwd = 3,
main = "ROC Curve Regresi Logistik",
legacy.axes = TRUE)
text(0.6, 0.2,
paste("AUC =", round(auc_value,3)),
cex = 1.2)
Berdasarkan kurva ROC yang ditampilkan, garis kurva model berada di atas
garis diagonal yang merepresentasikan klasifikasi acak (random
classifier). Hal ini menunjukkan bahwa model regresi logistik
memiliki kemampuan yang lebih baik dibandingkan tebakan acak dalam
membedakan antara individu yang memiliki risiko diabetes dan yang
tidak.
Nilai Area Under Curve (AUC) sebesar 0.84 menunjukkan kemampuan model dalam membedakan kelas positif dan negatif. Nilai tersebut mengindikasikan bahwa model memiliki kemampuan diskriminatif yang baik.
Semakin mendekati sudut kiri atas pada grafik ROC, semakin baik kemampuan model dalam mencapai tingkat true positive rate yang tinggi dengan false positive rate yang rendah. Dengan nilai AUC yang diperoleh, model dapat dikatakan memiliki performa yang baik dalam melakukan klasifikasi.
Analisis ini bertujuan untuk membangun model klasifikasi diabetes menggunakan regresi logistik berdasarkan variabel klinis pasien. Proses analisis diawali dengan tahapan preprocessing yang meliputi penanganan nilai nol sebagai missing value, imputasi median, deteksi dan penanganan outlier menggunakan metode winsorizing, serta normalisasi data untuk meningkatkan stabilitas estimasi model.
Berdasarkan hasil estimasi, model regresi logistik mampu mengidentifikasi hubungan antara variabel prediktor dan probabilitas kejadian diabetes. Evaluasi kinerja model menunjukkan bahwa model memiliki tingkat akurasi sebesar 0.7662, dengan nilai precision sebesar 0.7857 dan recall sebesar 0.88. Selain itu, nilai F1-Score sebesar 0.8302 menunjukkan keseimbangan antara precision dan recall dalam mengukur kemampuan model dalam mengklasifikasikan kasus diabetes.
Selain itu, nilai Area Under Curve (AUC) sebesar 0.8395 mengindikasikan bahwa model memiliki kemampuan diskriminatif yang baik dalam membedakan kelas positif dan negatif secara keseluruhan tanpa bergantung pada satu nilai threshold tertentu.
Secara umum, model regresi logistik yang dibangun telah mampu memberikan performa klasifikasi yang cukup baik dalam membedakan individu yang berisiko diabetes dan yang tidak. Meskipun demikian, performa model masih dapat ditingkatkan melalui eksplorasi metode klasifikasi lain atau penambahan variabel prediktor yang lebih komprehensif.