Cardiovascular Disease - Prediksi

Pengantar

Latar Belakang

Penyakit kardiovaskular merupakan penyakit yang tidak boleh dianggap remeh. Menurut data World Health Organization penyakit kardiovaskular dapat menyebabkan kematian 17,6 juta orang tiap tahunnya. Banyak faktor yang dapat menyebabkan seseorang terkena penyakit kardiovaskular, seperti pola makan tidak sehat, kebiasaan merokok, kebiasaan kurang gerak dan jarang berolahraga, konsumsi alkohol berlebihan, dsb.

Kita akan menggunakan dataset Cardiovascular Disease untuk mengetahui kemungkinan seseorang divonis penyakit kardiovaskular berdasarkan variabel yang ada dengan menggunakan model machine learning. Kita akan membuat model Logistic Regression dan K-NN dan membandingkan performa kedua model tersebut.

Tentang Dataset

Dataset Cardiovascular Disease merupakan data pasien yang diambil pada saat melakukan pemeriksaan kesehatan. Dataset memiliki 70.000 baris dan 13 kolom yang terdiri dari.

id : Nomor ID
age : Umur dalam hari
gender : Jenis kelamin, 1 = Perempuan 2 = Laki-laki
height : Tinggi badan dalam cm
weight : Berat badan dalam kg
ap_hi : Tekanan darah sistolik
ap_lo : Tekanan darah diastolik
cholesterol : Kadar kolesterol, 1 = Normal 2 = Di Atas Normal 3 = Jauh di Atas Normal
gluc : Kadar gula darah, 1 = Normal 2 = Di Atas Normal 3 = Jauh di Atas Normal
smoke : Apakah pasien merokok?, 1 = Ya 2 = Tidak
alco : Apakah pasien peminum?, 1 = Ya 2 = Tidak
active : Apakah pasien aktif bergerak?, 1 = Ya 2 = Tidak
cardio : Penyakit kardiovaskular, 1 = Ya 2 = Tidak

Set Up

Yang pertama kita lakukan adalah memanggil library yang dibutuhkan.

library(tidyverse)
library(GGally)
library(reactable)
library(car)
library(caret)
library(class)

Data Wrangling

Selanjutnya kita input dataset Cardiovascular Disease menggunakan fungsi read.csv() dan menyimpannya ke dalam variabel cardio_raw.

cardio_raw <- read.csv("cardio_train.csv")

reactable(cardio_raw)

Variabel dan nilai pada dataset yang kita punya masih dalam bentuk satu kesatuan. Kita perlu untuk memisahkan mereka menjadi 13 kolom menggunakan fungsi separate().

cardio <-
  cardio_raw %>% 
  separate(id.age.gender.height.weight.ap_hi.ap_lo.cholesterol.gluc.smoke.alco.active.cardio, 
           c("id", "age", "gender", "height", "weight", "decimal", "ap_hi", "ap_lo", "cholesterol", "gluc", "smoke", "alco", "active", "cardio")) %>% 
  select(-c(decimal, id)) %>% 
  head(200)

reactable(cardio)

Setelah itu kita melakukan glimpse() untuk melihat apakah tipe data sudah sesuai.

glimpse(cardio)
## Rows: 200
## Columns: 12
## $ age         <chr> "18393", "20228", "18857", "17623", "17474", "21914", "221…
## $ gender      <chr> "2", "1", "1", "2", "1", "1", "1", "2", "1", "1", "1", "2"…
## $ height      <chr> "168", "156", "165", "169", "156", "151", "157", "178", "1…
## $ weight      <chr> "62", "85", "64", "82", "56", "67", "93", "95", "71", "68"…
## $ ap_hi       <chr> "110", "140", "130", "150", "100", "120", "130", "130", "1…
## $ ap_lo       <chr> "80", "90", "70", "100", "60", "80", "80", "90", "70", "60…
## $ cholesterol <chr> "1", "3", "3", "1", "1", "2", "3", "3", "1", "1", "1", "1"…
## $ gluc        <chr> "1", "1", "1", "1", "1", "2", "1", "3", "1", "1", "1", "1"…
## $ smoke       <chr> "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"…
## $ alco        <chr> "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"…
## $ active      <chr> "1", "1", "0", "1", "0", "0", "1", "1", "1", "0", "1", "1"…
## $ cardio      <chr> "0", "1", "1", "1", "0", "0", "0", "1", "0", "0", "0", "0"…

