Pada kesempatan kali ini, saya akan mencoba untuk melakukan prediksi terhadap pasien penyakit jantung pada suatu rumah sakit yang akan diprediksi sakit atau tidak berdasarkan kategori dari beberapa variabel. Algoritma yang digunakan yaitu logistic regression, K-Nearest Neighboor, Naive Bayes, Decision Tree dan Random Forest yang merupakan supervised learning.
Dataset yang digunakan pada kesempatan kali ini adalah data mengenai pasien yang terkena penyakit jantung berdasarkan beberapa karakteristik yang menyertai, data diambil dari Kaggle - Heart Disease UCI yang dapat diunduh pada link berikut https://www.kaggle.com/ronitf/heart-disease-uci
library(dplyr)## Warning: package 'dplyr' was built under R version 4.0.4
##
## 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(class)
library(tidyr)## Warning: package 'tidyr' was built under R version 4.0.4
library(caret)## Warning: package 'caret' was built under R version 4.0.5
## Loading required package: lattice
## Loading required package: ggplot2
## Warning: package 'ggplot2' was built under R version 4.0.4
library(e1071)## Warning: package 'e1071' was built under R version 4.0.5
library(ROCR)## Warning: package 'ROCR' was built under R version 4.0.5
library(partykit)## Warning: package 'partykit' was built under R version 4.0.5
## Loading required package: grid
## Loading required package: libcoin
## Warning: package 'libcoin' was built under R version 4.0.5
## Loading required package: mvtnorm
## Warning: package 'mvtnorm' was built under R version 4.0.3
library(randomForest)## Warning: package 'randomForest' was built under R version 4.0.5
## randomForest 4.6-14
## Type rfNews() to see new features/changes/bug fixes.
##
## Attaching package: 'randomForest'
## The following object is masked from 'package:ggplot2':
##
## margin
## The following object is masked from 'package:dplyr':
##
## combine
options(scipen = 9999)jantung <- read.csv("dataset/heart.csv")
str(jantung)## 'data.frame': 303 obs. of 14 variables:
## $ ï..age : int 63 37 41 56 57 57 56 44 52 57 ...
## $ sex : int 1 1 0 1 0 1 0 1 1 1 ...
## $ cp : int 3 2 1 1 0 0 1 1 2 2 ...
## $ trestbps: int 145 130 130 120 120 140 140 120 172 150 ...
## $ chol : int 233 250 204 236 354 192 294 263 199 168 ...
## $ fbs : int 1 0 0 0 0 0 0 0 1 0 ...
## $ restecg : int 0 1 0 1 1 1 0 1 1 1 ...
## $ thalach : int 150 187 172 178 163 148 153 173 162 174 ...
## $ exang : int 0 0 0 0 1 0 0 0 0 0 ...
## $ oldpeak : num 2.3 3.5 1.4 0.8 0.6 0.4 1.3 0 0.5 1.6 ...
## $ slope : int 0 0 2 2 2 1 1 2 2 2 ...
## $ ca : int 0 0 0 0 0 0 0 0 0 0 ...
## $ thal : int 1 2 2 2 2 1 2 3 3 2 ...
## $ target : int 1 1 1 1 1 1 1 1 1 1 ...
Informasi penting dalam data :
ï..age : dalam beberapa tahun
sex : (1 = laki-laki; 0 = perempuan)
cp : tipe nyeri yang paling parah
trestbps : melacak tekanan darah(dalam mm Hg saat masuk ke rumah sakit)
chol : kolestoral dalam mg / dl
fbs : (gula darah puasa> 120 mg / dl) (1 = benar; 0 = salah)
restecg : mengembalikan hasil elektrokardiografi
thalach : denyut jantung maksimum tercapai
exang : exercise induced angina (1 = ya; 0 = tidak)
oldpeak : ST depresi yang disebabkan oleh olahraga relatif terhadap istirahat
slope : kemiringan segmen ST latihan puncak
ca : jumlah pembuluh darah utama (0-3) diwarnai dengan fluoroskopi
thal : 3 = normal; 6 = cacat tetap; 7 = cacat yang dapat dibalik
target : 1 = sakit atau 0 = tidak sakit
head(jantung)## ï..age sex cp trestbps chol fbs restecg thalach exang oldpeak slope ca thal
## 1 63 1 3 145 233 1 0 150 0 2.3 0 0 1
## 2 37 1 2 130 250 0 1 187 0 3.5 0 0 2
## 3 41 0 1 130 204 0 0 172 0 1.4 2 0 2
## 4 56 1 1 120 236 0 1 178 0 0.8 2 0 2
## 5 57 0 0 120 354 0 1 163 1 0.6 2 0 2
## 6 57 1 0 140 192 0 1 148 0 0.4 1 0 1
## target
## 1 1
## 2 1
## 3 1
## 4 1
## 5 1
## 6 1
Cek terlebih dahulu struktur dataset. Agar kita tahu gambaran struktur dataset dari masing-masing variabel yang akan kita gunakan. Sehingga dapat kita ketahui apakah sudah sesuai atau masih perlu ada yang dirubah, maka dapat dilakukan pada tahap selanjutnya.
glimpse(jantung)## Rows: 303
## Columns: 14
## $ ï..age <int> 63, 37, 41, 56, 57, 57, 56, 44, 52, 57, 54, 48, 49, 64, 58, 5~
## $ sex <int> 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1~
## $ cp <int> 3, 2, 1, 1, 0, 0, 1, 1, 2, 2, 0, 2, 1, 3, 3, 2, 2, 3, 0, 3, 0~
## $ trestbps <int> 145, 130, 130, 120, 120, 140, 140, 120, 172, 150, 140, 130, 1~
## $ chol <int> 233, 250, 204, 236, 354, 192, 294, 263, 199, 168, 239, 275, 2~
## $ fbs <int> 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0~
## $ restecg <int> 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1~
## $ thalach <int> 150, 187, 172, 178, 163, 148, 153, 173, 162, 174, 160, 139, 1~
## $ exang <int> 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0~
## $ oldpeak <dbl> 2.3, 3.5, 1.4, 0.8, 0.6, 0.4, 1.3, 0.0, 0.5, 1.6, 1.2, 0.2, 0~
## $ slope <int> 0, 0, 2, 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 1, 2, 0, 2, 2, 1~
## $ ca <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0~
## $ thal <int> 1, 2, 2, 2, 2, 1, 2, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3~
## $ target <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1~
Dalam hal ini terdapat beberapa variabel yang digunakan tetapi terdapat ketidaksesuaian tipe data, maka harus disesuaikan terlebih dahulu.
jantung <- jantung %>%
select(-ï..age) %>%
mutate_if(is.integer, as.factor) %>%
mutate(sex = factor(sex, levels = c(0,1),
labels = c("Female", "Male")),
fbs =factor(fbs, levels = c(0,1),
labels = c("False", "True")),
exang = factor(exang, levels = c(0,1),
labels = c("Tidak", "Iya")),
target = factor(target, levels = c(0,1),
labels = c("Sehat", "Tidak Sehat")))glimpse(jantung)## Rows: 303
## Columns: 13
## $ sex <fct> Male, Male, Female, Male, Female, Male, Female, Male, Male, M~
## $ cp <fct> 3, 2, 1, 1, 0, 0, 1, 1, 2, 2, 0, 2, 1, 3, 3, 2, 2, 3, 0, 3, 0~
## $ trestbps <fct> 145, 130, 130, 120, 120, 140, 140, 120, 172, 150, 140, 130, 1~
## $ chol <fct> 233, 250, 204, 236, 354, 192, 294, 263, 199, 168, 239, 275, 2~
## $ fbs <fct> True, False, False, False, False, False, False, False, True, ~
## $ restecg <fct> 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1~
## $ thalach <fct> 150, 187, 172, 178, 163, 148, 153, 173, 162, 174, 160, 139, 1~
## $ exang <fct> Tidak, Tidak, Tidak, Tidak, Iya, Tidak, Tidak, Tidak, Tidak, ~
## $ oldpeak <dbl> 2.3, 3.5, 1.4, 0.8, 0.6, 0.4, 1.3, 0.0, 0.5, 1.6, 1.2, 0.2, 0~
## $ slope <fct> 0, 0, 2, 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 1, 2, 0, 2, 2, 1~
## $ ca <fct> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0~
## $ thal <fct> 1, 2, 2, 2, 2, 1, 2, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3~
## $ target <fct> Tidak Sehat, Tidak Sehat, Tidak Sehat, Tidak Sehat, Tidak Seh~
Tipe data dalam dataset sudah sesuai, kemudian cek apakah terdapat missing value :
anyNA(jantung)## [1] FALSE
Tidak ada nilai Missing Value yang ditemukan
Hal yang dilakukan sebelum melakukan pemodelan, lakukan terlebih dahulu proporsi dari target variabel yang kita gunakan. dalam hal ini variabel yang digunakan adalah target.
prop.table(table(jantung$target))##
## Sehat Tidak Sehat
## 0.4554455 0.5445545
table(jantung$target)##
## Sehat Tidak Sehat
## 138 165
Dapat kita lihat dari proporsi kedua kelas tersebut menghasilkan proporsi yang seimbang, sehingga kita tidak perlu melakukan pre-processing tambahan untuk menyeimbangkan proporsi antar dua kelas target dari variabel yang kita gunakan.
Selanjutnya adalah melakukan splitting data menjadi data train dan data test. Splitting data train dan test ini bertujuan untuk membuat model yang digunakan yaitu data test dan data test tujuannya untuk menguji model terhadap unseen data. Splitting data train dan test ini menggunakan proporsi 80% untuk data train dan 20% untuk data test.
RNGkind(sample.kind = "Rounding")## Warning in RNGkind(sample.kind = "Rounding"): non-uniform 'Rounding' sampler
## used
set.seed(418)
indextrain <- sample(nrow(jantung),
nrow(jantung)*0.8)
jantung.train <- jantung[indextrain,]
jantung.test <- jantung[-indextrain,]prop.table(table(jantung.train$target))##
## Sehat Tidak Sehat
## 0.4752066 0.5247934
Hasil yang diperoleh bahwa proporsi dari data train masih seimbang dan tidak perlu pre-processing tambahan lagi untuk data train yang kita gunakan.
Dalam melakukan pemodelan kali ini menggunakan Logistc Regression. Pemodelan menggunakan fungsi glm() dalam membuat model Logistic Regression.
Dalam pembuatan atau analisa model logistic Regression, terlebih dahulu melakukan pengecekan terhadap korelasi chance_of_admit dengan variabel-variabel dari dataset. Dan variabel yang digunakan merupakan variabel yang dianggap mempengaruhi target variabel, dimana variabel yang menjadi responnya adalah variabel target.
model_jantung <- glm(formula = target ~ sex + cp + fbs + exang + oldpeak + slope + ca + thal,
family = "binomial",
data = jantung.train)
summary(model_jantung)##
## Call:
## glm(formula = target ~ sex + cp + fbs + exang + oldpeak + slope +
## ca + thal, family = "binomial", data = jantung.train)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -2.6772 -0.3484 0.1132 0.4849 2.9536
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 12.9559 1455.3979 0.009 0.992897
## sexMale -1.4911 0.5649 -2.640 0.008303 **
## cp1 0.8804 0.5791 1.520 0.128466
## cp2 2.0282 0.5493 3.692 0.000222 ***
## cp3 1.9919 0.7735 2.575 0.010019 *
## fbsTrue 0.6820 0.6086 1.121 0.262464
## exangIya -1.1585 0.4921 -2.354 0.018564 *
## oldpeak -0.4127 0.2510 -1.645 0.100060
## slope1 -0.6859 0.8397 -0.817 0.413989
## slope2 0.5820 0.9086 0.641 0.521837
## ca1 -2.0677 0.5572 -3.711 0.000206 ***
## ca2 -2.6754 0.7824 -3.420 0.000627 ***
## ca3 -2.0375 0.9011 -2.261 0.023755 *
## ca4 1.0094 1.5377 0.656 0.511523
## thal1 -10.5572 1455.3981 -0.007 0.994212
## thal2 -10.5196 1455.3978 -0.007 0.994233
## thal3 -11.8192 1455.3978 -0.008 0.993521
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 334.89 on 241 degrees of freedom
## Residual deviance: 159.26 on 225 degrees of freedom
## AIC: 193.26
##
## Number of Fisher Scoring iterations: 14
Pada pemodelan yang pertama, masih banyak variabel prediktor yang tidak signifikan terhadap target variabel, oleh karena itu kita akan coba melakukan model fitting menggunakan metode stepwise - backward. Sehingga diperoleh model sebagai berikut
backward_jantung <- step(object = model_jantung,
direction = "backward",
trace = F)
summary(backward_jantung)##
## Call:
## glm(formula = target ~ sex + cp + exang + oldpeak + slope + ca +
## thal, family = "binomial", data = jantung.train)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -2.7303 -0.3589 0.1093 0.4955 2.8958
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 12.9408 1455.3979 0.009 0.992906
## sexMale -1.4599 0.5616 -2.600 0.009333 **
## cp1 0.9050 0.5730 1.579 0.114271
## cp2 2.1583 0.5396 4.000 0.0000635 ***
## cp3 2.1137 0.7657 2.760 0.005775 **
## exangIya -1.0607 0.4799 -2.210 0.027073 *
## oldpeak -0.4518 0.2491 -1.814 0.069747 .
## slope1 -0.7858 0.8329 -0.943 0.345454
## slope2 0.4670 0.9058 0.516 0.606201
## ca1 -2.0297 0.5475 -3.707 0.000209 ***
## ca2 -2.5424 0.7649 -3.324 0.000887 ***
## ca3 -1.9492 0.8878 -2.196 0.028122 *
## ca4 1.2397 1.6143 0.768 0.442503
## thal1 -10.4020 1455.3981 -0.007 0.994297
## thal2 -10.4033 1455.3978 -0.007 0.994297
## thal3 -11.6919 1455.3978 -0.008 0.993590
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 334.89 on 241 degrees of freedom
## Residual deviance: 160.54 on 226 degrees of freedom
## AIC: 192.54
##
## Number of Fisher Scoring iterations: 14
Dengan menggunakan hasil modelling backward_jantung yang merupakan hasil dari stepwise backward selanjutnya akan dihitung prediksi probability sakit atau tidak untuk jantung.test.
jantung.test$pred.Risk <- predict(backward_jantung, newdata = jantung.test, type = "response")
head(jantung.test)## sex cp trestbps chol fbs restecg thalach exang oldpeak slope ca thal
## 8 Male 1 120 263 False 1 173 Tidak 0.0 2 0 3
## 11 Male 0 140 239 False 1 160 Tidak 1.2 2 0 2
## 14 Male 3 110 211 False 0 144 Iya 1.8 1 0 2
## 17 Female 2 120 340 False 1 172 Tidak 0.0 2 0 2
## 27 Male 2 150 212 True 1 157 Tidak 1.6 2 0 2
## 35 Male 3 125 213 False 0 125 Iya 1.4 2 1 2
## target pred.Risk
## 8 Tidak Sehat 0.7615005
## 11 Tidak Sehat 0.7315103
## 14 Tidak Sehat 0.6298010
## 17 Tidak Sehat 0.9943067
## 27 Tidak Sehat 0.9516575
## 35 Tidak Sehat 0.4837961
Selanjutkan lakukan klasifikasi jantung.test berdasarkan pred.Risk dengan syarat lebih besar dari 0.5.
jantung.test$pred.Label <- ifelse(jantung.test$pred.Risk > 0.5, yes = "Tidak Sehat", no = "Sehat")
jantung.test$pred.Label <- as.factor(jantung.test$pred.Label)Hasil prediksi berdasarkan klasifikasi yang telah dibuat sebelumnya.
jantung.test %>%
select(target, pred.Label) %>%
head(10)## target pred.Label
## 8 Tidak Sehat Tidak Sehat
## 11 Tidak Sehat Tidak Sehat
## 14 Tidak Sehat Tidak Sehat
## 17 Tidak Sehat Tidak Sehat
## 27 Tidak Sehat Tidak Sehat
## 35 Tidak Sehat Sehat
## 36 Tidak Sehat Tidak Sehat
## 42 Tidak Sehat Tidak Sehat
## 44 Tidak Sehat Tidak Sehat
## 51 Tidak Sehat Tidak Sehat
Confusion Matrix digunakan dalam mengevaluasi model yang telah dibuat.
model_logistic <- confusionMatrix(jantung.test$pred.Label, jantung.test$target, positive = "Tidak Sehat")
model_logistic## Confusion Matrix and Statistics
##
## Reference
## Prediction Sehat Tidak Sehat
## Sehat 19 3
## Tidak Sehat 4 35
##
## Accuracy : 0.8852
## 95% CI : (0.7778, 0.9526)
## No Information Rate : 0.623
## P-Value [Acc > NIR] : 0.000004712
##
## Kappa : 0.7536
##
## Mcnemar's Test P-Value : 1
##
## Sensitivity : 0.9211
## Specificity : 0.8261
## Pos Pred Value : 0.8974
## Neg Pred Value : 0.8636
## Prevalence : 0.6230
## Detection Rate : 0.5738
## Detection Prevalence : 0.6393
## Balanced Accuracy : 0.8736
##
## 'Positive' Class : Tidak Sehat
##
Berdasarkan hasil model evaluation dengan menggunakan confusion matrix di atas, diperoleh hasil bahwa kemampuan model dalam menebak target “1” atau “Tidak Sehat” sebesar 92,11% untuk Recall/Sensitivity, 82,61% untuk Specificity, 89,74% untuk Precision/Pos Pred Value dan 88,52% untuk Accuracy.
Membuat terlebih dahulu variabel dummy dari data-data kategori yang akan digunakan dalam klasifikasi.
data_dummy <- dummyVars(formula = ~ target + sex + cp + fbs + exang + oldpeak + slope + ca + thal, data = jantung)
data_dummy <- data.frame(predict(data_dummy, newdata = jantung))
str(data_dummy)## 'data.frame': 303 obs. of 25 variables:
## $ target.Sehat : num 0 0 0 0 0 0 0 0 0 0 ...
## $ target.Tidak.Sehat: num 1 1 1 1 1 1 1 1 1 1 ...
## $ sex.Female : num 0 0 1 0 1 0 1 0 0 0 ...
## $ sex.Male : num 1 1 0 1 0 1 0 1 1 1 ...
## $ cp.0 : num 0 0 0 0 1 1 0 0 0 0 ...
## $ cp.1 : num 0 0 1 1 0 0 1 1 0 0 ...
## $ cp.2 : num 0 1 0 0 0 0 0 0 1 1 ...
## $ cp.3 : num 1 0 0 0 0 0 0 0 0 0 ...
## $ fbs.False : num 0 1 1 1 1 1 1 1 0 1 ...
## $ fbs.True : num 1 0 0 0 0 0 0 0 1 0 ...
## $ exang.Tidak : num 1 1 1 1 0 1 1 1 1 1 ...
## $ exang.Iya : num 0 0 0 0 1 0 0 0 0 0 ...
## $ oldpeak : num 2.3 3.5 1.4 0.8 0.6 0.4 1.3 0 0.5 1.6 ...
## $ slope.0 : num 1 1 0 0 0 0 0 0 0 0 ...
## $ slope.1 : num 0 0 0 0 0 1 1 0 0 0 ...
## $ slope.2 : num 0 0 1 1 1 0 0 1 1 1 ...
## $ ca.0 : num 1 1 1 1 1 1 1 1 1 1 ...
## $ ca.1 : num 0 0 0 0 0 0 0 0 0 0 ...
## $ ca.2 : num 0 0 0 0 0 0 0 0 0 0 ...
## $ ca.3 : num 0 0 0 0 0 0 0 0 0 0 ...
## $ ca.4 : num 0 0 0 0 0 0 0 0 0 0 ...
## $ thal.0 : num 0 0 0 0 0 0 0 0 0 0 ...
## $ thal.1 : num 1 0 0 0 0 1 0 0 0 0 ...
## $ thal.2 : num 0 1 1 1 1 0 1 0 0 1 ...
## $ thal.3 : num 0 0 0 0 0 0 0 1 1 0 ...
Menghapus variabel dummy yang di variabel sebelumnya terdapat 2 kategori
data_dummy$target.Sehat <- NULL
data_dummy$sex.Female <- NULL
data_dummy$fbs.False <- NULL
data_dummy$exang.Tidak <- NULLnames(data_dummy)## [1] "target.Tidak.Sehat" "sex.Male" "cp.0"
## [4] "cp.1" "cp.2" "cp.3"
## [7] "fbs.True" "exang.Iya" "oldpeak"
## [10] "slope.0" "slope.1" "slope.2"
## [13] "ca.0" "ca.1" "ca.2"
## [16] "ca.3" "ca.4" "thal.0"
## [19] "thal.1" "thal.2" "thal.3"
Membentuk data training dan data testing dari data_dummy yang telah terbentuk.
RNGkind(sample.kind = "Rounding")## Warning in RNGkind(sample.kind = "Rounding"): non-uniform 'Rounding' sampler
## used
set.seed(418)
indextrain_dummy <- sample(nrow(data_dummy),
nrow(data_dummy)*0.8)
data_dummy.train <- data_dummy[indextrain_dummy,]
data_dummy.test <- data_dummy[-indextrain_dummy,]#prediktor
jantung.train_x <- data_dummy.train %>%
select(-target.Tidak.Sehat)
jantung.test_x <- data_dummy.train %>%
select(-target.Tidak.Sehat)
#target
jantung.train_y <- data_dummy.train %>%
select(target.Tidak.Sehat)
jantung.test_y <- data_dummy.train %>%
select(target.Tidak.Sehat)Data prediktor akan dilkukan scalling menggunakan z-score standarization. Data test juga harus dilakukan scalling dengan menggunakan parameter dari data train karena data tets tersebut merupakan unseen data.
jantung.train_xs <- scale(jantung.train_x)
jantung.test_xs <- scale(x = jantung.test_x,
center = attr(jantung.train_xs, "scaled:center"),
scale = attr(jantung.train_xs, "scaled:scale"))Mencari nilai optimum k ini bertujuan mengelompokkan jumlah tetangga terdekat.
round(sqrt(nrow(jantung.train)))## [1] 16
Dalam hal ini jumlah kelas target yaitu 2 (Sehat dan Tidak Sehat). Nilai optimum k yang didapat adlaah 16, untuk menghindari terjadinya seri maka nilai k dinaikkan menjadi 17.
jantung.pred <- knn(train = jantung.train_xs,
test = jantung.test_xs,
cl = jantung.train_y$target.Tidak.Sehat,
k = 17)dim(jantung.train_xs)## [1] 242 20
dim(jantung.test_xs)## [1] 242 20
head(jantung.pred)## [1] 1 0 1 1 1 0
## Levels: 0 1
Menggunakan Confusion Matrix untuk melakukan model evaluasi
model_knn <- confusionMatrix(data = as.factor(jantung.pred), reference = as.factor(jantung.train_y$target.Tidak.Sehat), positive = "1")
model_knn## Confusion Matrix and Statistics
##
## Reference
## Prediction 0 1
## 0 94 15
## 1 21 112
##
## Accuracy : 0.8512
## 95% CI : (0.8, 0.8936)
## No Information Rate : 0.5248
## P-Value [Acc > NIR] : <0.0000000000000002
##
## Kappa : 0.701
##
## Mcnemar's Test P-Value : 0.4047
##
## Sensitivity : 0.8819
## Specificity : 0.8174
## Pos Pred Value : 0.8421
## Neg Pred Value : 0.8624
## Prevalence : 0.5248
## Detection Rate : 0.4628
## Detection Prevalence : 0.5496
## Balanced Accuracy : 0.8496
##
## 'Positive' Class : 1
##
Berdasarkan hasil model evaluation dengan menggunakan confusion matrix di atas, diperoleh hasil bahwa kemampuan model dalam menebak target “1” atau “Tidak Sehat” sebesar 88,19% untuk Recall/Sensitivity, 81,74% untuk Specificity, 84,21% untuk Precision/Pos Pred Value dan 85,12% untuk Accuracy.
length(jantung.pred)## [1] 242
length(jantung.train_y$target.Tidak.Sehat)## [1] 242
Dari Dataset Heart tersebut, selanjutnya kan dipakai untuk membuat model klasifikasi lain dengan menggunakan Naive Bayes, Decision Tree, Random Forest.
Data yang digunakan adalah data hasil cross validation sebelumnya
jantung_naivebayes <- naiveBayes(x = jantung.train %>%
select(-target), y = jantung.train$target, laplace = 1)Menghitung prediksi terhadap model klasifikasi naiveBayes.
jantung_predClass <- predict(object = jantung_naivebayes,
newdata = jantung.test,
type = "class")table(jantung_predClass)## jantung_predClass
## Sehat Tidak Sehat
## 22 39
head(jantung_predClass)## [1] Tidak Sehat Tidak Sehat Sehat Tidak Sehat Tidak Sehat Sehat
## Levels: Sehat Tidak Sehat
Data diatas memperlihatkan bahwa pasien yang sehat sebanyak 22 orang dan pasien yang tidak sehat sebanyak 39 orang. Bagaiman dengan confusion matrix nya.
model_naivebayes <- confusionMatrix(data = jantung_predClass,
reference = jantung.test$target,
positive = "Tidak Sehat")
model_naivebayes## Confusion Matrix and Statistics
##
## Reference
## Prediction Sehat Tidak Sehat
## Sehat 18 4
## Tidak Sehat 5 34
##
## Accuracy : 0.8525
## 95% CI : (0.7383, 0.9302)
## No Information Rate : 0.623
## P-Value [Acc > NIR] : 0.0000748
##
## Kappa : 0.6832
##
## Mcnemar's Test P-Value : 1
##
## Sensitivity : 0.8947
## Specificity : 0.7826
## Pos Pred Value : 0.8718
## Neg Pred Value : 0.8182
## Prevalence : 0.6230
## Detection Rate : 0.5574
## Detection Prevalence : 0.6393
## Balanced Accuracy : 0.8387
##
## 'Positive' Class : Tidak Sehat
##
Berdasarkan hasil model evaluation dengan menggunakan confusion matrix di atas, diperoleh hasil bahwa kemampuan model dalam menebak target “1” atau “Tidak Sehat” sebesar 89,47% untuk Recall/Sensitivity, 78,26% untuk Specificity, 87,18% untuk Precision/Pos Pred Value dan 85,25% untuk Accuracy.
Melihat ROC dan AUC untuk melihat model bagus memprediksi atau tidak.
#mengambil hasil prediksi probability
jantung_predProb <- predict(jantung_naivebayes, newdata = jantung.test, type = "raw")
head(jantung_predProb)## Sehat Tidak Sehat
## [1,] 0.0123089141 0.9876911
## [2,] 0.1643566902 0.8356433
## [3,] 0.8405785714 0.1594214
## [4,] 0.0001127051 0.9998873
## [5,] 0.0093486265 0.9906514
## [6,] 0.8666469679 0.1333530
Selanjutnya membuat ROC dengan menyiapkan objek prediction.
#buat objek prediction
jantung_roc <- prediction(predictions = jantung_predProb[,1],
labels = as.numeric(jantung.test$target == "Sehat"))
#buat performance dari objek prediction
perf <- performance(prediction.obj = jantung_roc, measure = "tpr", x.measure = "fpr")
#buat plot
plot(perf)
abline(0,1, lty = 2)Selanjutnya kita mencari AUC. Semakin mendekati 1, semakin bagus performa modelnya.
auc <- performance(prediction.obj = jantung_roc, measure = "auc")
auc@y.values## [[1]]
## [1] 0.9153318
Nilai AUC dari Naive Bayes mendekati 1, artinya model kita mampu mengklasifikasikan kelas positif dengan baik tetapi kelas negatif dengan tidak baik.
Klasifikasi menggunakan Decision Tree
jantung_dtree <- ctree(formula = target ~ ., data = jantung.train,
control = ctree_control(mincriterion = 0.95,
minsplit = 0,
minbucket = 0))plot(jantung_dtree, type = "simple")Setelah dapat tree tersebut, selanjutkan lakukan evaluasi untuk melihat apakah model terjadi overfitting atau tidak.
jantung_predict <- predict(object = jantung_dtree, newdata = jantung.train %>%
select(-target), type = "response")model_decision_tree_train <- confusionMatrix(data = jantung_predict,
reference = jantung.train$target,
positive = "Tidak Sehat")
model_decision_tree_train## Confusion Matrix and Statistics
##
## Reference
## Prediction Sehat Tidak Sehat
## Sehat 83 10
## Tidak Sehat 32 117
##
## Accuracy : 0.8264
## 95% CI : (0.7727, 0.872)
## No Information Rate : 0.5248
## P-Value [Acc > NIR] : < 0.00000000000000022
##
## Kappa : 0.6489
##
## Mcnemar's Test P-Value : 0.001194
##
## Sensitivity : 0.9213
## Specificity : 0.7217
## Pos Pred Value : 0.7852
## Neg Pred Value : 0.8925
## Prevalence : 0.5248
## Detection Rate : 0.4835
## Detection Prevalence : 0.6157
## Balanced Accuracy : 0.8215
##
## 'Positive' Class : Tidak Sehat
##
Dari hasil confusionMatrix decision tree diatas, performa model tidak cukup baik untuk matriks Pos Pred Value atau Precision. Coba cek apakah hal tersebut terjadi overfitting.
jantung_predict_test <- predict(object = jantung_dtree,
newdata = jantung.test %>%
select(-target), type = "response")model_decision_tree_test <- confusionMatrix(data = jantung_predict_test,
reference = jantung.test$target, positive = "Tidak Sehat")
model_decision_tree_test## Confusion Matrix and Statistics
##
## Reference
## Prediction Sehat Tidak Sehat
## Sehat 15 3
## Tidak Sehat 8 35
##
## Accuracy : 0.8197
## 95% CI : (0.7002, 0.9064)
## No Information Rate : 0.623
## P-Value [Acc > NIR] : 0.0007305
##
## Kappa : 0.5989
##
## Mcnemar's Test P-Value : 0.2278000
##
## Sensitivity : 0.9211
## Specificity : 0.6522
## Pos Pred Value : 0.8140
## Neg Pred Value : 0.8333
## Prevalence : 0.6230
## Detection Rate : 0.5738
## Detection Prevalence : 0.7049
## Balanced Accuracy : 0.7866
##
## 'Positive' Class : Tidak Sehat
##
Hasil didapatkan adalah terjadi overfitting yang tidak terlalu ekstrim antara jantung.train dan jantung.test pada matriks Pos Pred Value. Matriks Pos Pred Value di jantung.train sebesar 78.52% sedangkan pada jantung.test sebesar 81.40%.
Selanjutnya melakukan prediksi dengan menggunakan klasifikasi yaitu decision tree.
pred <- predict(jantung_dtree, jantung.test)table(pred, jantung.test$target)##
## pred Sehat Tidak Sehat
## Sehat 15 3
## Tidak Sehat 8 35
Hasil prediksi menggunakan Decision Tree terdapat pasien Sehat yang benar di prediksi Sehat sebanyak 15 orang dan salah diprediksi Tidak Sehat sebanyak 3 orang. Dan untuk pasien yang Tidak Sehat yang diprediksi benar **Tidak Sehat sebanyak 35 orang dan pasien yang salah diprediksi sebanyak 8 pasien.
Selanjutnya dari klasifikasi decision tree, maka akan melakukan uji coba lagi, agar dapat memperoleh nilai Pos Pred Value yang begitu tinggi dan baik. Klasifikasi yang ingin di terapkan yaitu klasifikasi Random Forest.
RNGkind(sample.kind = "Rounding")## Warning in RNGkind(sample.kind = "Rounding"): non-uniform 'Rounding' sampler
## used
set.seed(418)
control <- trainControl(method = "repeatedcv", number = 4, repeats = 3)
modelRF_jantung <- train(target ~ ., data = jantung.train,
method = "rf",
trainControl = control)
saveRDS(modelRF_jantung, "modelRF_jantung.RDS")modelRF_jantung <- readRDS("modelRF_jantung.RDS")modelRF_jantung## Random Forest
##
## 242 samples
## 12 predictor
## 2 classes: 'Sehat', 'Tidak Sehat'
##
## No pre-processing
## Resampling: Bootstrapped (25 reps)
## Summary of sample sizes: 242, 242, 242, 242, 242, 242, ...
## Resampling results across tuning parameters:
##
## mtry Accuracy Kappa
## 2 0.5848558 0.1713838
## 154 0.7540696 0.5076202
## 307 0.7362990 0.4709659
##
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was mtry = 154.
Melihat Out of Bag Error yang dihasilkan dari klasifikasi random forest yang dihasilkan.
modelRF_jantung$finalModel##
## Call:
## randomForest(x = x, y = y, mtry = param$mtry, trainControl = ..1)
## Type of random forest: classification
## Number of trees: 500
## No. of variables tried at each split: 154
##
## OOB estimate of error rate: 24.38%
## Confusion matrix:
## Sehat Tidak Sehat class.error
## Sehat 84 31 0.2695652
## Tidak Sehat 28 99 0.2204724
plot(modelRF_jantung$finalModel)
legend("topright", colnames(modelRF_jantung$finalModel$err.rate),
col = 1:6, cex = 0.8, fill = 1:6)Dengan mrty = 154, variabel yang didapatkan model dapat memprediksi data dengan pasien Tidak Sehat atau berpenyakit jantung sebanyak 99 orang sedangkan yang salah diprediksi pasien Sehat sebanyak 84 orang, seharusnya pasien Tidak Sehat sebanyak 28 pasien. Sedangkan pasien sehat yang diprediksi Tidak Sehat sebanyak 31 orang.
Nilai Out of Bag pada modelRF_jantung sebesar 24.38%. Dengan kata lain, akurasi model pada unseen data adalah 75,62%.
Melihat prediktor apa saja yang paling berpengaruh terhadap target.
varImp(modelRF_jantung)## rf variable importance
##
## only 20 most important variables shown (out of 307)
##
## Overall
## thal2 100.000
## oldpeak 89.518
## exangIya 86.588
## thal3 59.877
## cp2 35.013
## ca1 23.244
## slope2 19.902
## restecg1 19.240
## cp3 18.779
## ca2 16.175
## ca3 13.333
## sexMale 12.250
## slope1 11.479
## chol335 8.317
## chol237 7.960
## chol330 7.816
## cp1 7.761
## chol243 7.316
## thalach154 6.989
## thalach152 6.767
Dari tabel diatas, dapat diketahui prediktor yang paling tinggi pengaruhnya terhadap target adalah thal2 yang mendapatkan hasil overall sebanyak 100.
jantung_predict_RF <- predict(modelRF_jantung, jantung.test)ConfusionMatrix dari klasifikasi Random Forest
confusionMatrix(data = jantung_predict_RF,
reference = jantung.test$target, positive = "Tidak Sehat")## Confusion Matrix and Statistics
##
## Reference
## Prediction Sehat Tidak Sehat
## Sehat 17 5
## Tidak Sehat 6 33
##
## Accuracy : 0.8197
## 95% CI : (0.7002, 0.9064)
## No Information Rate : 0.623
## P-Value [Acc > NIR] : 0.0007305
##
## Kappa : 0.6128
##
## Mcnemar's Test P-Value : 1.0000000
##
## Sensitivity : 0.8684
## Specificity : 0.7391
## Pos Pred Value : 0.8462
## Neg Pred Value : 0.7727
## Prevalence : 0.6230
## Detection Rate : 0.5410
## Detection Prevalence : 0.6393
## Balanced Accuracy : 0.8038
##
## 'Positive' Class : Tidak Sehat
##
Hasil confusion matrix di atas, Berdasarkan hasil interpretasi dengan menggunakan confusion matrix di atas, diperoleh hasil bahwa kemampuan model dalam menebak target “1” atau “Tidak Sehat” sebesar 86,84% untuk Recall/Sensitivity, 73,91% untuk Specificity, 84,62% untuk Precision/Pos Pred Value dan 81,97% untuk Accuracy.
Dari klasifikasi yang telah digunakan logistic Regression, K-Nearst Neighboor, Naive Bayes, Decision Tree dan Random Forest. Jika hasil yang diperoleh dengan menggunggulkan Pos Pred Value / Precision masing-masing yaitu :
Logistic Regression : 89.74%
Naive Bayes : 87.18%
K-Nearst Neighboor : 84.21%
Decision Tree : 78.52%
Random Forest : 84.62%
Jadi, untuk dapat digunakan dalam mendeteksi pasien denga resiko memiliki penyakit jantung atau dalam kondisi tidak sehat maka dapat menggunakan Logistic Regression, karena dengan Logistic Regression akan menghasilkan Pos Pred Value yang cukup tinggi dibanding dengan model klasifikasi yang lain yaitu sebesar 89.74%.