Laporan ini membahas analisis klasifikasi menggunakan dataset
Heart
dengan tujuan lakukan klasifikasi pasien berdasarkan
adanya penyakit jantung. Dalam laporan ini, model
Decision Tree
dan Naive Bayes
digunakan untuk
memprediksi keberadaan penyakit jantung pada pasien.
Deteksi dini penyakit jantung sangat penting dalam upaya pencegahan dan pengobatan yang efektif. Metode klasifikasi menjadi alat yang berguna dalam memprediksi keberadaan penyakit jantung berdasarkan berbagai faktor dan gejala yang terkait. Dalam konteks ini, model Decision Tree dan Naive Bayes telah dipilih untuk dilakukan perbandingan dalam hal kemampuan prediksi.
Decision Tree adalah model klasifikasi yang menggunakan struktur pohon keputusan untuk memisahkan data berdasarkan aturan-aturan yang dihasilkan. Sementara itu, Naive Bayes adalah model klasifikasi yang berdasarkan pada Teorema Bayes dengan asumsi sederhana yaitu independensi antara fitur-fitur yang digunakan.
Tujuan dari laporan ini adalah untuk menganalisis dan membandingkan performa model Decision Tree dan Naive Bayes dalam memprediksi keberadaan penyakit jantung pada pasien berdasarkan dataset Heart.
HeartDisease
.kecuali HeartDisease
.::include_graphics("assets/heart.jpg") knitr
<- read.csv("data_input/heart.csv")
data glimpse(data)
#> Rows: 918
#> Columns: 12
#> $ Age <int> 40, 49, 37, 48, 54, 39, 45, 54, 37, 48, 37, 58, 39, 49,…
#> $ Sex <chr> "M", "F", "M", "F", "M", "M", "F", "M", "M", "F", "F", …
#> $ ChestPainType <chr> "ATA", "NAP", "ATA", "ASY", "NAP", "NAP", "ATA", "ATA",…
#> $ RestingBP <int> 140, 160, 130, 138, 150, 120, 130, 110, 140, 120, 130, …
#> $ Cholesterol <int> 289, 180, 283, 214, 195, 339, 237, 208, 207, 284, 211, …
#> $ FastingBS <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
#> $ RestingECG <chr> "Normal", "Normal", "ST", "Normal", "Normal", "Normal",…
#> $ MaxHR <int> 172, 156, 98, 108, 122, 170, 170, 142, 130, 120, 142, 9…
#> $ ExerciseAngina <chr> "N", "N", "N", "Y", "N", "N", "N", "N", "Y", "N", "N", …
#> $ Oldpeak <dbl> 0.0, 1.0, 0.0, 1.5, 0.0, 0.0, 0.0, 0.0, 1.5, 0.0, 0.0, …
#> $ ST_Slope <chr> "Up", "Flat", "Up", "Flat", "Up", "Up", "Up", "Up", "Fl…
#> $ HeartDisease <int> 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1…
Penjelasan tiap kolom (metadata) :
Age
: Usia pasien dalam tahun.Sex
: Jenis kelamin pasien. Nilai “M” mengindikasikan
pria (male), dan nilai “F” mengindikasikan wanita (female).RestingBP
: Tekanan darah istirahat pasien dalam mm Hg
(milimeter raksa).Cholesterol
: Kadar kolesterol serum pasien dalam mg/dl
(miligram per desiliter).FastingBS
: Kadar gula darah puasa pasien. Nilai 0
menunjukkan kadar gula darah normal, sedangkan nilai 1 menunjukkan kadar
gula darah tinggi.RestingECG
: Hasil elektrokardiogram (ECG) istirahat
pasien. Nilai-nilai yang mungkin adalah “Normal” (normal), “ST” (ada
perubahan segmen ST-T), dan “Hyp” (hipertrofi ventrikel kiri).MaxHR
: Denyut jantung maksimum yang dicapai oleh pasien
selama tes olahraga.ExerciseAngina
: Kehadiran angina (nyeri dada) selama
tes olahraga. Nilai “N” menunjukkan tidak hadir angina, sedangkan nilai
“Y” menunjukkan hadirnya angina.Oldpeak
: Depresi segmen ST yang disebabkan oleh
olahraga relatif terhadap istirahat.ST_Slope
: Kemiringan segmen ST selama puncak latihan.
Nilai-nilai yang mungkin adalah “Up” (naik), “Flat” (datar), dan “Down”
(turun).HeartDisease
: Kehadiran atau tidaknya penyakit jantung.
Nilai 0 menunjukkan tidak adanya penyakit jantung, sedangkan nilai 1
menunjukkan adanya penyakit jantung.table(data$HeartDisease)
#>
#> 0 1
#> 410 508
Saya mempertahankan ketidakseimbangan data, karena tujuan kali ini ialah menganalisis perbedaan karakteristik atau tren antara kategori, case ini juga sesuai dengan kondisi dunia nyata dimana kategori minoritas memang lebih jarang terjadi.
colSums(is.na(data))
#> Age Sex ChestPainType RestingBP Cholesterol
#> 0 0 0 0 0
#> FastingBS RestingECG MaxHR ExerciseAngina Oldpeak
#> 0 0 0 0 0
#> ST_Slope HeartDisease
#> 0 0
<- data[ ,!names(data) %in% "HeartDisease" ]
dataset_selected dataset_selected
<-data %>%
data mutate(Sex = as.factor(data$Sex),
ChestPainType = as.factor(data$ChestPainType),
RestingECG = as.factor(data$RestingECG),
ST_Slope = as.factor(data$ST_Slope),
ExerciseAngina = as.factor(data$ExerciseAngina),
FastingBS = as.factor(data$FastingBS),
HeartDisease = as.factor(data$HeartDisease),)
glimpse(data)
#> Rows: 918
#> Columns: 12
#> $ Age <int> 40, 49, 37, 48, 54, 39, 45, 54, 37, 48, 37, 58, 39, 49,…
#> $ Sex <fct> M, F, M, F, M, M, F, M, M, F, F, M, M, M, F, F, M, F, M…
#> $ ChestPainType <fct> ATA, NAP, ATA, ASY, NAP, NAP, ATA, ATA, ASY, ATA, NAP, …
#> $ RestingBP <int> 140, 160, 130, 138, 150, 120, 130, 110, 140, 120, 130, …
#> $ Cholesterol <int> 289, 180, 283, 214, 195, 339, 237, 208, 207, 284, 211, …
#> $ FastingBS <fct> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
#> $ RestingECG <fct> Normal, Normal, ST, Normal, Normal, Normal, Normal, Nor…
#> $ MaxHR <int> 172, 156, 98, 108, 122, 170, 170, 142, 130, 120, 142, 9…
#> $ ExerciseAngina <fct> N, N, N, Y, N, N, N, N, Y, N, N, Y, N, Y, N, N, N, N, N…
#> $ Oldpeak <dbl> 0.0, 1.0, 0.0, 1.5, 0.0, 0.0, 0.0, 0.0, 1.5, 0.0, 0.0, …
#> $ ST_Slope <fct> Up, Flat, Up, Flat, Up, Up, Up, Up, Flat, Up, Up, Flat,…
#> $ HeartDisease <fct> 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1…
summary(data)
#> Age Sex ChestPainType RestingBP Cholesterol
#> Min. :28.00 F:193 ASY:496 Min. : 0.0 Min. : 0.0
#> 1st Qu.:47.00 M:725 ATA:173 1st Qu.:120.0 1st Qu.:173.2
#> Median :54.00 NAP:203 Median :130.0 Median :223.0
#> Mean :53.51 TA : 46 Mean :132.4 Mean :198.8
#> 3rd Qu.:60.00 3rd Qu.:140.0 3rd Qu.:267.0
#> Max. :77.00 Max. :200.0 Max. :603.0
#> FastingBS RestingECG MaxHR ExerciseAngina Oldpeak
#> 0:704 LVH :188 Min. : 60.0 N:547 Min. :-2.6000
#> 1:214 Normal:552 1st Qu.:120.0 Y:371 1st Qu.: 0.0000
#> ST :178 Median :138.0 Median : 0.6000
#> Mean :136.8 Mean : 0.8874
#> 3rd Qu.:156.0 3rd Qu.: 1.5000
#> Max. :202.0 Max. : 6.2000
#> ST_Slope HeartDisease
#> Down: 63 0:410
#> Flat:460 1:508
#> Up :395
#>
#>
#>
Diperoleh Insight :
Age
): Rata-rata usia penumpang adalah 53.51
tahun, dengan rentang usia antara 28 hingga 77 tahun.Sex
): Terdapat 193 penumpang perempuan
dan 725 penumpang laki-laki dalam dataset.ChestPainType
): Terdapat 4 jenis
nyeri dada, yaitu ASY, ATA, NAP, dan TA. Jenis yang paling umum adalah
ASY dengan 496 observasi.RestingBP
): Tekanan darah
istirahat rata-rata adalah 132.4, dengan rentang antara 0 hingga
200.Cholesterol
): Kolesterol rata-rata adalah
198.8, dengan rentang antara 0 hingga 603.FastingBS
): Sekitar 23.31% penumpang
memiliki gula darah puasa yang lebih tinggi dari normal.MaxHR
): Rata-rata denyut
jantung maksimal adalah 136.8, dengan rentang antara 60 hingga 202.HeartDisease
): Sekitar 55.34%
penumpang dalam dataset memiliki penyakit jantung (nilai 1), sementara
sisanya tidak memiliki penyakit jantung (nilai 0)<- data$HeartDisease target
# Histogram usia (Age)
ggplot(data, aes(x = Age)) +
geom_histogram(fill = "steelblue", color = "black") +
labs(x = "Usia", y = "Frekuensi") +
ggtitle("Distribusi Usia") +
theme_minimal()
# Box plot kadar kolesterol (Cholesterol) berdasarkan jenis nyeri dada (ChestPainType)
ggplot(data, aes(x = ChestPainType, y = Cholesterol, fill = ChestPainType)) +
geom_boxplot() +
labs(x = "Jenis Nyeri Dada", y = "Kadar Kolesterol") +
ggtitle("Perbandingan Kadar Kolesterol\nBerdasarkan Jenis Nyeri Dada") +
theme_minimal()
Note :: “ATA
” (Atypical Angina) “NAP”
(Non-Anginal Pain) “ASY
” (Asymptomatic) “TA
”
(Typical Angina)
# keberadaan penyakit jantung (HeartDisease) berdasarkan jenis nyeri dada (ChestPainType) dan jenis kelamin (Sex)
ggplot(data, aes(x = ChestPainType, fill = factor(HeartDisease))) +
geom_bar(position = "fill") +
facet_wrap(~Sex) +
labs(x = "Jenis Nyeri Dada", y = "Persentase") +
ggtitle("Persentase Penyakit Jantung\nBerdasarkan Jenis Nyeri Dada dan Jenis Kelamin") +
theme_minimal()+
coord_flip()
# Bar plot jenis kelamin (Sex)
ggplot(data, aes(x = Sex)) +
geom_bar(fill = "steelblue") +
labs(x = "Jenis Kelamin", y = "Jumlah") +
ggtitle("Distribusi Peserta Berdasarkan Jenis Kelamin") +
theme_minimal()
RNGkind(sample.kind = "Rounding")
set.seed(123)
# index sampling
<- sample(x = nrow(data),
index size = nrow(data)*0.8)
# splitting
<- data[index, ]
data_train <- data[-index, ] data_test
library(caret)
<- train(HeartDisease ~ ., data = data_train, method ="naive_bayes")
model_nb model_nb
#> Naive Bayes
#>
#> 734 samples
#> 11 predictor
#> 2 classes: '0', '1'
#>
#> No pre-processing
#> Resampling: Bootstrapped (25 reps)
#> Summary of sample sizes: 734, 734, 734, 734, 734, 734, ...
#> Resampling results across tuning parameters:
#>
#> usekernel Accuracy Kappa
#> FALSE 0.8605491 0.7178287
#> TRUE 0.8526397 0.6989693
#>
#> Tuning parameter 'laplace' was held constant at a value of 0
#> Tuning
#> parameter 'adjust' was held constant at a value of 1
#> Accuracy was used to select the optimal model using the largest value.
#> The final values used for the model were laplace = 0, usekernel = FALSE
#> and adjust = 1.
<- predict(model_nb, newdata = data_train)
nb_pred_train <- predict(model_nb, newdata = data_test) nb_pred_test
<- mean(nb_pred_train == data_train$HeartDisease)
nb_train_accuracy <- mean(nb_pred_test == data_test$HeartDisease) nb_test_accuracy
# Naive Bayes
confusionMatrix(nb_pred_test, data_test$HeartDisease)
#> Confusion Matrix and Statistics
#>
#> Reference
#> Prediction 0 1
#> 0 67 9
#> 1 12 96
#>
#> Accuracy : 0.8859
#> 95% CI : (0.8308, 0.9279)
#> No Information Rate : 0.5707
#> P-Value [Acc > NIR] : <2e-16
#>
#> Kappa : 0.766
#>
#> Mcnemar's Test P-Value : 0.6625
#>
#> Sensitivity : 0.8481
#> Specificity : 0.9143
#> Pos Pred Value : 0.8816
#> Neg Pred Value : 0.8889
#> Prevalence : 0.4293
#> Detection Rate : 0.3641
#> Detection Prevalence : 0.4130
#> Balanced Accuracy : 0.8812
#>
#> 'Positive' Class : 0
#>
<- train(HeartDisease ~ ., data = data_train, method = "rpart")
tree_model tree_model
#> CART
#>
#> 734 samples
#> 11 predictor
#> 2 classes: '0', '1'
#>
#> No pre-processing
#> Resampling: Bootstrapped (25 reps)
#> Summary of sample sizes: 734, 734, 734, 734, 734, 734, ...
#> Resampling results across tuning parameters:
#>
#> cp Accuracy Kappa
#> 0.01812689 0.8284746 0.6519166
#> 0.05135952 0.8176127 0.6282037
#> 0.58610272 0.6755823 0.3067103
#>
#> Accuracy was used to select the optimal model using the largest value.
#> The final value used for the model was cp = 0.01812689.
<- predict(tree_model, newdata = data_train)
tree_pred_train <- predict(tree_model, newdata = data_test) tree_pred_test
<- mean(tree_pred_train == data_train$HeartDisease)
tree_train_accuracy <- mean(tree_pred_test == data_test$HeartDisease) tree_test_accuracy
# Decision Tree
confusionMatrix(tree_pred_test, data_test$HeartDisease)
#> Confusion Matrix and Statistics
#>
#> Reference
#> Prediction 0 1
#> 0 59 11
#> 1 20 94
#>
#> Accuracy : 0.8315
#> 95% CI : (0.7695, 0.8826)
#> No Information Rate : 0.5707
#> P-Value [Acc > NIR] : 4.032e-14
#>
#> Kappa : 0.6513
#>
#> Mcnemar's Test P-Value : 0.1508
#>
#> Sensitivity : 0.7468
#> Specificity : 0.8952
#> Pos Pred Value : 0.8429
#> Neg Pred Value : 0.8246
#> Prevalence : 0.4293
#> Detection Rate : 0.3207
#> Detection Prevalence : 0.3804
#> Balanced Accuracy : 0.8210
#>
#> 'Positive' Class : 0
#>
<- data.frame(
comparison Model = c("Decision Tree", "Naive Bayes"),
Train_Accuracy = c(tree_train_accuracy, nb_train_accuracy),
Test_Accuracy = c(tree_test_accuracy, nb_test_accuracy)
)
comparison
Note :
Saya menggunakan metrik akurasi, karena akurasi merupakan metrik yang paling umum digunakan dalam klasifikasi, dan itu mengukur persentase prediksi yang benar dibandingkan dengan jumlah total prediksi.
# Calculate precision
<- precision(tree_pred_train, data_train$HeartDisease)
tree_train_precision <- precision(tree_pred_test, data_test$HeartDisease)
tree_test_precision <- precision(nb_pred_train, data_train$HeartDisease)
nb_train_precision <- precision(nb_pred_test, data_test$HeartDisease)
nb_test_precision
# Calculate recall
<- recall(tree_pred_train, data_train$HeartDisease)
tree_train_recall <- recall(tree_pred_test, data_test$HeartDisease)
tree_test_recall <- recall(nb_pred_train, data_train$HeartDisease)
nb_train_recall <- recall(nb_pred_test, data_test$HeartDisease)
nb_test_recall
# Create dataframe
<- data.frame(
comparison_df Model = c("Decision Tree", "Naive Bayes"),
Train_Precision = c(tree_train_precision, nb_train_precision),
Test_Precision = c(tree_test_precision, nb_test_precision),
Train_Recall = c(tree_train_recall, nb_train_recall),
Test_Recall = c(tree_test_recall, nb_test_recall)
)
comparison_df
Insight :
Dari hasil comparison
diatas, diperoleh insight
:
Berdasarkan hasil perbandingan model, dapat disimpulkan bahwa kedua model memiliki performa yang baik. Namun, Naive Bayes memiliki nilai presisi dan recall yang lebih tinggi pada data pengujian, sehingga bisa menjadi pilihan yang lebih baik untuk kasus ini. Model Naive Bayes cenderung lebih baik dalam mengidentifikasi kasus positif dengan benar pada data yang belum pernah dilihat sebelumnya.
Berdasarkan hasil analisis, disarankan untuk menggunakan model Naive Bayes dalam memprediksi keberadaan penyakit jantung pada pasien. Namun, penting juga untuk terus melakukan evaluasi dan pengembangan model dengan menggunakan lebih banyak data dan menerapkan teknik tuning model yang lebih canggih untuk meningkatkan performa prediksi.