Terdapat beberapa variabel yang perlu untuk diubah tipe datanya. Dalam hal ini kolom age, height, weight, ap_hi, ap_lo menjadi tipe data numerik dan kolom gender, cholesterol, gluc, smoke, alco, active, cardio menjadi factor.

cardio <- 
  cardio %>% 
  mutate(age = as.numeric(age),
         height = as.numeric(height),
         weight = as.numeric(weight),
         ap_hi = as.numeric(ap_hi),
         ap_lo = as.numeric(ap_lo),
         gender = as.factor(gender),
         cholesterol = as.factor(cholesterol),
         gluc = as.factor(gluc),
         smoke = as.factor(smoke),
         alco = as.factor(alco),
         active = as.factor(active),
         cardio = as.factor(cardio))

glimpse(cardio)
## Rows: 200
## Columns: 12
## $ age         <dbl> 18393, 20228, 18857, 17623, 17474, 21914, 22113, 22584, 17…
## $ gender      <fct> 2, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 2, 1, 2, 2, 1, 1, 1, 2…
## $ height      <dbl> 168, 156, 165, 169, 156, 151, 157, 178, 158, 164, 169, 173…
## $ weight      <dbl> 62, 85, 64, 82, 56, 67, 93, 95, 71, 68, 80, 60, 60, 78, 95…
## $ ap_hi       <dbl> 110, 140, 130, 150, 100, 120, 130, 130, 110, 110, 120, 120…
## $ ap_lo       <dbl> 80, 90, 70, 100, 60, 80, 80, 90, 70, 60, 80, 80, 80, 70, 9…
## $ cholesterol <fct> 1, 3, 3, 1, 1, 2, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
## $ gluc        <fct> 1, 1, 1, 1, 1, 2, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1…
## $ smoke       <fct> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1…
## $ alco        <fct> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0…
## $ active      <fct> 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1…
## $ cardio      <fct> 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0…

Setelah tipe data sudah sesuai selanjutnya kita melihat apakah terdapat missing value menggunakan anyNA().

anyNA(cardio)
## [1] FALSE

Tidak terdapat missing value pada dataset Cardiovascular Disease.

Exploratory Data Analysis

Dalam tahap Exploratory Data Analysis yang pertama kita lakukan adalah summary(). Hal ini bertujuan untuk melihat bagaimana persebaran dan karakteristik dataset yang kita punya.

summary(cardio)
##       age        gender      height          weight           ap_hi      
##  Min.   :14321   1:118   Min.   :148.0   Min.   : 45.00   Min.   : 90.0  
##  1st Qu.:18081   2: 82   1st Qu.:158.0   1st Qu.: 63.75   1st Qu.:120.0  
##  Median :19660           Median :165.0   Median : 71.00   Median :120.0  
##  Mean   :19542           Mean   :165.2   Mean   : 73.17   Mean   :126.8  
##  3rd Qu.:21756           3rd Qu.:170.0   3rd Qu.: 82.00   3rd Qu.:140.0  
##  Max.   :23589           Max.   :188.0   Max.   :115.00   Max.   :190.0  
##      ap_lo        cholesterol gluc    smoke   alco    active  cardio 
##  Min.   : 60.00   1:147       1:174   0:179   0:191   0: 46   0:100  
##  1st Qu.: 80.00   2: 32       2: 15   1: 21   1:  9   1:154   1:100  
##  Median : 80.00   3: 21       3: 11                                  
##  Mean   : 81.38                                                      
##  3rd Qu.: 90.00                                                      
##  Max.   :110.00

Kita juga perlu untuk melihat proporsi jumlah variabel target apakah seimbang atau tidak. Hal ini bertujuan agar memaksimalkan performa model dalam bekerja.

prop.table(table(cardio$cardio))
## 
##   0   1 
## 0.5 0.5

Proporsi jumlah variabel target sudah seimbang.

