Berikut adalah package yang digunakan dalam analisis ini. Package
caret digunakan untuk proses pemodelan dan evaluasi,
tidyverse dan dplyr untuk manipulasi data,
serta e1071 sebagai dependensi algoritma klasifikasi.
library(readxl)
library(caret)
library(tidyverse)
library(dplyr)
library(e1071)
Dataset yang digunakan adalah “Heart Attack Prediction in Indonesia” yang bersumber dari platform Kaggle. Dataset ini terdiri dari 158.355 data rekam kesehatan individu dengan 27 variabel independen dan 1 variabel dependen berupa status serangan jantung (heart_attack) dengan kategori positif (1) dan negatif (0).
Datajantung <- read_excel("C:/Users/User/Downloads/heart_attack_prediction_indonesia.xlsx"
)
cat("Dimensi dataset:", nrow(Datajantung), "baris dan", ncol(Datajantung), "kolom\n")
## Dimensi dataset: 158355 baris dan 28 kolom
head(Datajantung)
## # A tibble: 6 × 28
## age gender region income_level hypertension diabetes cholesterol_level
## <dbl> <chr> <chr> <chr> <dbl> <dbl> <dbl>
## 1 60 Male Rural Middle 0 1 211
## 2 53 Female Urban Low 0 0 208
## 3 62 Female Urban Low 0 0 231
## 4 73 Male Urban Low 1 0 202
## 5 52 Male Urban Middle 1 0 232
## 6 52 Male Urban Middle 0 0 231
## # ℹ 21 more variables: obesity <dbl>, waist_circumference <dbl>,
## # family_history <dbl>, smooking_status <chr>, alcohol_consumption <chr>,
## # physical_activity <chr>, dietary_habits <chr>,
## # air_pollution_exposure <chr>, stress_level <chr>, sleep_hours <dbl>,
## # blood_pressure_systolic <dbl>, blood_pressure_diastolic <dbl>,
## # fasting_blood_sugar <dbl>, cholesterol_hdl <dbl>, cholesterol_ldl <dbl>,
## # triglycerides <dbl>, EKG_results <chr>, previous_heart_disease <dbl>, …
Mengingat jumlah data yang sangat besar (158.355 data) sehingga memerlukan komputasi yang berat, maka dilakukan pengambilan sampel menggunakan rumus Slovin dengan tingkat kesalahan sebesar 1% sebagai berikut:
\[n = \frac{N}{1 + N(e)^2} = \frac{158.355}{1 + 158.355(0,01)^2} = \frac{158.355}{16,8355} \approx 9.407 \text{ data}\]
Berdasarkan perhitungan tersebut, diperoleh jumlah sampel sebanyak 9.407 data yang diambil secara acak (random sampling) dari keseluruhan dataset untuk memastikan sampel yang diperoleh bersifat representatif terhadap populasi data.
set.seed(36)
sample_index <- sample(
1:nrow(Datajantung),
9407
)
Data_sample <- Datajantung[sample_index, ]
cat("Jumlah sampel:", nrow(Data_sample), "\n")
## Jumlah sampel: 9407
Seluruh variabel kategorikal dikonversi ke dalam tipe data
factor agar dapat diproses dengan benar oleh algoritma
klasifikasi. Variabel yang dikonversi meliputi variabel demografis,
klinis, gaya hidup, serta variabel target heart_attack.
selected_data_preprocessed_factors <- Data_sample %>%
mutate(
gender = factor(gender),
region = factor(region),
income_level = factor(income_level),
hypertension = factor(hypertension),
diabetes = factor(diabetes),
obesity = factor(obesity),
family_history = factor(family_history),
smooking_status = factor(smooking_status),
alcohol_consumption = factor(alcohol_consumption),
physical_activity = factor(physical_activity),
dietary_habits = factor(dietary_habits),
air_pollution_exposure = factor(air_pollution_exposure),
stress_level = factor(stress_level),
EKG_results = factor(EKG_results),
previous_heart_disease = factor(previous_heart_disease),
medication_usage = factor(medication_usage),
participated_in_free_screening = factor(participated_in_free_screening),
heart_attack = factor(heart_attack)
)
glimpse(selected_data_preprocessed_factors)
## Rows: 9,407
## Columns: 28
## $ age <dbl> 64, 44, 36, 75, 46, 49, 57, 72, 67, 47,…
## $ gender <fct> Male, Female, Female, Male, Female, Fem…
## $ region <fct> Urban, Urban, Urban, Urban, Urban, Urba…
## $ income_level <fct> Low, Middle, Low, Middle, Low, Middle, …
## $ hypertension <fct> 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, …
## $ diabetes <fct> 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, …
## $ cholesterol_level <dbl> 261, 230, 292, 217, 199, 165, 193, 243,…
## $ obesity <fct> 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, …
## $ waist_circumference <dbl> 105, 108, 93, 100, 80, 115, 117, 91, 85…
## $ family_history <fct> 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, …
## $ smooking_status <fct> Past, Never, Never, Never, Never, Past,…
## $ alcohol_consumption <fct> None, None, None, None, High, None, Non…
## $ physical_activity <fct> Moderate, Low, Moderate, Low, Low, Low,…
## $ dietary_habits <fct> Unhealthy, Unhealthy, Healthy, Unhealth…
## $ air_pollution_exposure <fct> Low, Low, Moderate, Moderate, Moderate,…
## $ stress_level <fct> Moderate, Moderate, Moderate, Moderate,…
## $ sleep_hours <dbl> 5.504516, 6.668030, 8.058486, 7.335143,…
## $ blood_pressure_systolic <dbl> 125, 138, 114, 130, 148, 145, 129, 120,…
## $ blood_pressure_diastolic <dbl> 74, 93, 76, 98, 82, 88, 87, 75, 76, 92,…
## $ fasting_blood_sugar <dbl> 116, 111, 84, 147, 85, 138, 176, 96, 70…
## $ cholesterol_hdl <dbl> 46, 66, 44, 46, 51, 46, 40, 45, 46, 34,…
## $ cholesterol_ldl <dbl> 185, 121, 107, 156, 176, 70, 109, 50, 1…
## $ triglycerides <dbl> 145, 243, 140, 192, 107, 178, 223, 125,…
## $ EKG_results <fct> Abnormal, Normal, Normal, Normal, Norma…
## $ previous_heart_disease <fct> 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, …
## $ medication_usage <fct> 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, …
## $ participated_in_free_screening <fct> 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, …
## $ heart_attack <fct> 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, …
Variabel target (dependent variable) yang digunakan adalah
heart_attack, sedangkan seluruh variabel lainnya digunakan
sebagai fitur (independent variable) dalam proses
pemodelan.
y <- selected_data_preprocessed_factors$heart_attack
X <- selected_data_preprocessed_factors %>%
dplyr::select(-heart_attack)
cat("Jumlah fitur:", ncol(X), "\n")
## Jumlah fitur: 27
cat("Jumlah data :", nrow(X), "\n")
## Jumlah data : 9407
Dataset dibagi menjadi dua bagian yang saling independen, yaitu 80% data training untuk membangun model dan 20% data testing untuk mengevaluasi performa model. Pembagian ini bertujuan untuk mengurangi potensi bias serta menghindari terjadinya overfitting pada saat evaluasi model.
set.seed(36)
train_index <- createDataPartition(
y,
p = 0.8,
list = FALSE
)
train_data <- selected_data_preprocessed_factors[train_index, ]
test_data <- selected_data_preprocessed_factors[-train_index, ]
cat("Jumlah data training:", nrow(train_data), "\n")
## Jumlah data training: 7526
cat("Jumlah data testing :", nrow(test_data), "\n")
## Jumlah data testing : 1881
Pemeriksaan proporsi kelas target dilakukan untuk memastikan distribusi kelas pada data training dan testing cukup seimbang, sehingga model tidak condong (bias) terhadap salah satu kelas.
cat("Proporsi heart_attack pada data training:\n")
## Proporsi heart_attack pada data training:
prop.table(table(train_data$heart_attack))
##
## 0 1
## 0.5932766 0.4067234
cat("\nProporsi heart_attack pada data testing:\n")
##
## Proporsi heart_attack pada data testing:
prop.table(table(test_data$heart_attack))
##
## 0 1
## 0.5933014 0.4066986
Metode validasi yang digunakan adalah 5-Fold Cross Validation, yaitu dataset training dibagi menjadi 5 bagian (fold) secara bergantian sebagai data validasi, sementara 4 bagian lainnya digunakan sebagai data latih. Metode ini bertujuan untuk menghasilkan evaluasi model yang lebih stabil dan konsisten serta meminimalkan kemungkinan terjadinya overfitting.
train_control <- trainControl(
method = "cv",
number = 5
)
Model KNN dilatih menggunakan Grid Search Cross Validation untuk mencari nilai K yang paling optimal. Rentang nilai K yang diuji adalah K = 1 hingga K = 97 dengan interval 2 (bilangan ganjil) guna menghindari hasil tie dalam proses voting. Selain itu, fitur dinormalisasi terlebih dahulu menggunakan center dan scale agar perbedaan skala antar variabel tidak memengaruhi perhitungan jarak pada algoritma KNN.
set.seed(36)
knn_model <- train(
heart_attack ~ .,
data = train_data,
method = "knn",
preProcess = c("center", "scale"),
trControl = train_control,
tuneGrid = expand.grid(
k = seq(1, 97, by = 2)
)
)
print(knn_model)
## k-Nearest Neighbors
##
## 7526 samples
## 27 predictor
## 2 classes: '0', '1'
##
## Pre-processing: centered (33), scaled (33)
## Resampling: Cross-Validated (5 fold)
## Summary of sample sizes: 6021, 6021, 6020, 6021, 6021
## Resampling results across tuning parameters:
##
## k Accuracy Kappa
## 1 0.6163949 0.1840840
## 3 0.6371242 0.2099754
## 5 0.6533340 0.2355387
## 7 0.6621034 0.2489196
## 9 0.6671533 0.2545688
## 11 0.6680833 0.2541828
## 13 0.6716707 0.2582010
## 15 0.6735313 0.2594258
## 17 0.6761893 0.2656316
## 19 0.6749942 0.2596409
## 21 0.6737987 0.2533051
## 23 0.6748613 0.2546526
## 25 0.6722036 0.2470049
## 27 0.6760565 0.2546347
## 29 0.6722032 0.2438988
## 31 0.6707423 0.2393650
## 33 0.6706096 0.2368217
## 35 0.6714063 0.2372753
## 37 0.6691473 0.2318988
## 39 0.6696789 0.2325027
## 41 0.6695460 0.2318959
## 43 0.6688819 0.2278814
## 45 0.6699444 0.2300803
## 47 0.6688819 0.2260009
## 49 0.6694140 0.2272251
## 51 0.6660920 0.2186238
## 53 0.6646298 0.2146982
## 55 0.6651616 0.2154783
## 57 0.6638330 0.2108359
## 59 0.6633009 0.2087614
## 61 0.6651616 0.2130432
## 63 0.6638327 0.2089083
## 65 0.6629025 0.2061147
## 67 0.6613080 0.2016227
## 69 0.6619725 0.2034154
## 71 0.6625038 0.2041127
## 73 0.6601122 0.1976280
## 75 0.6599793 0.1975475
## 77 0.6585174 0.1931067
## 79 0.6567904 0.1883111
## 81 0.6569232 0.1893422
## 83 0.6561259 0.1869753
## 85 0.6578535 0.1906366
## 87 0.6569232 0.1881064
## 89 0.6565246 0.1866398
## 91 0.6573216 0.1886156
## 93 0.6571889 0.1883530
## 95 0.6554614 0.1838239
## 97 0.6555943 0.1837132
##
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was k = 17.
Nilai K terbaik dipilih berdasarkan akurasi tertinggi yang diperoleh selama proses Grid Search Cross Validation. Nilai K inilah yang digunakan sebagai parameter final model KNN dalam proses prediksi.
cat("Nilai K terbaik:\n")
## Nilai K terbaik:
print(knn_model$bestTune)
## k
## 9 17
Grafik berikut menunjukkan hubungan antara nilai K dan akurasi model selama proses hyperparameter tuning. Nilai K optimal ditandai dengan titik akurasi tertinggi pada grafik.
plot(
knn_model,
main = "Akurasi KNN berdasarkan Nilai K",
xlab = "Jumlah Tetangga (K)",
ylab = "Akurasi (Cross Validation)"
)
Setelah model KNN terbaik diperoleh, dilakukan prediksi terhadap data testing yang belum pernah dilihat oleh model sebelumnya. Hasil prediksi ini kemudian dibandingkan dengan label sebenarnya untuk mengukur performa model secara objektif.
knn_predictions <- predict(
knn_model,
newdata = test_data
)
Confusion Matrix digunakan untuk mengevaluasi performa model secara menyeluruh dengan membandingkan hasil prediksi terhadap nilai aktual. Kelas positif yang digunakan adalah 1 yang merepresentasikan individu dengan risiko serangan jantung.
cm <- confusionMatrix(
knn_predictions,
test_data$heart_attack,
positive = "1"
)
print(cm)
## Confusion Matrix and Statistics
##
## Reference
## Prediction 0 1
## 0 1040 529
## 1 76 236
##
## Accuracy : 0.6784
## 95% CI : (0.6567, 0.6994)
## No Information Rate : 0.5933
## P-Value [Acc > NIR] : 1.609e-14
##
## Kappa : 0.2651
##
## Mcnemar's Test P-Value : < 2.2e-16
##
## Sensitivity : 0.3085
## Specificity : 0.9319
## Pos Pred Value : 0.7564
## Neg Pred Value : 0.6628
## Prevalence : 0.4067
## Detection Rate : 0.1255
## Detection Prevalence : 0.1659
## Balanced Accuracy : 0.6202
##
## 'Positive' Class : 1
##
Berikut adalah ringkasan metrik evaluasi model KNN yang digunakan dalam penelitian ini:
cat("==============================\n")
## ==============================
cat(" Ringkasan Performa Model KNN\n")
## Ringkasan Performa Model KNN
cat("==============================\n")
## ==============================
cat("Accuracy :", round(cm$overall["Accuracy"] * 100, 2), "%\n")
## Accuracy : 67.84 %
cat("Precision:", round(cm$byClass["Precision"] * 100, 2), "%\n")
## Precision: 75.64 %
cat("Recall :", round(cm$byClass["Recall"] * 100, 2), "%\n")
## Recall : 30.85 %
cat("F1-Score :", round(cm$byClass["F1"] * 100, 2), "%\n")
## F1-Score : 43.83 %
cat("==============================\n")
## ==============================