Melakukan prediksi atau Classification pada pasien yang Menderita penyakit jantung atau tidak berdasarkan kategori dari beberapa variabel penunjangnya, menggunakan logistik regression dan k-nearest neighbor
Data Source : Heart Disease UCI
import Data to RStudio, dan pastikan data tersebut ditempatkan di folter yang sama dengan data project R.
#Library
library(dplyr)
##
## 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(gtools)
library(gmodels)
## Warning: package 'gmodels' was built under R version 4.1.1
library(ggplot2)
library(class)
library(tidyr)
<- read.csv("heart.csv")
heart 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~
Penjelasan Data :
ï..age
: Umur Pasian ( Tahun )sex
: Jenis Kelasim ( 1 = Pria , 2= Prempuan)cp
: Tipe Nyeri/sakit Dadatrestbps
: Tekanan Darah (mmHg)chol
: Kolestrol (mg/dL)fbs
: gula darah puasa> 120 mg /dl (1 = benar; 0 = salah)restecg
: mengembalikan hasil elektrokardiografithalach
: Maksimum Denyut Jantungexang
: exercise induced angina (1 = ya; 0 = tidak)oldpeak
: ST depresi yang disebabkan oleh olahraga relatif terhadap istirahatslope
: kemiringan segmen ST latihan puncakca
: jumlah pembuluh darah utama (0-3) diwarnai dengan fluoroskopithal
: 3 = normal; 6 = cacat tetap; 7 = cacat yang dapat dibaliktarget
: 1 = sakit atau 0 = tidak sakitmenyesuaikan Type data (sex, cp, fbs, restecg, exang, slope, ca, thal, and target) sebelumnya Integer Menjadi Factor, kemudian melakukan Penyesuaian deskripsi data
<- heart %>%
heart_clean mutate_if(is.integer, as.factor) %>% #merubah type data
mutate(sex = factor(sex, levels = c(0,1), labels = c("Female", "Male")), #penyesuaian deskripsi data
fbs =factor(fbs, levels = c(0,1), labels = c("False", "True")),
exang = factor(exang, levels = c(0,1), labels = c("No", "Yes")),
target = factor(target, levels = c(0,1),
labels = c("Health", "Not Health")))
#cek Type data apakah data sudah sesuai?
glimpse(heart_clean)
## Rows: 303
## Columns: 14
## $ ï..age <fct> 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 <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> 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> Not Health, Not Health, Not Health, Not Health, Not Health, N~
Selanjutnya yaitu melakukan pengecekan terhadap missing value. Missing value perlu kita cek terlebih dahulu agar tidak mengganggu dalam melakukan pemodelan nantinya.
colSums(is.na(heart_clean))
## ï..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
Melakukan Cek proporsi dari target variabel Target
prop.table(table(heart_clean$target))
##
## Health Not Health
## 0.4554455 0.5445545
summary(heart_clean)
## ï..age sex cp trestbps chol fbs
## 58 : 19 Female: 96 0:143 120 : 37 197 : 6 False:258
## 57 : 17 Male :207 1: 50 130 : 36 204 : 6 True : 45
## 54 : 16 2: 87 140 : 32 234 : 6
## 59 : 14 3: 23 110 : 19 212 : 5
## 52 : 13 150 : 17 254 : 5
## 51 : 12 138 : 13 269 : 5
## (Other):212 (Other):149 (Other):270
## restecg thalach exang oldpeak slope ca thal
## 0:147 162 : 11 No :204 Min. :0.00 0: 21 0:175 0: 2
## 1:152 160 : 9 Yes: 99 1st Qu.:0.00 1:140 1: 65 1: 18
## 2: 4 163 : 9 Median :0.80 2:142 2: 38 2:166
## 152 : 8 Mean :1.04 3: 20 3:117
## 173 : 8 3rd Qu.:1.60 4: 5
## 125 : 7 Max. :6.20
## (Other):251
## target
## Health :138
## Not Health:165
##
##
##
##
##
Splitting Train-test
Membagi data heart
menjadi data train dan test, dengan pembagian 80% train dan 20% test
RNGkind(sample.kind = "Rounding")
## Warning in RNGkind(sample.kind = "Rounding"): non-uniform 'Rounding' sampler
## used
set.seed(100)
<- sample(nrow(heart_clean),
index nrow(heart_clean) *0.8) #80%
<- heart_clean[index, ]
heart_train <- heart_clean[-index, ] heart_test
membuat permodelan menggunakan regresi logistik, dengan variable target menjadi variable responsnya, karena variabel tersebut mempengaruhi target variabel
<- glm(formula = target ~ sex+cp+fbs+exang+oldpeak+slope+ca+thal,
modelheart data = heart_train,
family = "binomial")
disini saya menggunakan metode stepwise karena masih banyak variabel prediktor yang tidak signifikan terhadap target variabel
<- step(modelheart,
model_back direction = "backward")
## Start: AIC=175.96
## target ~ sex + cp + fbs + exang + oldpeak + slope + ca + thal
##
## Df Deviance AIC
## - fbs 1 142.65 174.65
## - slope 2 145.29 175.29
## <none> 141.96 175.96
## - thal 3 150.02 178.02
## - exang 1 146.66 178.66
## - oldpeak 1 151.18 183.18
## - sex 1 151.98 183.98
## - ca 4 166.60 192.60
## - cp 3 168.97 196.97
##
## Step: AIC=174.65
## target ~ sex + cp + exang + oldpeak + slope + ca + thal
##
## Df Deviance AIC
## - slope 2 145.66 173.66
## <none> 142.65 174.65
## - thal 3 150.65 176.65
## - exang 1 147.37 177.37
## - oldpeak 1 152.11 182.11
## - sex 1 152.30 182.30
## - ca 4 166.64 190.64
## - cp 3 172.95 198.95
##
## Step: AIC=173.66
## target ~ sex + cp + exang + oldpeak + ca + thal
##
## Df Deviance AIC
## <none> 145.66 173.66
## - thal 3 154.60 176.60
## - exang 1 151.52 177.52
## - sex 1 153.42 179.42
## - ca 4 166.98 186.98
## - oldpeak 1 166.33 192.33
## - cp 3 176.03 198.03
summary(model_back)
##
## Call:
## glm(formula = target ~ sex + cp + exang + oldpeak + ca + thal,
## family = "binomial", data = heart_train)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -2.6997 -0.2954 0.1324 0.4566 2.5844
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 0.6854 3.5054 0.196 0.844993
## sexMale -1.4212 0.5307 -2.678 0.007409 **
## cp1 1.7022 0.6150 2.768 0.005643 **
## cp2 2.4105 0.5680 4.244 2.20e-05 ***
## cp3 3.4547 0.9737 3.548 0.000388 ***
## exangYes -1.1908 0.4951 -2.405 0.016170 *
## oldpeak -1.1692 0.2904 -4.026 5.67e-05 ***
## ca1 -1.9377 0.5198 -3.728 0.000193 ***
## ca2 -2.1905 0.8871 -2.469 0.013533 *
## ca3 -1.5541 0.9675 -1.606 0.108212
## ca4 14.1969 1281.4345 0.011 0.991160
## thal1 2.9101 3.6180 0.804 0.421194
## thal2 1.9429 3.5012 0.555 0.578935
## thal3 0.7945 3.5062 0.227 0.820730
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 332.24 on 241 degrees of freedom
## Residual deviance: 145.66 on 228 degrees of freedom
## AIC: 173.66
##
## Number of Fisher Scoring iterations: 15
menggunakan model_back yaitu hasil dari stepwise, yang akan dicoba coba prediksi menggunakan data test yang sudah kita miliki.
$prob_heart<-predict(object = model_back,
heart_testtype = "response",
newdata = heart_test)
Classification result predict with ifelse function
$pred_label <- factor(ifelse(heart_test$prob_heart > 0.5, "Not Health","Health"))
heart_testc("pred_label", "target")] heart_test[
melihats peluang prediksi data
ggplot(heart_test, aes(x=prob_heart)) +
geom_density() +
theme_minimal()
value =
library(caret)
## Loading required package: lattice
<- confusionMatrix(heart_test$pred_label,
model_back_eval $target,
heart_testpositive = "Not Health")
model_back_eval
## Confusion Matrix and Statistics
##
## Reference
## Prediction Health Not Health
## Health 23 6
## Not Health 8 24
##
## Accuracy : 0.7705
## 95% CI : (0.645, 0.8685)
## No Information Rate : 0.5082
## P-Value [Acc > NIR] : 2.346e-05
##
## Kappa : 0.5414
##
## Mcnemar's Test P-Value : 0.7893
##
## Sensitivity : 0.8000
## Specificity : 0.7419
## Pos Pred Value : 0.7500
## Neg Pred Value : 0.7931
## Prevalence : 0.4918
## Detection Rate : 0.3934
## Detection Prevalence : 0.5246
## Balanced Accuracy : 0.7710
##
## 'Positive' Class : Not Health
##
Summary Evaluation Logistic Regression =
-Accuracy : 0.7705 –> 77% seberapa mampu model menebak dengan benar target (Health & Not Health).
-Sensitivity (Recall) : 0.8000 –> 80% dari semua data aktual yang positif, seberapa mampu proporsi model saya menebak benar.
-Specificity : 0.7419 –> 74,1% dari semua data aktual yang negatif, seberapa mampu proporsi model saya menebak yang benar.
-Pos Pred (Precision) : 0.7500 –> 75% dari semua hasil prediksi, seberapa mampu model saya dapat menebak benar kelas positif.
Membuat variabel dummy dari data-data kategori yang akan digunakan dalam klasifikasi.
<- dummyVars(" ~target+sex+cp+fbs+exang+oldpeak+slope+ca+thal",
knn data = heart_clean)
<- data.frame(predict(knn,
knn newdata = heart_clean)
)str(knn)
## 'data.frame': 303 obs. of 25 variables:
## $ target.Health : num 0 0 0 0 0 0 0 0 0 0 ...
## $ target.Not.Health: 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.No : num 1 1 1 1 0 1 1 1 1 1 ...
## $ exang.Yes : 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 ...
$target.Health <- NULL
knn$sex.Female <- NULL
knn$fbs.False <- NULL
knn$exang.No <- NULL
knnnames(knn)
## [1] "target.Not.Health" "sex.Male" "cp.0"
## [4] "cp.1" "cp.2" "cp.3"
## [7] "fbs.True" "exang.Yes" "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"
names(heart_clean)
## [1] "ï..age" "sex" "cp" "trestbps" "chol" "fbs"
## [7] "restecg" "thalach" "exang" "oldpeak" "slope" "ca"
## [13] "thal" "target"
Membentuk data training dan data testing dari data knn yang telah terbentuk.
set.seed(110)
<- knn[index,2:21]
knn_train <- knn[-index,2:21]
knn_test
<- knn[index,1]
knn_train_label <- knn[-index,1] knn_test_label
round(sqrt(nrow(knn_train)))
## [1] 16
Melakukan prediksi dengan K-NN menggunakan k = 17 karena hasilnya 16 tdk bagus digunakan (genap)
<- class::knn(train = knn_train,
pred_knn test = knn_test,
cl = knn_train_label,
k = 17)
<- confusionMatrix(data = as.factor(pred_knn),
pred_knn_conf reference = as.factor(knn_test_label),
positive = "1")
pred_knn_conf
## Confusion Matrix and Statistics
##
## Reference
## Prediction 0 1
## 0 26 5
## 1 5 25
##
## Accuracy : 0.8361
## 95% CI : (0.7191, 0.9185)
## No Information Rate : 0.5082
## P-Value [Acc > NIR] : 9.418e-08
##
## Kappa : 0.672
##
## Mcnemar's Test P-Value : 1
##
## Sensitivity : 0.8333
## Specificity : 0.8387
## Pos Pred Value : 0.8333
## Neg Pred Value : 0.8387
## Prevalence : 0.4918
## Detection Rate : 0.4098
## Detection Prevalence : 0.4918
## Balanced Accuracy : 0.8360
##
## 'Positive' Class : 1
##
Summary Evaluation Logistic Regression =
-Accuracy : 0.8361 –> 83% seberapa mampu model menebak dengan benar target (Health & Not Health).
-Sensitivity (Recall) : 0.8333 –> 83% dari semua data aktual yang positif, seberapa mampu proporsi model saya menebak benar.
-Specificity : 0.8387 –> 83% dari semua data aktual yang negatif, seberapa mampu proporsi model saya menebak yang benar.
-Pos Pred (Precision) : 0.8333 –> 83% dari semua hasil prediksi, seberapa mampu model saya dapat menebak benar kelas positif.
<- c("Logistic Regression", "K-Nearest Neighbor")
Model <- c(0.7705,0.8361)
Accuracy <- c(0.8000,0.8333)
Recall <- c(0.7419,0.8387)
Specificity <- c(0.7500 ,0.8333)
Precision <- data.frame(Model,Accuracy,Recall,Specificity,Precision)
tabelmodel tabelmodel
Hasil dari Kedua model cukup baik dalam hal Accuracy, Recall, Specificity dan Precision.
Tetapi pada kasus ini, saya memilih pasien Not Health
atau penderita penyakit Jantung, maka dari itu saya memilih model dengan tingkat (Precision) dan (Accuracy) tinggi, karena lebih baik model tersebut dapat memprediksi secara akurat dan tidak salah dalam memprediksi pasien Jantung / FP
( Prediksi benar, tetapi Aktual salah) [Memprediksi orang sakit, sebenarnya tidak sakit], lebih baik dari pada FN
( Prediksi salah tapi Aktual benar) [Memprediksi Tidak sakit, Tetapi sebenarnya Sakit]
Sehingga saya memilih KNN karena memiliki Tinggkat Accuracy dan Precision lebih tinggi dibandingkan Logistic Regression