Untuk melihat korelasi antar variabel kita lakukan ggcorr().

ggcorr(cardio, label = T)

Berdasarkan plot di atas korelasi signifikan positif terdapat pada variabel ap_lo dan ap_hi. Sedangkan variabel ap_hi dan height sama sekali tidak menggambarkan korelasi. Untuk height dan age berkorelasi negatif tetapi tidak signifikan.

Cross Validation

Cross Validation adalah proses dimana kita membagi dataset yang kita punya untuk melatih dan menguji model. Kita akan mengambil sample Cardiovascular Disease secara random untuk dimasukkan ke dalam variabel cardio_train sebanyak 80% dan sisanya dimasukkan ke dalam cardio_test.

RNGkind(sample.kind = "Rounding")  
set.seed(417) 

index <- sample(x = nrow(cardio), size = nrow(cardio)*0.8)

cardio_train <- cardio[index,]
cardio_test <- cardio[-index,]

Modeling

Kita akan membuat beberapa model dan menguji performanya.

Tanpa Prediktor

Model yang pertama kita buat adalah model tanpa prediktor menggunakan glm() dan dimasukkan dalam variabel model_1.

model_1 <- glm(formula = cardio ~ 1, 
               data = cardio_train, 
               family = "binomial")

summary(model_1)
## 
## Call:
## glm(formula = cardio ~ 1, family = "binomial", data = cardio_train)
## 
## Deviance Residuals: 
##    Min      1Q  Median      3Q     Max  
## -1.156  -1.156  -1.156   1.199   1.199  
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)
## (Intercept) -0.05001    0.15816  -0.316    0.752
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 221.71  on 159  degrees of freedom
## Residual deviance: 221.71  on 159  degrees of freedom
## AIC: 223.71
## 
## Number of Fisher Scoring iterations: 3

Nilai (Intercept) pada model tanpa prediktor merupakan nilai log of odds dari variabel target 1 atau pasien yang divonis penyakit cardiovascular. Yang perlu kita perhatikan juga untuk model tanpa prediktor adalah nilai Null Deviance. Semakin kecil nilai Null Deviance semakin baik sebuah model. Yang terakhir adalah nilai AIC yang merupakan jumlah informasi yang hilang. Sama seperti Null Deviance semakin kecil nilai AIC semakin baik sebuah model.

Karena nilai (Intercept) tidak dapat diinterpretasikan maka kita perlu mengubahnya ke odds menggunakan fungsi exp().

exp(-0.05001)
## [1] 0.9512199

Dari nilai di atas kita dapat menginterpretasikan 0,95 kali lebih mungkin pasien divonis penyakit kardiovascular dibanding dengan yang tidak.

Prediktor Kategorik

Setelah membuat model tanpa prediktor kita akan membuat model dengan prediktor kategorik. Seluruh variabel bertipe factor kita jadikan prediktor dan menyimpannya ke dalam variabel model_cat.

model_cat <- glm(formula = cardio ~ gender + cholesterol + gluc + smoke + alco + active, 
                 data = cardio_train, 
                 family = "binomial")

summary(model_cat)
## 
## Call:
## glm(formula = cardio ~ gender + cholesterol + gluc + smoke + 
##     alco + active, family = "binomial", data = cardio_train)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -2.3889  -1.0386  -0.7144   1.2883   1.4002  
## 
## Coefficients:
##              Estimate Std. Error z value Pr(>|z|)    
## (Intercept)  -0.50109    0.38319  -1.308 0.190975    
## gender2       0.07872    0.37423   0.210 0.833400    
## cholesterol2 -0.17417    0.55010  -0.317 0.751540    
## cholesterol3  2.88660    0.87292   3.307 0.000944 ***
## gluc2         0.46248    0.68182   0.678 0.497584    
## gluc3        -0.85663    0.94311  -0.908 0.363717    
## smoke1        0.16431    0.62552   0.263 0.792794    
## alco1         0.83047    0.96438   0.861 0.389161    
## active1       0.16543    0.40221   0.411 0.680848    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 221.71  on 159  degrees of freedom
## Residual deviance: 200.30  on 151  degrees of freedom
## AIC: 218.3
## 
## Number of Fisher Scoring iterations: 4

