Regresi logistik merupakan suatu algoritma dalam pengklasifikasian atau classifier yang dapat digunakan untuk memprediksi suatu kelas variabel. Dalam praktiknya, regresi logistik banyak digunakan dalam mengklasifikasikan 2 kelas dalam suatu variabel atau klasifikasi biner. Aplikasi regresi logistik menggunakan software R tidak jauh berbeda dengan regresi linear. Jika regresi linear menggunakan fungsi lm() untuk membangun model, maka regresi logistik menggunakan fungsi glm() untuk membangun modelnya
Data untuk contoh ini adalah data heart disease sebanyak 303 observasi dan 14 variabel. Dari data ini akan diprediksi apakah seseorang terkena penyakit jantung atau tidak (variabel num) berdasarkan 13 variabel penjelas. Berikut adalah deskripsi dari data heart disease:
Sex : Jenis kelamin 0 : female 1 : male
Cp : Chest Pain type 1 : typical angina 2 : atypical angina 3 : non-anginal pain 4 : asymptomatic
Trestbps : Resting blood pressure (mm Hg)
Chol : Serum cholesterol (mg/dl)
Fbs : Fasting blood sugar > 120 mg/dl 0: tidak 1: ya
restecg : resting electrocardiographic results 0: normal 1: having ST-T wave abnormality (T wave inversions and/or ST
elevation or depression of > 0.05 mV) 2: showing probable or definite left ventricular hypertrophy by Estes’ criteria
Thalach : maximum heart rate achieved
Exang : Exercise induced angina 0: tidak 1: ya
Oldpeak : ST depression induced by exercise relative to rest
Slope : Slope of peak exercise ST segment 1: upsloping 2: flat 3: downsloping
Ca : number of major vessels (0-3) colored by flourosopy
Thal : 3 = normal | 6 = fixed defect | 7 = reversable defec
Num : diagnosis of heart disease (angiographic disease status) : 0: < 50% diameter narrowing : 1: > 50% diameter narrowing
Variabel yang akan diprediksi adalah variabel num, dengan mengasumsikan bahwa nilai 0 berarti jantung baik-baik saja, dan nilai 1, 2, 3, 4 berarti jantung terkena penyakit. Sumber data berasal dari https://archive.ics.uci.edu/ml/datasets/heart+Disease .
library(tidyverse)
## -- Attaching packages --------------------------------------- tidyverse 1.2.1 --
## v ggplot2 3.1.1 v purrr 0.3.2
## v tibble 2.1.1 v dplyr 0.8.0.1
## v tidyr 0.8.3 v stringr 1.4.0
## v readr 1.3.1 v forcats 0.4.0
## -- Conflicts ------------------------------------------ tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
library(caret)
## Loading required package: lattice
##
## Attaching package: 'caret'
## The following object is masked from 'package:purrr':
##
## lift
library(class)
library(gtools)
library(gmodels)
library(class)
heartdisease <- read.csv(url
("https://archive.ics.uci.edu/ml/machine-learning-databases/heart-disease/processed.cleveland.data"),
header = FALSE)
colnames(heartdisease) <- c("age","sex","cp","trestbps", "chol", "fbs",
"restecg", "thalach", "exang", "oldpeak", "slope", "ca", "thal", "num")
head(heartdisease)
## age sex cp trestbps chol fbs restecg thalach exang oldpeak slope ca
## 1 63 1 1 145 233 1 2 150 0 2.3 3 0.0
## 2 67 1 4 160 286 0 2 108 1 1.5 2 3.0
## 3 67 1 4 120 229 0 2 129 1 2.6 2 2.0
## 4 37 1 3 130 250 0 0 187 0 3.5 3 0.0
## 5 41 0 2 130 204 0 2 172 0 1.4 1 0.0
## 6 56 1 2 120 236 0 0 178 0 0.8 1 0.0
## thal num
## 1 6.0 0
## 2 3.0 2
## 3 7.0 1
## 4 3.0 0
## 5 3.0 0
## 6 3.0 0
heartdisease$num<-ifelse(heartdisease$num==0,0,1)
str(heartdisease)
## 'data.frame': 303 obs. of 14 variables:
## $ age : num 63 67 67 37 41 56 62 57 63 53 ...
## $ sex : num 1 1 1 1 0 1 0 0 1 1 ...
## $ cp : num 1 4 4 3 2 2 4 4 4 4 ...
## $ trestbps: num 145 160 120 130 130 120 140 120 130 140 ...
## $ chol : num 233 286 229 250 204 236 268 354 254 203 ...
## $ fbs : num 1 0 0 0 0 0 0 0 0 1 ...
## $ restecg : num 2 2 2 0 2 0 2 0 2 2 ...
## $ thalach : num 150 108 129 187 172 178 160 163 147 155 ...
## $ exang : num 0 1 1 0 0 0 0 1 0 1 ...
## $ oldpeak : num 2.3 1.5 2.6 3.5 1.4 0.8 3.6 0.6 1.4 3.1 ...
## $ slope : num 3 2 2 3 1 1 3 1 2 3 ...
## $ ca : Factor w/ 5 levels "?","0.0","1.0",..: 2 5 4 2 2 2 4 2 3 2 ...
## $ thal : Factor w/ 4 levels "?","3.0","6.0",..: 3 2 4 2 2 2 2 2 4 4 ...
## $ num : num 0 1 1 0 0 0 1 0 1 1 ...
Variabel-variabel di atas semuanya berupa numerik, akan tetapi jika dilihat dari deskripsi data, ada beberapa variabel yang berupa kategorik yaitu variabel sex, cp, fbs, restecg, exang, slope, ca, thal, dan num sehingga variabel-variabel ini perlu diubah menjadi variabel yang berupa kategorik.
heartdisease$sex<-as.factor(heartdisease$sex)
heartdisease$cp<-as.factor(heartdisease$cp)
heartdisease$fbs<-as.factor(heartdisease$fbs)
heartdisease$restecg<-as.factor(heartdisease$restecg)
heartdisease$exang<-as.factor(heartdisease$exang)
heartdisease$slope<-as.factor(heartdisease$slope)
heartdisease$ca<-as.factor(heartdisease$ca)
heartdisease$thal<-as.factor(heartdisease$thal)
heartdisease$num<-as.factor(heartdisease$num)
str(heartdisease)
## 'data.frame': 303 obs. of 14 variables:
## $ age : num 63 67 67 37 41 56 62 57 63 53 ...
## $ sex : Factor w/ 2 levels "0","1": 2 2 2 2 1 2 1 1 2 2 ...
## $ cp : Factor w/ 4 levels "1","2","3","4": 1 4 4 3 2 2 4 4 4 4 ...
## $ trestbps: num 145 160 120 130 130 120 140 120 130 140 ...
## $ chol : num 233 286 229 250 204 236 268 354 254 203 ...
## $ fbs : Factor w/ 2 levels "0","1": 2 1 1 1 1 1 1 1 1 2 ...
## $ restecg : Factor w/ 3 levels "0","1","2": 3 3 3 1 3 1 3 1 3 3 ...
## $ thalach : num 150 108 129 187 172 178 160 163 147 155 ...
## $ exang : Factor w/ 2 levels "0","1": 1 2 2 1 1 1 1 2 1 2 ...
## $ oldpeak : num 2.3 1.5 2.6 3.5 1.4 0.8 3.6 0.6 1.4 3.1 ...
## $ slope : Factor w/ 3 levels "1","2","3": 3 2 2 3 1 1 3 1 2 3 ...
## $ ca : Factor w/ 5 levels "?","0.0","1.0",..: 2 5 4 2 2 2 4 2 3 2 ...
## $ thal : Factor w/ 4 levels "?","3.0","6.0",..: 3 2 4 2 2 2 2 2 4 4 ...
## $ num : Factor w/ 2 levels "0","1": 1 2 2 1 1 1 2 1 2 2 ...
Variabel-variabel di atas sudah memiliki jenis variabel yang sesuai dengan deskripsi data. Langkah selanjutnya adalah melihat jumlah kelas pada variabel yang akan diprediksi.
levels(heartdisease$num) <- c("No Disease","Disease")
levels(heartdisease$sex) = c("Female","Male")
table(heartdisease$num)
##
## No Disease Disease
## 164 139
prop.table(table(heartdisease$num))
##
## No Disease Disease
## 0.5412541 0.4587459
Jumlah kelas pada data menunjukkan proporsi yang cukup seimbang, sehingga dapat dikatakan tidak ada masalah imbalanced class pada data.
mytable <- table(heartdisease$num)
lbls <- paste(names(mytable), "\n", mytable, sep="")
pie(mytable, labels = lbls,
main="Pie Chart of Disease \n (with sample sizes)",
col=c("aquamarine","pink"))
counts <- table(heartdisease$num,heartdisease$sex)
barplot(counts, main="Sebaran Penyakit berdasarkan Jenis Kelamin",
xlab=" ", col=c("aquamarine","pink"),
legend=rownames(counts), beside=TRUE)
Pie chart dari variabel respon menunjukkan kelas cukup seimbang, sedangkan bar chart di atas menunjukkan sebaran penyakit berdasarkan jenis kelamin dimana pada kelompok female jumlah pasien yang terdiagnosa penyakit jantung lebih kecil dibanding yang normal, sedangkan pada kelompok male jumlah pasien yang terdiagnosa penyakit jantung lebih besar dibanding yang normal.
Stratifikasi data diperlukan untuk memastikan setiap kelas respon (target) terambil dalam pengacakan sample. Stratifikasi data dilakukan dengan fungsi filter() dalam paket dplyr.
library(dplyr)
hn <- filter(heartdisease,num=="No Disease")
hd <- filter(heartdisease,num=="Disease")
Data training digunakan untuk membangun model, sedangkan data testing digunakan untuk validasi dengan pembagian training:testing 70%:30%. Pengambilan data secara acak menggunakan nilai seed 121. Penggunaan seed dilakukan agar hasil pengacakan tetap. Setiap nilai seed akan melakukan pengacakan yang berbeda sehingga hasil dari setiap nilai seed juga akan berbeda (misal dalam akurasi).
set.seed (121)
acak.hn <- sample(1:nrow(hn), 0.7*nrow(hn))
acak.hd <- sample(1:nrow(hd), 0.7*nrow(hd))
heartdisease.train <- rbind(hn[acak.hn,],hd[acak.hd,])
heartdisease.test <- rbind(hn[-acak.hn,],hd[-acak.hd,])
Data training digunakan untuk membangun model regresi logistik dengan menggunakan fungsi glm() karena regresi logistik termasuk ke dalam generalized linear model dengan family = binomial.
model.logistik<-glm(num~.,data=heartdisease.train, family="binomial")
## Warning: glm.fit: fitted probabilities numerically 0 or 1 occurred
summary(model.logistik)
##
## Call:
## glm(formula = num ~ ., family = "binomial", data = heartdisease.train)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -2.15102 -0.36942 -0.07888 0.21040 2.97359
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -6.192085 5.882622 -1.053 0.29252
## age -0.039534 0.035755 -1.106 0.26886
## sexMale 1.702289 0.725228 2.347 0.01891 *
## cp2 0.801538 1.073830 0.746 0.45541
## cp3 0.005056 0.936113 0.005 0.99569
## cp4 2.642421 0.975885 2.708 0.00677 **
## trestbps 0.020537 0.015092 1.361 0.17359
## chol 0.007787 0.005353 1.455 0.14577
## fbs1 -0.240200 0.858485 -0.280 0.77963
## restecg1 16.185863 3827.223385 0.004 0.99663
## restecg2 -0.259544 0.537720 -0.483 0.62933
## thalach -0.006536 0.015438 -0.423 0.67202
## exang1 0.484235 0.596636 0.812 0.41702
## oldpeak 0.006995 0.315247 0.022 0.98230
## slope2 2.234603 0.683121 3.271 0.00107 **
## slope3 2.060036 1.313013 1.569 0.11666
## ca0.0 1.601283 1.938373 0.826 0.40875
## ca1.0 4.929167 2.057810 2.395 0.01660 *
## ca2.0 5.913015 2.343387 2.523 0.01163 *
## ca3.0 22.334154 1436.633995 0.016 0.98760
## thal3.0 -2.784289 4.033094 -0.690 0.48997
## thal6.0 -3.722967 4.136043 -0.900 0.36805
## thal7.0 -0.992611 4.043990 -0.245 0.80611
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 291.14 on 210 degrees of freedom
## Residual deviance: 111.73 on 188 degrees of freedom
## AIC: 157.73
##
## Number of Fisher Scoring iterations: 17
Dari output di atas, variabel sex, cp, trestbps, ca, dan thal signifikan mempengaruhi respon (num) pada taraf nyata 5%. Pemilihan variabel juga dapat dilakukan dengan hanya memasukkan variabel-variabel yang signifikan mempengaruhi respon dan kemudian membandingkan nilai akurasinya. Dalam analisa ini akan dilakukan dengan memasukkan semua variabel.
exp(1.702289)
## [1] 5.486492
Interpretasi untuk pengaruh setiap variabel dapat dilihat dari nilai rasio odds. Misalnya, untuk variabel sex memiliki nilai koefisien sebesar 1.702289 dengan reference category = female, nilai rasio odds nya adalah exp( 1.702289) = 5.48 yang artinya untuk pasien laki-laki, kemungkinan untuk terkena penyakit jantung adalah 5.48 kali lebih besar dari perempuan atau dapat dikatakan kecenderungan laki-laki untuk terkena penyakit jantung lebih besar dibanding perempuan.
Prediksi respon pada data testing dilakukan menggunakan threshold = 0.5 dimana jika nilai probabilitas prediksi > 0.5 maka akan diprediksi di kelas event (disease) dan sebaliknya akan diprediksi masuk di kelas non event (no disease).
heartdisease.train$peluang <- predict(model.logistik, heartdisease.train,
type = "response")
heartdisease.train$predik <- as.factor(ifelse(heartdisease.train$peluang > 0.5,
"Disease","No Disease"))
heartdisease.test$peluang <- predict(model.logistik, heartdisease.test,
type = "response")
heartdisease.test$predik <- as.factor(ifelse(heartdisease.test$peluang > 0.5,
"Disease","No Disease"))
library(caret)
confusionMatrix(as.factor(heartdisease.test$predik),
heartdisease.test$num)
## Warning in confusionMatrix.default(as.factor(heartdisease.test$predik), :
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Confusion Matrix and Statistics
##
## Reference
## Prediction No Disease Disease
## No Disease 38 6
## Disease 12 36
##
## Accuracy : 0.8043
## 95% CI : (0.7085, 0.8797)
## No Information Rate : 0.5435
## P-Value [Acc > NIR] : 0.0000001529
##
## Kappa : 0.6102
##
## Mcnemar's Test P-Value : 0.2386
##
## Sensitivity : 0.7600
## Specificity : 0.8571
## Pos Pred Value : 0.8636
## Neg Pred Value : 0.7500
## Prevalence : 0.5435
## Detection Rate : 0.4130
## Detection Prevalence : 0.4783
## Balanced Accuracy : 0.8086
##
## 'Positive' Class : No Disease
##
Dari Output di atas, untuk hasil confusion matrix prediksi yang meleset cukup kecil yaitu 6 untuk false positive dan 12 untuk false negative. Nilai akurasi sebesar 80.43% menunjukkan bahwa hasil klasifikasi dengan regresi logistik pada data ini sudah cukup baik.
Untuk menghindari/ meningkatkan nilai presisi bisa dilakukan dengan merubah nilai cut off.
prob.prediksi2 <- predict(model.logistik,
heartdisease.test, type="response")
prediksi2 <- ifelse(prob.prediksi2 >0.4,
"Disease","No Disease")
pred.aktual2 <- data.frame ("Prediksi" = prediksi2 ,
"Aktual" = heartdisease.test$num)
head(pred.aktual2)
## Prediksi Aktual
## 14 No Disease No Disease
## 17 No Disease No Disease
## 22 No Disease No Disease
## 23 No Disease No Disease
## 25 No Disease No Disease
## 26 No Disease No Disease
heartdisease.test$predik <- as.factor (heartdisease.test$predik)
confusionMatrix(as.factor(heartdisease.test$predik),
heartdisease.test$num)
## Warning in confusionMatrix.default(as.factor(heartdisease.test$predik), :
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Confusion Matrix and Statistics
##
## Reference
## Prediction No Disease Disease
## No Disease 38 6
## Disease 12 36
##
## Accuracy : 0.8043
## 95% CI : (0.7085, 0.8797)
## No Information Rate : 0.5435
## P-Value [Acc > NIR] : 0.0000001529
##
## Kappa : 0.6102
##
## Mcnemar's Test P-Value : 0.2386
##
## Sensitivity : 0.7600
## Specificity : 0.8571
## Pos Pred Value : 0.8636
## Neg Pred Value : 0.7500
## Prevalence : 0.5435
## Detection Rate : 0.4130
## Detection Prevalence : 0.4783
## Balanced Accuracy : 0.8086
##
## 'Positive' Class : No Disease
##
levels(heartdisease$num) <- c("Disease", "No Disease")
Pada prediksi2, nilai cutoff diturunkan menjadi 0.4 dan terlihat penurunan nilai akurasi menjadi 78% namun terjadi peningkatan nilai false positive menjadi 15. Yang artinya model menebak seseorang yang tidak terdiagnosa penyakit jantung menjadi terdiagnosa. Secara bussines wise, antara kedua model logistik diatas maka lebih baik menggunakan model ke dua dengan cut.off 0.4 karena lebih baik model menebak seseorang menjadi terdiagnosa jantung dibandingkan salah memprediksi yang terdiagnosa penyakit jantung namun dikatan tidak terdiagnosa.
performa <- function(cutoff, prob, ref, postarget, negtarget)
#cutoff = threshold 0.5
#prob = peluang
#ref = refrence (variable target dari data test)
{
predict <- as.factor(ifelse(prob >= cutoff, postarget, negtarget))
conf <- caret::confusionMatrix(predict , ref, positive = postarget)
acc <- conf$overall[1]
rec <- conf$byClass[1]
prec <- conf$byClass[3]
spec <- conf$byClass[2]
mat <- t(as.matrix(c(rec , acc , prec, spec)))
colnames(mat) <- c("recall", "accuracy", "precicion", "specificity")
#urutan evaluasi performa model
return(mat)
}
co <- seq(0.01,0.80,length=92)
result <- matrix(0,92,4)
for(i in 1:92){
result[i,] = performa(cutoff = co[i],
prob = heartdisease.test$peluang,
ref = heartdisease.test$num,
postarget = "Disease",
negtarget = "No Disease")
}
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
## Warning in confusionMatrix.default(predict, ref, positive = postarget):
## Levels are not in the same order for reference and data. Refactoring data
## to match.
data_frame("Recall" = result[,1], #mengikuti index kolom yang telah dibuat didalam func performa diatas jadi sesuai urutan yang kita buat. Recall berada pada kolom satu
"Accuracy" = result[,2],
"Precision" = result[,3],
"Specificity" = result[,4],
"Cutoff" = co) %>%
gather(key = "performa", value = "value", 1:4) %>% #mengubah kolom evaluasi menjadi baris. agar bisa diplot
ggplot(aes(x = Cutoff, y = value, col = performa)) +
geom_line(lwd = 1.5) +
scale_color_manual(values = c("darkred","darkgreen","orange", "blue")) +
scale_y_continuous(breaks = seq(0,1,0.1), limits = c(0,1)) +
scale_x_continuous(breaks = seq(0,1,0.1)) +
labs(title = "Tradeoff model perfomance") +
theme_minimal() +
theme(legend.position = "top",
panel.grid.minor.y = element_blank(),
panel.grid.minor.x = element_blank())
## Warning: `data_frame()` is deprecated, use `tibble()`.
## This warning is displayed once per session.
Sesuai dengan penjelasan sebelumnya, jika fokus kita pada nilai recall maka bisa menurunkan cutoff. Seperti pada visualisasi plot diatas semakin kecil nilai cutoff maka kecenderungan naiknya nilai recall semakin besar. Walupun sebenarnya pada visualisasi garis yang berpotongan nilai cutoff yang disarankan berkisar 0.6 - 0.63. Hasil plot membantu kita untuk menentukan nilai cutoff yang ideal. Untuk dataset ini yang kita dipakai 0.5, acuan utama dalam model 0.5. Jika melihat grafik, boleh saja kita naikkan nilai cutoff menjadi 0.63 namun nilai recall menjadi turun diangka 81% (yang artinya turun menjadi 8% dari nilai cutoff 0.4 yaitu 89%). Karena pada data ini yang kita utamakan adalah recall maka kita pertahankan nilainya atau jika bisa lebih memilih naikkan.
heart <- read.csv(url
("https://archive.ics.uci.edu/ml/machine-learning-databases/heart-disease/processed.cleveland.data"),
header = FALSE)
colnames(heart) <- c("age","sex","cp","trestbps", "chol", "fbs",
"restecg", "thalach", "exang", "oldpeak", "slope", "ca", "thal", "num")
head(heart)
## age sex cp trestbps chol fbs restecg thalach exang oldpeak slope ca
## 1 63 1 1 145 233 1 2 150 0 2.3 3 0.0
## 2 67 1 4 160 286 0 2 108 1 1.5 2 3.0
## 3 67 1 4 120 229 0 2 129 1 2.6 2 2.0
## 4 37 1 3 130 250 0 0 187 0 3.5 3 0.0
## 5 41 0 2 130 204 0 2 172 0 1.4 1 0.0
## 6 56 1 2 120 236 0 0 178 0 0.8 1 0.0
## thal num
## 1 6.0 0
## 2 3.0 2
## 3 7.0 1
## 4 3.0 0
## 5 3.0 0
## 6 3.0 0
Ada beberapa fitur kosong di kolom “thal” dan “cp”. Kedua variabel tersebut dibaca sebagai “?” selanjutnya kita akan menghapusnya sehingga algoritma K-NN dapat berjalan.
heart[heart == "?"] <- NA
heart <- na.omit(heart)
sum(is.na(heart))
## [1] 0
Pada variabel num yang merupakan variabel biner (target) yang kita inginkan untuk diprediksi, maka kita harus mengubahnya menjadi faktor. Variabel num yang dikodekan sebagai 0 adalah bebas penyakit jantung, dan kemudian 1, 2, 3, dan 4 merupakan tingkat keparahan penyakit jantung.
PAda analisis ini, untuk memprediksi apakah pasien memiliki penyakit atau tidak maka “variabel dummy” yang akan digunakan adalah: 0 = Tidak Ada Penyakit (No Disease) 1 = Penyakit (Disease)
Pertama-tama adalah merubah variabel integer “num” “2”, “3”, dan “4” menjadi “1”, jadi hanya variabel “0” dan “1” yang tersisa.
heart$num[heart$num=="4"] <- "1"
heart$num[heart$num=="3"] <- "1"
heart$num[heart$num=="2"] <- "1"
Selanjutnya rubah variabel “num” menjadi faktor.
heart$num <- as.factor(heart$num)
class(heart$num)
## [1] "factor"
head(heart$num)
## [1] 0 1 1 0 0 0
## Levels: 0 1
round(prop.table(table(heart$num)) * 100, digits = 1)
##
## 0 1
## 53.9 46.1
Seperti yang ditunjukkan di atas, kumpulan data mencakup 45,9% dengan penyakit jantung dan 54,1% tanpa penyakit jantung. Kemudian, perlu mengkonversi “thal” dan “ca” ke variabel angka, karena K-NN mengharuskan semua variabel selain variabel prediktor menjadi numerik.
heart$thal <- as.character(heart$thal)
heart$thal <- as.numeric(heart$thal)
str(heart)
## 'data.frame': 297 obs. of 14 variables:
## $ age : num 63 67 67 37 41 56 62 57 63 53 ...
## $ sex : num 1 1 1 1 0 1 0 0 1 1 ...
## $ cp : num 1 4 4 3 2 2 4 4 4 4 ...
## $ trestbps: num 145 160 120 130 130 120 140 120 130 140 ...
## $ chol : num 233 286 229 250 204 236 268 354 254 203 ...
## $ fbs : num 1 0 0 0 0 0 0 0 0 1 ...
## $ restecg : num 2 2 2 0 2 0 2 0 2 2 ...
## $ thalach : num 150 108 129 187 172 178 160 163 147 155 ...
## $ exang : num 0 1 1 0 0 0 0 1 0 1 ...
## $ oldpeak : num 2.3 1.5 2.6 3.5 1.4 0.8 3.6 0.6 1.4 3.1 ...
## $ slope : num 3 2 2 3 1 1 3 1 2 3 ...
## $ ca : Factor w/ 5 levels "?","0.0","1.0",..: 2 5 4 2 2 2 4 2 3 2 ...
## $ thal : num 6 3 7 3 3 3 3 3 7 7 ...
## $ num : Factor w/ 2 levels "0","1": 1 2 2 1 1 1 2 1 2 2 ...
## - attr(*, "na.action")= 'omit' Named int 88 167 193 267 288 303
## ..- attr(*, "names")= chr "88" "167" "193" "267" ...
heart$ca <- as.character(heart$ca)
heart$ca <- as.numeric(heart$ca)
str(heart)
## 'data.frame': 297 obs. of 14 variables:
## $ age : num 63 67 67 37 41 56 62 57 63 53 ...
## $ sex : num 1 1 1 1 0 1 0 0 1 1 ...
## $ cp : num 1 4 4 3 2 2 4 4 4 4 ...
## $ trestbps: num 145 160 120 130 130 120 140 120 130 140 ...
## $ chol : num 233 286 229 250 204 236 268 354 254 203 ...
## $ fbs : num 1 0 0 0 0 0 0 0 0 1 ...
## $ restecg : num 2 2 2 0 2 0 2 0 2 2 ...
## $ thalach : num 150 108 129 187 172 178 160 163 147 155 ...
## $ exang : num 0 1 1 0 0 0 0 1 0 1 ...
## $ oldpeak : num 2.3 1.5 2.6 3.5 1.4 0.8 3.6 0.6 1.4 3.1 ...
## $ slope : num 3 2 2 3 1 1 3 1 2 3 ...
## $ ca : num 0 3 2 0 0 0 2 0 1 0 ...
## $ thal : num 6 3 7 3 3 3 3 3 7 7 ...
## $ num : Factor w/ 2 levels "0","1": 1 2 2 1 1 1 2 1 2 2 ...
## - attr(*, "na.action")= 'omit' Named int 88 167 193 267 288 303
## ..- attr(*, "names")= chr "88" "167" "193" "267" ...
Kita sekarang perlu menormalkan data sehingga vektor KNN tidak dipengaruhi secara tidak tepat oleh pengukuran dan panjang yang berbeda. Saya pertama-tama akan membuat fungsi normalisasi pada menerapkannya pada dataset.
Mari kita buat fungsi normalisasinya
normalize <- function(x) {
return ((x - min(x)) / (max(x) - min(x)))
}
normalize(c(1,2,3,4,5))
## [1] 0.00 0.25 0.50 0.75 1.00
normalize(c(10, 20, 30, 40, 50))
## [1] 0.00 0.25 0.50 0.75 1.00
Seperti yang ditunjukkan di atas, - set kedua 10x lebih besar dari yang pertama, dan setelah dilakukan normalisasi keduanya menghasilkan nilai yang sama. Sekarang kita akan menormalkan semua fitur numerik dalam bingkai data.
heart[1:13] <- as.data.frame(lapply(heart[1:13], normalize))
summary(heart$chol)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.0000 0.1941 0.2671 0.2771 0.3425 1.0000
Sebagai contoh variabel chol susah memiliki rentang value yang sama yaitu 0 - 1. Selanjutnya lakukan split data.
set.seed (33)
data <- sample(2, nrow(heart), replace=TRUE, prob=c(0.7, 0.3))
trainknn <- heart[data==1,]
testknn <- heart[data==2,]
str(trainknn)
## 'data.frame': 209 obs. of 14 variables:
## $ age : num 0.708 0.792 0.792 0.562 0.688 ...
## $ sex : num 1 1 1 1 0 0 1 1 1 0 ...
## $ cp : num 0 1 1 0.333 1 ...
## $ trestbps: num 0.481 0.623 0.245 0.245 0.434 ...
## $ chol : num 0.244 0.365 0.235 0.251 0.324 ...
## $ fbs : num 1 0 0 0 0 0 0 1 0 0 ...
## $ restecg : num 1 1 1 0 1 0 1 1 0 1 ...
## $ thalach : num 0.603 0.282 0.443 0.817 0.679 ...
## $ exang : num 0 1 1 0 0 1 0 1 0 0 ...
## $ oldpeak : num 0.371 0.242 0.419 0.129 0.581 ...
## $ slope : num 1 0.5 0.5 0 1 0 0.5 1 0.5 0.5 ...
## $ ca : num 0 1 0.667 0 0.667 ...
## $ thal : num 0.75 0 1 0 0 0 1 1 0.75 0 ...
## $ num : Factor w/ 2 levels "0","1": 1 2 2 1 2 1 2 2 1 1 ...
## - attr(*, "na.action")= 'omit' Named int 88 167 193 267 288 303
## ..- attr(*, "names")= chr "88" "167" "193" "267" ...
Kami telah membagi 70% data train dan 30% data test.
pred <- knn(train = trainknn[1:13], test= testknn[1:13],
cl = trainknn$num, k = 1)
Sekarang kita telah memiliki output penyakit jantung yang diprediksi untuk set data uji. Untuk mengevaluasi model, kita akan membandingkan prediksi dengan pengamatan yang ada dalam data uji.
library("gmodels")
library(caret)
confusionMatrix(pred, testknn$num, positive = "1")
## Confusion Matrix and Statistics
##
## Reference
## Prediction 0 1
## 0 41 12
## 1 5 30
##
## Accuracy : 0.8068
## 95% CI : (0.7088, 0.8832)
## No Information Rate : 0.5227
## P-Value [Acc > NIR] : 0.00000002822
##
## Kappa : 0.61
##
## Mcnemar's Test P-Value : 0.1456
##
## Sensitivity : 0.7143
## Specificity : 0.8913
## Pos Pred Value : 0.8571
## Neg Pred Value : 0.7736
## Prevalence : 0.4773
## Detection Rate : 0.3409
## Detection Prevalence : 0.3977
## Balanced Accuracy : 0.8028
##
## 'Positive' Class : 1
##
Terlihat pada model prediksi pertama (pred) dari total 88 observasi model dapat menebak dengan benar sebanyak 73 observasi dimana ada 5 false negative dan 12 false positive.
Artinya ada 15 prediksi dimana pada data sesungguhnya ada 12 pasien dengan kondisi jantung buruk namun diprediksi oleh model menjadi tidak terdiagonasis penyakit jantung. Apakah model prediksi pertama dengan nilai k = 1 sudah bagus?? Mari lakukan pengujian terhadap nilai k=2 dibawah ini.
pred2 <- knn(train = trainknn[1:13], test= testknn[1:13],
cl = trainknn$num, k = 2)
confusionMatrix(pred2, testknn$num, positive = "1")
## Confusion Matrix and Statistics
##
## Reference
## Prediction 0 1
## 0 39 14
## 1 7 28
##
## Accuracy : 0.7614
## 95% CI : (0.6586, 0.8458)
## No Information Rate : 0.5227
## P-Value [Acc > NIR] : 0.000003544
##
## Kappa : 0.5182
##
## Mcnemar's Test P-Value : 0.1904
##
## Sensitivity : 0.6667
## Specificity : 0.8478
## Pos Pred Value : 0.8000
## Neg Pred Value : 0.7358
## Prevalence : 0.4773
## Detection Rate : 0.3182
## Detection Prevalence : 0.3977
## Balanced Accuracy : 0.7572
##
## 'Positive' Class : 1
##
Terlihat pada model prediksi pertama (pred2) dari total 88 observasi model dapat menebak dengan benar sebanyak 67 observasi daiman ada 5 false negative dan 16 false positive.
Artinya ada 16 prediksi dimana pada data sesungguhnya ada 16 pasien dengan kondisi jantung buruk namun diprediksi oleh model menjadi tidak terdiagonasis penyakit jantung (jantung sehat). Jika dilihat dari tingkat akurasinya, model kedua mengalami penurunan namun false positifnya meningkat. Dalam pemilihan suatu model dalam metode knn dan business wise pada kasus ini, yang ingin dilihat adalah meminimalisir false positif. Kita coba pengujian pada model ketiga.
pred3 <- knn(train = trainknn[1:13], test= testknn[1:13],
cl = trainknn$num, k = 5)
confusionMatrix(pred3, testknn$num, positive = "1")
## Confusion Matrix and Statistics
##
## Reference
## Prediction 0 1
## 0 42 10
## 1 4 32
##
## Accuracy : 0.8409
## 95% CI : (0.7475, 0.9102)
## No Information Rate : 0.5227
## P-Value [Acc > NIR] : 0.0000000003635
##
## Kappa : 0.6792
##
## Mcnemar's Test P-Value : 0.1814
##
## Sensitivity : 0.7619
## Specificity : 0.9130
## Pos Pred Value : 0.8889
## Neg Pred Value : 0.8077
## Prevalence : 0.4773
## Detection Rate : 0.3636
## Detection Prevalence : 0.4091
## Balanced Accuracy : 0.8375
##
## 'Positive' Class : 1
##
Pada pred3 dari total 88 observasi model dapat menebak dengan benar sebanyak 74 observasi daiman ada 4 false negative dan 10 false positive.
Pada model ketiga terlihat mengalami peningkatan akurasi prediksi dan peningkatan recall. Dimana berkurang false positif dan negatif, pada data actual orang dengan jantung sehat/baik-baik saja namun diprediksi sakit jantung sebanyak 4 orang dan untuk data actual orang dengan kondisi jantung buruk namun diprediksi dengan jantung sehat turun menjadi 10 orang. Yang artinya nilai K=5 meningkatkan nilai recall yang merupakan acuan utama dalam analisis dataset HeartDisease.
Jika kita urutkan nilai recall berdasarkan rumus nya maka tampilannya seprti dibawah ini :
#k=1
acc1 <- (42+30)/(42+12+5+30)
recall1 <- 30/(30+12)
spe1 <- 41/(41+12)
pre1 <- 30/(30+5)
#k=2
acc2 <- (41+26)/(41+16+5+26)
recall2 <- 26/(26+16)
spe2 <- 41/(41+16)
pre2 <- 26/(26+5)
#k=5
acc3 <- (42+32)/(42+10+4+32)
recall3 <- 32/(32+10)
spe3 <- 42/(42+10)
pre3 <- 32/(32+4)
round(acc1, 2); round(recall1, 2); round(spe1, 2); round(pre1, 2)
## [1] 0.81
## [1] 0.71
## [1] 0.77
## [1] 0.86
round(acc2, 2); round(recall2, 2); round(spe2, 2); round(pre2, 2)
## [1] 0.76
## [1] 0.62
## [1] 0.72
## [1] 0.84
round(acc3, 2); round(recall3, 2); round(spe3, 2); round(pre3, 2)
## [1] 0.84
## [1] 0.76
## [1] 0.81
## [1] 0.89
Terlihat jelas pada model prediksi ketiga dengan nilai k=5 memiliki tingkat akurasi dan recall tertinggi, dibanding dengan model knn lainnya. Karena pada kasus data set ini yang kita ingin tingkatkan adalah nilai recall maka otomatis model terbaik menggunakan nilai k=5 dengan tingkat accuracy 84% dan recall 76%.
Pada pembuatan model dengan pendekatan regresi logistik didapatkan bahwa model terbaik ada pada model dengan nilai cutoff 0.4 yang menghasilkan false positive 5 dan tingkat akurasi 78% dan recall 70%.
Sedangkan pada analisas model dengan pendekatan K-Nearest Neighbor khususnya pada model prediksi ketiga didapatkan bahwa tingkat akurasi model yang tertinggi dengan accuracy 84% dan recall 76% menggunakan niali k=5. Pada pendekatan metode KNN dan berdasarkan bussiness wise yang diharapkan maka dapat diasumsikan bahwa penurunan nilai false positif menjadi 10 observasi pada model knn prediksi ketiga adalah yang terbaik.
Dalam mencari formula model yang baik, sebenarnya ada banyak cara. Contohnya pada pembuatan model dengan metode regresi logistik, untuk mendapatkan tingkat spefisikasi model yang diinginkan sesuai business wise yang diharapkan bisa merubah tingkat hasil accuracy, recall, spesificity dll dengan melakukan uji coba Tuning Treshold nilai cutoff. Lain halnya dengan pembuatan model menggunakan pendekatan metode K-Nearest Neighbor, untuk mendapatkan model yang terbaik adalah dengan melakukan banyak percobaan pada nilai k.
Nilai k yang terbaik untuk algoritme ini tergantung pada data; secara umumnya, nilai k yang tinggi akan mengurangi efek noise pada klasifikasi, tetapi membuat batasan antara setiap klasifikasi menjadi lebih kabur. Nilai k yang bagus dapat dipilih dengan optimasi parameter, misalnya dengan menggunakan cross-validation. Kasus khusus di mana klasifikasi diprediksikan berdasarkan data pembelajaran yang paling dekat (dengan kata lain, k = 1). Pada intinya pemilihan metode dalam pembuatan model pembelajaran dan penentuan model terbaik tergantung dari sektor bisnis/data dan hasil yang ingin diharapkan.