# clear-up the environment
rm(list = ls())
# chunk options
knitr::opts_chunk$set(
message = FALSE,
warning = FALSE,
fig.align = "center",
comment = "#>"
)
options(scipen = 123)Salam bahagia kepada para pembaca yang membaca report ini. Dalam report ini saya akan membuat model Klasifikasi logistic regression dan model KNN untuk memprediksi apakah seorang pasien memiliki masalah jantung atau tidak. dimana model Logistic regression dan KNN adalah model supervised learning untuk klasifikasi.
kita akan memasukan library packages yang akan kita gunakan dalam LBB ini.
library(dplyr)
library(class)
library(tidyr)
library(gmodels)
library(gtools)
library(caret)heart <- read.csv("heart.csv")
glimpse(heart)#> 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~
Informasi penting dalam data :
Disini kita akan mengubah tipe data yang tidak sesuai dan memeriksa apakah ada missing value dalam dataset.
heart_new <- heart %>%
mutate(sex = as.character(sex),
sex = ifelse(sex == "0","Female", "Male"),
sex = as.factor(sex),
fbs = as.character(fbs),
fbs = ifelse(fbs == "0","False", "True"),
fbs = as.factor(fbs),
exang = as.character(exang),
exang = ifelse(exang == "0","No", "Yes"),
exang = as.factor(exang),
target = as.character(target),
target = ifelse(target == "0","Health", "Sick"),
target = as.factor(target)) %>%
mutate_at(.vars = c("cp", "fbs", "restecg", "slope", "ca", "thal"), .funs = as.factor)
glimpse(heart_new)#> Rows: 303
#> Columns: 14
#> $ ï..age <int> 63, 37, 41, 56, 57, 57, 56, 44, 52, 57, 54, 48, 49, 64, 58, 5~
#> $ 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 <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 <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 <int> 150, 187, 172, 178, 163, 148, 153, 173, 162, 174, 160, 139, 1~
#> $ exang <fct> No, No, No, No, Yes, No, No, No, No, No, No, No, No, Yes, No,~
#> $ 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> Sick, Sick, Sick, Sick, Sick, Sick, Sick, Sick, Sick, Sick, S~
sekarang kita akan memeriksa apakah adanya missing value dalam data
anyNA(heart_new)#> [1] FALSE
colSums(is.na(heart_new))#> ï..age sex cp trestbps chol fbs restecg thalach
#> 0 0 0 0 0 0 0 0
#> exang oldpeak slope ca thal target
#> 0 0 0 0 0 0
dapat dilihat kalau data yang diberikan sudahlah bersih tanpa adanya missing value sehingga data sudah bisa langsung dibuat model tanpa cleansing.
sebelum membuat model, kita ingin terlebih dahulu melihat apakah proposi data yang ada sudah seimbang atau tidak.
prop.table(table(heart_new$target))#>
#> Health Sick
#> 0.4554455 0.5445545
proposi yang ada masih bisa dikategorikan sebagai seimbang. sehinga kita tidak perlu untuk melakukan penyesuaian jumlah data.
Disini kita akan memecah jumlah data menjadi 2 bagian yaitu data train untuk melatih model dan data test untuk melakukan prediksi. pemecahan dilakukan dengan jumlah 70% data train dan 30% data test
RNGkind(sample.kind = "Rounding")
set.seed(417)
idx <- sample(nrow(heart_new), nrow(heart_new)*0.7)
heart_train <- heart_new[idx,]
heart_test <- heart_new[-idx,]prop.table(table(heart_train$target))#>
#> Health Sick
#> 0.4528302 0.5471698
prop.table(table(heart_test$target))#>
#> Health Sick
#> 0.4615385 0.5384615
proposi data train dan data test sudah seimbang. sekarang kita akan mulai membuat model.
Kita akan mulai dengan membuat model logistic regression dengan function glm(). target = target, prediktor akan kita pilih yang kita anggap mempengaruhi apakah prediktor tersebut mempengaruhi seseorang terkena penyakit jantung.
model_heart <- glm(target ~ ï..age + sex + trestbps + chol + fbs + thalach + exang + oldpeak + ca, data = heart_train, family = "binomial")
summary(model_heart)#>
#> Call:
#> glm(formula = target ~ ï..age + sex + trestbps + chol + fbs +
#> thalach + exang + oldpeak + ca, family = "binomial", data = heart_train)
#>
#> Deviance Residuals:
#> Min 1Q Median 3Q Max
#> -2.6461 -0.3579 0.1577 0.4897 2.0338
#>
#> Coefficients:
#> Estimate Std. Error z value Pr(>|z|)
#> (Intercept) 1.52499 2.98496 0.511 0.609429
#> ï..age 0.02772 0.03015 0.919 0.357908
#> sexMale -1.87723 0.54831 -3.424 0.000618 ***
#> trestbps -0.01002 0.01321 -0.758 0.448267
#> chol -0.01353 0.00548 -2.469 0.013546 *
#> fbsTrue 0.81222 0.60685 1.338 0.180758
#> thalach 0.03563 0.01405 2.536 0.011213 *
#> exangYes -2.22027 0.53489 -4.151 0.00003312 ***
#> oldpeak -0.66531 0.24109 -2.760 0.005787 **
#> ca1 -2.62533 0.57431 -4.571 0.00000485 ***
#> ca2 -3.16290 0.77351 -4.089 0.00004332 ***
#> ca3 -2.40633 0.91657 -2.625 0.008656 **
#> ca4 13.19262 1005.27414 0.013 0.989529
#> ---
#> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#>
#> (Dispersion parameter for binomial family taken to be 1)
#>
#> Null deviance: 292.00 on 211 degrees of freedom
#> Residual deviance: 140.55 on 199 degrees of freedom
#> AIC: 166.55
#>
#> Number of Fisher Scoring iterations: 14
dilihat dari summary model yang sudah kita buat. masih ada beberapa prediktor yang tidak memiliki korelasi yang kuat. oleh karena itu, kita akan melakukan fitting model stepwise backward.
model_heart_back <- step(model_heart, direction = "backward", trace = F)
summary(model_heart_back)#>
#> Call:
#> glm(formula = target ~ sex + chol + thalach + exang + oldpeak +
#> ca, family = "binomial", data = heart_train)
#>
#> Deviance Residuals:
#> Min 1Q Median 3Q Max
#> -2.5652 -0.3784 0.1573 0.4906 2.2048
#>
#> Coefficients:
#> Estimate Std. Error z value Pr(>|z|)
#> (Intercept) 2.150905 2.253729 0.954 0.339893
#> sexMale -1.746675 0.518088 -3.371 0.000748 ***
#> chol -0.012026 0.005157 -2.332 0.019701 *
#> thalach 0.029999 0.012819 2.340 0.019278 *
#> exangYes -2.236655 0.525195 -4.259 0.0000206 ***
#> oldpeak -0.659266 0.229736 -2.870 0.004109 **
#> ca1 -2.515567 0.561283 -4.482 0.0000074 ***
#> ca2 -2.832273 0.716643 -3.952 0.0000775 ***
#> ca3 -2.087391 0.855958 -2.439 0.014742 *
#> ca4 13.140749 984.179544 0.013 0.989347
#> ---
#> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#>
#> (Dispersion parameter for binomial family taken to be 1)
#>
#> Null deviance: 292.00 on 211 degrees of freedom
#> Residual deviance: 143.45 on 202 degrees of freedom
#> AIC: 163.45
#>
#> Number of Fisher Scoring iterations: 14
setelah fitting kita mendapatkan AIC yang lebih kecil dan kita juga melihat kalau semua prediktor sudah memiliki signifikasi yang kuat dengan target.
kita sudah membuat sebuah model_heart_backwards yang akan kita gunakan untuk melakukan prediksi kepada data test yang sudah kita pisah diawal.
heart_test$pred.Risk <- predict(model_heart_back, heart_test, type = "response")
heart_test$pred_heart <-factor(ifelse(heart_test$pred.Risk > 0.5, "Sick","Health"))
heart_test[1:10, c("pred_heart", "target")]setelah membuat model, kita ingin mengevaluasi model yang sudah kita buat
confusionMatrix(heart_test$pred_heart, heart_test$target, positive = "Sick")#> Confusion Matrix and Statistics
#>
#> Reference
#> Prediction Health Sick
#> Health 33 13
#> Sick 9 36
#>
#> Accuracy : 0.7582
#> 95% CI : (0.6572, 0.8419)
#> No Information Rate : 0.5385
#> P-Value [Acc > NIR] : 0.00001259
#>
#> Kappa : 0.5169
#>
#> Mcnemar's Test P-Value : 0.5224
#>
#> Sensitivity : 0.7347
#> Specificity : 0.7857
#> Pos Pred Value : 0.8000
#> Neg Pred Value : 0.7174
#> Prevalence : 0.5385
#> Detection Rate : 0.3956
#> Detection Prevalence : 0.4945
#> Balanced Accuracy : 0.7602
#>
#> 'Positive' Class : Sick
#>
Metrics yang akan digunakan adalah Precision dengan alasan kalau dalam kasus prediksi masalah jantung. kita tidak bisa mengambil resiko sehingga kita membutuhkan akurasi yang sangat tepat agar tidak terjadi kesalahan dimana model kita mendapatkan hasil precision sebesar 80%
kita sudah membuat model untuk Logistic Regression. sekarang kita akan membuat model KNN dan membandingkan kedua model.
heart2 <- heart %>%
mutate(target = as.character(target),
target = ifelse(target == "0","Health", "Sick"),
target = as.factor(target))RNGkind(sample.kind = "Rounding")
set.seed(417)
idx2 <- sample(nrow(heart2), nrow(heart2)*0.7)
heart2_train <- heart2[idx2,] #70% data train
heart2_test <- heart2[-idx2,] #30% data testKarena tiap prediktor memiliki range yang berbeda maka kita perlu melakukan scaling agar range tiap prediktor tidak berbeda jauh
heart_train_scale <- heart2_train %>%
select_if(is.numeric) %>%
scale()
heart_test_scale <- heart2_test %>%
select_if(is.numeric) %>%
scale(center = attr(heart_train_scale, "scaled:center"),
scale = attr(heart_train_scale, "scaled:scale"))
heart_train_target <- heart2_train$target
heart_test_target <- heart2_test$targetdisini kita akan melihat nilai K untuk permodelan.
k_heart <- sqrt(nrow(heart_train_scale))
k_heart#> [1] 14.56022
selanjutnya kita membuat model KNN.
knn_heart <- knn(train = heart_train_scale, test = heart_test_scale, cl= heart_train_target, k=14)selanjutnya kita akan melakukan evaluasi model KNN yang sudah kita bentuk
confusionMatrix(data= knn_heart, reference = heart_test_target, positive = "Sick")#> Confusion Matrix and Statistics
#>
#> Reference
#> Prediction Health Sick
#> Health 32 5
#> Sick 10 44
#>
#> Accuracy : 0.8352
#> 95% CI : (0.7427, 0.9047)
#> No Information Rate : 0.5385
#> P-Value [Acc > NIR] : 0.000000002394
#>
#> Kappa : 0.6655
#>
#> Mcnemar's Test P-Value : 0.3017
#>
#> Sensitivity : 0.8980
#> Specificity : 0.7619
#> Pos Pred Value : 0.8148
#> Neg Pred Value : 0.8649
#> Prevalence : 0.5385
#> Detection Rate : 0.4835
#> Detection Prevalence : 0.5934
#> Balanced Accuracy : 0.8299
#>
#> 'Positive' Class : Sick
#>
dengan model KNN kita mendapatkan nilai precision sebesar 81.4%
dengan evalusi model yang sudah kita lakukan. kita mendapatkan hasil nilai precision logistic regression sebesar 80% dan model KNN sebesar 81.4%. model KNN mendapatkan hasil presisi yang lebih tinggi daripada model logistic regression.
jika saya seorang dokter. maka saya akan memakai model KNN untuk denga metric precision yang lebih tinggi. karena menghadapi penyakit jantung yang bersifat fatal, saya tidak ingin salah memprediksi agar saya tidak salah dalam menangani pasien.