Karena kita membuat model dengan prediktor, nilai yang harus kita perhatikan adalah nilai Residual Deviance. Nilai tersebut lebih kecil dari model sebelumnya. Begitu juga dengan nilai AIC yang lebih kecil dari model sebelumnya. Ini mengindikasikan model semakin baik.

Berbeda dengan yang di atas kita perlu untuk mengambil satu contoh variabel yang ingin kita interpretasikan. Kita memilih variabel alco1 dengan nilai log of odds 0,83047.

exp(0.83047)
## [1] 2.294397

Berdasarkan hasil di atas kita dapat menginterpretasikan bahwa pasien yang merupakan seorang peminum 2,29 kali lebih mungkin divonis penyakit kardiovaskular dibandingkan dengan yang tidak.

Prediktor Numerik

Model ketiga adalah model dengan prediktor numerik. Sama seperti prediktor kategorik semua variabel bertipe data numerik kita jadikan prediktor.

model_num <- glm(formula = cardio ~ weight + age + height + ap_hi + ap_lo, 
                 data = cardio_train, 
                 family = "binomial")

summary(model_num)
## 
## Call:
## glm(formula = cardio ~ weight + age + height + ap_hi + ap_lo, 
##     family = "binomial", data = cardio_train)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -2.4550  -0.9693  -0.4350   0.9559   2.0931  
## 
## Coefficients:
##               Estimate Std. Error z value Pr(>|z|)  
## (Intercept) -1.078e+01  4.334e+00  -2.487   0.0129 *
## weight       3.532e-03  1.468e-02   0.241   0.8098  
## age          1.116e-04  7.618e-05   1.465   0.1429  
## height      -4.388e-03  2.329e-02  -0.188   0.8506  
## ap_hi        4.167e-02  1.915e-02   2.176   0.0296 *
## ap_lo        4.653e-02  3.109e-02   1.497   0.1345  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 221.71  on 159  degrees of freedom
## Residual deviance: 183.28  on 154  degrees of freedom
## AIC: 195.28
## 
## Number of Fisher Scoring iterations: 4

Dengan menggunakan prediktor numerik nilai Residual Deviance dan AIC lebih kecil dari model yang sebelumnya. Ini mengindikasikan model semakin baik.

Kita mengambil variabel age dengan nilai log of odds 1,116e-04 untuk diinterpretasikan.

exp(1.116e-04)
## [1] 1.000112

Dari hasil di atas dapat diinterpretasikan bahwa dengan meningkatnya satu point dari nilai variabel age maka satu kali lebih mungkin pasien divonis penyakit kardiovaskular.

Semua

Yang terakhir kita membuat model dengan variabel bertipe data kategorik dan numerik dijadikan sebagai prediktor.

model_all <- glm(formula = cardio ~ ., 
                 data = cardio_train, 
                 family = "binomial")

summary(model_all)
## 
## Call:
## glm(formula = cardio ~ ., family = "binomial", data = cardio_train)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -2.2473  -0.8568  -0.4118   0.8944   2.3008  
## 
## Coefficients:
##                Estimate Std. Error z value Pr(>|z|)  
## (Intercept)  -1.257e+01  5.153e+00  -2.438   0.0148 *
## age           1.298e-04  8.272e-05   1.569   0.1167  
## gender2      -4.584e-02  4.818e-01  -0.095   0.9242  
## height        9.173e-03  2.809e-02   0.326   0.7440  
## weight       -2.553e-03  1.641e-02  -0.156   0.8763  
## ap_hi         2.961e-02  2.009e-02   1.474   0.1404  
## ap_lo         5.720e-02  3.396e-02   1.684   0.0921 .
## cholesterol2 -5.428e-01  6.555e-01  -0.828   0.4076  
## cholesterol3  2.335e+00  9.072e-01   2.573   0.0101 *
## gluc2         4.694e-01  8.226e-01   0.571   0.5682  
## gluc3        -4.793e-01  9.956e-01  -0.481   0.6302  
## smoke1       -1.071e-01  6.885e-01  -0.156   0.8763  
## alco1         5.733e-01  1.072e+00   0.535   0.5926  
## active1       1.749e-01  4.567e-01   0.383   0.7017  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 221.71  on 159  degrees of freedom
## Residual deviance: 171.09  on 146  degrees of freedom
## AIC: 199.09
## 
## Number of Fisher Scoring iterations: 5

Kita dapat melihat nilai Residual Deviance dan nilai AIC adalah nilai terkecil diantara model-model sebelumnya. Kita dapat menginterpretasikan model tersebut adalah model yang terbaik.

Sebelum kita melakukan prediksi kita melakukan uji asumsi Multicollinearity. Kita perlu mengetahui apakah antar prediktor memiliki korelasi kuat hingga satu atau minus satu. Hal ini untuk menghindari variabel redundan pada model. Kita dapat menggunakan fungsi vif() seperti pada chunk di bawah.

vif(model_all)
##                 GVIF Df GVIF^(1/(2*Df))
## age         1.146507  1        1.070751
## gender      1.637035  1        1.279467
## height      1.684248  1        1.297786
## weight      1.543854  1        1.242519
## ap_hi       2.151095  1        1.466661
## ap_lo       2.224869  1        1.491599
## cholesterol 2.282557  2        1.229152
## gluc        2.292236  2        1.230452
## smoke       1.300683  1        1.140475
## alco        1.120498  1        1.058536
## active      1.086204  1        1.042211

Berdasarkan hasil vif() di atas tidak terdapat nilai yang lebih besar dari 10. Maka bisa diasumsikan tidak ada multicollinearity atau uji asumsi terpenuhi.

Prediction

Setelah membuat model kita melakukan prediksi.

Tanpa Prediktor

Pertama melakukan prediksi menggunakan model tanpa prediktor. Kita menggunakan fungsi predict() pada data baru cardio_test.

pred_prob <- predict(object = model_1, 
                     newdata = cardio_test, 
                     type = "response")

head(pred_prob)
##     11     15     29     43     46     47 
## 0.4875 0.4875 0.4875 0.4875 0.4875 0.4875

Kita perlu mengklasifikasikan hasil prediksi untuk mengetahui jumlah pasien yang kemungkinan divonis penyakit kardiovaskular. Kita menggunakan fungsi ifelse() dan menetapkan threshold 0,5.

pred_label <- ifelse(pred_prob > 0.5, yes = 1, no = 0)

pred_label <- as.factor(pred_label)

head(pred_label)
## 11 15 29 43 46 47 
##  0  0  0  0  0  0 
## Levels: 0

Pada enam data teratas hasil prediksi menunjukkan tidak terdapat pasien dengan kemungkinan divonis penyakit kardiovaskular.

Prediktor Kategorik

Yang perlu kita lakukan selanjutnya melakukan prediksi menggunakan model dengan prediktor kategorik.

pred_prob2 <- predict(object = model_cat, 
                      newdata = cardio_test, 
                      type = "response")

head(pred_prob2)
##        11        15        29        43        46        47 
## 0.4168641 0.6765231 0.4361154 0.4361154 0.4168641 0.3752341

Sama dengan sebelumnya kita menggunakan threshold 0,5.

pred_label2 <- ifelse(pred_prob2 > 0.5, yes = 1, no = 0)

pred_label2 <- as.factor(pred_label2)

head(pred_label2)
## 11 15 29 43 46 47 
##  0  1  0  0  0  0 
## Levels: 0 1

Dengan menggunakan model dengan prediktor kategorik pada enam data teratas menunjukkan satu pasien yang kemungkinan divonis penyakit kardiovaskular.

Prediktor Numerik

Selanjutnya menggunakan model dengan prediktor numerik yaitu model_num.

pred_prob3 <- predict(object = model_num, 
                      newdata = cardio_test, 
                      type = "response")

head(pred_prob3)
##        11        15        29        43        46        47 
## 0.4994445 0.4968867 0.2836613 0.7560348 0.6695294 0.7952308

Kita kembali mengklasifikasikan nilai hasil prediksi dan menyimpannya dalam variabel pred_label3.

pred_label3 <- ifelse(pred_prob3 > 0.5, yes = 1, no = 0)

pred_label3 <- as.factor(pred_label3)

head(pred_label3)
## 11 15 29 43 46 47 
##  0  0  0  1  1  1 
## Levels: 0 1

Meningkat dari sebelumnya pada enam data teratas terdapat tiga pasien yang kemungkinan divonis penyakit kardiovaskular.

Semua

Yang terakhir adalah model dengan semua variabel dijadikan prediktor.

pred_prob4 <- predict(object = model_all, 
                      newdata = cardio_test, 
                      type = "response")

head(pred_prob4)
##        11        15        29        43        46        47 
## 0.5020859 0.5819560 0.2570082 0.7381329 0.6207697 0.5577139

Kita kembali melihat hasil klasifikasi nilai prediksi pred_prob4.

pred_label4 <- ifelse(pred_prob4 > 0.5, yes = 1, no = 0)

pred_label4 <- as.factor(pred_label4)

head(pred_label4)
## 11 15 29 43 46 47 
##  1  1  0  1  1  1 
## Levels: 0 1

Dengan model ini pada enam data teratas ada lima kemungkinan pasien yang divonis penyakit kardiovaskular.

Kita perlu melakukan evaluasi model menggunakan fungsi confusionMatrix() dari library caret. Kita akan membandingkan hasil prediksi dengan referensi data yang kita punya.

confusionMatrix(data = pred_label4, 
                reference = cardio_test$cardio, 
                positive = "1")
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  0  1
##          0 13  7
##          1  5 15
##                                           
##                Accuracy : 0.7             
##                  95% CI : (0.5347, 0.8344)
##     No Information Rate : 0.55            
##     P-Value [Acc > NIR] : 0.03859         
##                                           
##                   Kappa : 0.4             
##                                           
##  Mcnemar's Test P-Value : 0.77283         
##                                           
##             Sensitivity : 0.6818          
##             Specificity : 0.7222          
##          Pos Pred Value : 0.7500          
##          Neg Pred Value : 0.6500          
##              Prevalence : 0.5500          
##          Detection Rate : 0.3750          
##    Detection Prevalence : 0.5000          
##       Balanced Accuracy : 0.7020          
##                                           
##        'Positive' Class : 1               
## 

Dari hasil confusionMatrix() di atas kita dapat melihat rentang nilai yang cukup besar antara True Positive dan False Positive. Begitu juga dengan True negative dan False Negative. Ini menandakan model yang kita buat sudah cukup baik. Kita juga dapat melihat kebaikan dari sebuah model berdasarkan nilai Accuracynya. Model yang kita punya memiliki nilai Accuracy 0,7.

Model yang sudah kita buat memiliki performa yang cukup baik tetapi kita perlu melakukan evaluasi. Kita akan memanfaatkan metriks Sensitivity atau Recall dengan tujuan untuk meminimalisir prediksi yang salah terhadap pasien yang kemungkinan besar divonis penyakit kardiovaskular. Kita menurunkan nilai threshold menjadi 0,4 dan melihat hasil prediksinya kembali.

pred_label5 <- ifelse(pred_prob4 > 0.4, yes = 1, no = 0)

pred_label5 <- as.factor(pred_label5)

head(pred_label5)
## 11 15 29 43 46 47 
##  1  1  0  1  1  1 
## Levels: 0 1

Kita melakukan confusionMatrix() terhadap hasil prediksi yang baru.

confusionMatrix(data = pred_label5, 
                reference = cardio_test$cardio, 
                positive = "1")
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  0  1
##          0 12  6
##          1  6 16
##                                           
##                Accuracy : 0.7             
##                  95% CI : (0.5347, 0.8344)
##     No Information Rate : 0.55            
##     P-Value [Acc > NIR] : 0.03859         
##                                           
##                   Kappa : 0.3939          
##                                           
##  Mcnemar's Test P-Value : 1.00000         
##                                           
##             Sensitivity : 0.7273          
##             Specificity : 0.6667          
##          Pos Pred Value : 0.7273          
##          Neg Pred Value : 0.6667          
##              Prevalence : 0.5500          
##          Detection Rate : 0.4000          
##    Detection Prevalence : 0.5500          
##       Balanced Accuracy : 0.6970          
##                                           
##        'Positive' Class : 1               
## 

Setelah itu kita dapat melihat rentang nilai antara True Positive dan False Positive sudah cukup berkurang. Nilai Sensitivity bertambah menjadi 0,7273 dan Accuracy tidak berubah dari model sebelumnya.

K-NN

Kita akan membuat satu model lagi menggunakan metode K-NN atau K-Nearest Neighbor. Metode ini akan mengklasifikasikan data test berdasarkan karakteristik data train. Klasifikasi ditentukan berdasarkan kedekatan karakteristik yang diukur dengan cara Euclidean Distance atau menghitung jarak. Setelah itu dipilih menggunakan majority voting sesuai dengan nilai k yang sudah kita tentukan. Nilai k adalah jumlah data terdekat dari data test. Kita akan memisahkan terlebih dahulu variabel target dan variabel prediktor sebelum membuat model K-NN.

cardio_train_x <- cardio_train[,-c(2,7,8,9,10,11,12)]
cardio_test_x <- cardio_test[,-c(2,7,8,9,10,11,12)]

cardio_train_y <- cardio_train$cardio
cardio_test_y <- cardio_test$cardio

Setelah itu kita melakukan scaling pada variabel prediktor di data train dan data test.

cardio_train_xs <- scale(cardio_train_x)

cardio_test_xs <- scale(cardio_test_x , 
                        center = attr(cardio_train_xs,"scaled:center"), 
                        scale = attr(cardio_train_xs,"scaled:scale"))

Seperti yang sudah kita bahas sebelumnya kita perlu menentukan nilai k. Untuk mendapatkan nilai k yang optimal kita dapat mengakar kuadratkan jumlah baris pada data kita.

sqrt(nrow(cardio_train_xs))
## [1] 12.64911

Yang selanjutnya kita lakukan adalah melakukan prediksi menggunakan fungsi knn().

cardio_pred <- knn(train = cardio_train_xs, 
                   test = cardio_test_xs, 
                   cl = cardio_train_y, 
                   k = 13)

head(cardio_pred)
## [1] 1 0 0 1 1 1
## Levels: 0 1

Di atas kita dapat melihat hasil prediksi pada model yang telah kita buat.

Kita melakukan confusionMatrix() untuk evaluasi sama seperti model-model sebelumnya.

confusionMatrix(data = cardio_pred, 
                reference = cardio_test_y, 
                positive = "1")
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction  0  1
##          0 15  5
##          1  3 17
##                                           
##                Accuracy : 0.8             
##                  95% CI : (0.6435, 0.9095)
##     No Information Rate : 0.55            
##     P-Value [Acc > NIR] : 0.0008833       
##                                           
##                   Kappa : 0.6             
##                                           
##  Mcnemar's Test P-Value : 0.7236736       
##                                           
##             Sensitivity : 0.7727          
##             Specificity : 0.8333          
##          Pos Pred Value : 0.8500          
##          Neg Pred Value : 0.7500          
##              Prevalence : 0.5500          
##          Detection Rate : 0.4250          
##    Detection Prevalence : 0.5000          
##       Balanced Accuracy : 0.8030          
##                                           
##        'Positive' Class : 1               
## 

Model K-NN memiliki nilai Accuracy yang lebih tinggi dibanding dengan model-model yang sudah kita buat sebelumnya. Rentang nilai antara True Positive dan False Positive juga sangat besar begitu juga dengan True Negative dan False Negative. Oleh karena itu model K-NN adalah model yang robust tetapi tidak dapat diinterpretasikan.

Kesimpulan

Kita sudah membuat model Logistic Regression dan K-NN. Model Logistic Regression dengan performa yang paling baik yang telah kita buat adalah model yang menggunakan semua variabelnya untuk dijadikan sebagai prediktor. Tetapi kita perlu menurunkan threshold untuk meminimalisir prediksi yang salah terhadap pasien yang kemungkinan besar divonis penyakit kardiovaskular. Sedangkan performa model K-NN lebih robust dalam melakukan prediksi dibandingkan dengan model Logistic Regression. Tetapi model K-NN merupakan model yang tidak dapat diinterpretasi.