K-Nearest Neighbor (KNN) termasuk pada metode supervised machine learning yang dapat digunakan untuk masalah klasifikasi maupun regresi. Konsep dasar KNN adalah mencari k-nearest neighbors atau k tetangga terdekat dari data amatan berdasarkan jarak euclidean ataupun ukuran jarak lainnya. KNN umumnya digunakan untuk pemodelan klasifikasi namun dapat digunakan juga pada pemodelan regresi.
Dalam model klasifikasi, algoritma KNN menghitung jarak antara data amatan yang akan diklasifikasikan dengan dataset yang sudah memiliki label kelas, dan memilih k tetangga terdekat dengan data amatan tersebut berdasarkan ukuran jarak. Setelah itu, dari k tetangga tersebut, dilihat kelas apa yang paling banyak (mayoritas) menjadi tetangga terdekatnya. Hasil akhir yaitu mengklasifikasikan amatan tersebut masuk ke dalam kelas mayoritas yang diperoleh.
Langkah-Langkah KNN
Menentukan Parameter K:
K dalam KNN menunjukkan jumlah tetangga terdekat yang akan digunakan untuk membuat prediksi. Algoritma ini memeriksa label k tetangga terdekat suatu titik data dan menentukan label mayoritas sebagai prediksi.
Pemilihan nilai k adalah bagian utama dari KNN. Nilai k yang terlalu kecil dapat menyebabkan model rentan terhadap noise, sedangkan nilai k yang besar dapat mengaburkan batas keputusan yang sebenarnya.
Menghitung Jarak:
Menentukan Tetangga Terdekat:
Menentukan Kelas Prediksi:
heart<-read.csv("heart.csv")
str(heart)
## 'data.frame': 303 obs. of 14 variables:
## $ age : int 63 37 41 56 57 57 56 44 52 57 ...
## $ sex : int 1 1 0 1 0 1 0 1 1 1 ...
## $ cp : int 3 2 1 1 0 0 1 1 2 2 ...
## $ trestbps: int 145 130 130 120 120 140 140 120 172 150 ...
## $ chol : int 233 250 204 236 354 192 294 263 199 168 ...
## $ fbs : int 1 0 0 0 0 0 0 0 1 0 ...
## $ restecg : int 0 1 0 1 1 1 0 1 1 1 ...
## $ thalachh: int 150 187 172 178 163 148 153 173 162 174 ...
## $ exang : int 0 0 0 0 1 0 0 0 0 0 ...
## $ oldpeak : num 2.3 3.5 1.4 0.8 0.6 0.4 1.3 0 0.5 1.6 ...
## $ slope : int 0 0 2 2 2 1 1 2 2 2 ...
## $ ca : int 0 0 0 0 0 0 0 0 0 0 ...
## $ thall : int 1 2 2 2 2 1 2 3 3 2 ...
## $ target : int 1 1 1 1 1 1 1 1 1 1 ...
library(dplyr)
heart <- heart %>%
mutate_if(is.integer, as.numeric) %>%
mutate(sex = factor(sex, levels = c(0,1), labels = c("Female", "Male")),
fbs =factor(fbs, levels = c(0,1), labels = c("False", "True")),
exang = factor(exang, levels = c(0,1), labels = c("No", "Yes")),
target = factor(target, levels = c(0,1),
labels = c("Health", "Not Health")))
glimpse(heart)
## Rows: 303
## Columns: 14
## $ age <dbl> 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 <dbl> 3, 2, 1, 1, 0, 0, 1, 1, 2, 2, 0, 2, 1, 3, 3, 2, 2, 3, 0, 3, 0…
## $ trestbps <dbl> 145, 130, 130, 120, 120, 140, 140, 120, 172, 150, 140, 130, 1…
## $ chol <dbl> 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 <dbl> 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1…
## $ thalachh <dbl> 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 <dbl> 0, 0, 2, 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 1, 2, 0, 2, 2, 1…
## $ ca <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0…
## $ thall <dbl> 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…
Tipe data dari variabel respon sebaiknya berupa factor agar memudahkan pengukuran pada proses-proses selanjutnya.
Kode berikut menampilkan jumlah data dan distribusinya berdasarkan variabel target. Dari total 303 observasi, terdapat 138 dalam kelas “Health” dan 165 dalam kelas “Not Health”. Proporsi kedua kelas ini cukup seimbang, yaitu sekitar 45,55% untuk kelas “Health” dan 54,45% untuk kelas “Not Health”.
Melakukan pengecekan terhadap missing value
colSums(is.na(heart))
## age sex cp trestbps chol fbs restecg thalachh
## 0 0 0 0 0 0 0 0
## exang oldpeak slope ca thall target
## 0 0 0 0 0 0
# Pre-Processing Data
prop.table(table(heart$target))
##
## Health Not Health
## 0.4554455 0.5445545
table(heart$target)
##
## Health Not Health
## 138 165
library(caret)
set.seed(100) # mengatur seed tertentu untuk hasil yang dapat direproduksi
# data latih = 70%, data uji = 30%
trainIndex <- createDataPartition(heart$target, p = 0.70, list = FALSE, times = 1)
# Buat data latih dan data uji berdasarkan indeks yang dihasilkan
data.train <- heart[trainIndex, ]
data.test <- heart[-trainIndex, ]
# melihat komposisi setiap kelas pada data train dan test
cbind("train" = table(data.train$target), "test" = table(data.test$target))
## train test
## Health 97 41
## Not Health 116 49
KNN adalah model berbasis jarak, sehingga ukuran jarak menjadi faktor penting. Perbedaan skala antar fitur dapat memengaruhi kinerja model. Untuk mengatasi hal ini, disarankan untuk selalu melakukan scaling pada fitur, terutama ketika terdapat perbedaan skala yang signifikan. Salah satu metode scaling yang dapat digunakan adalah normalisasi (MinMax Scaling), yang mengubah setiap fitur menjadi rentang nilai antara 0 dan 1.
Menerapkan scaling untuk variabel tipe numerik, yaitu pada variabel 1, 3, 4, 5, 7, 8, 10, 11, 12, dan 13.
preproc.params <- preProcess(data.train[, c(1,3,4,5,7,8,10,11,12,13)], method = "range")
scaled.data.train <- predict(preproc.params, data.train[, c(1,3,4,5,7,8,10,11,12,13)])
scaled.data.test <- predict(preproc.params, data.test[, c(1,3,4,5,7,8,10,11,12,13)])
summary(scaled.data.train)
## age cp trestbps chol
## Min. :0.0000 Min. :0.0000 Min. :0.0000 Min. :0.0000
## 1st Qu.:0.3250 1st Qu.:0.0000 1st Qu.:0.2453 1st Qu.:0.3025
## Median :0.5000 Median :0.3333 Median :0.3396 Median :0.4093
## Mean :0.4954 Mean :0.3114 Mean :0.3480 Mean :0.4254
## 3rd Qu.:0.6500 3rd Qu.:0.6667 3rd Qu.:0.4340 3rd Qu.:0.5160
## Max. :1.0000 Max. :1.0000 Max. :1.0000 Max. :1.0000
## restecg thalachh oldpeak slope
## Min. :0.000 Min. :0.0000 Min. :0.0000 Min. :0.0000
## 1st Qu.:0.000 1st Qu.:0.4667 1st Qu.:0.0000 1st Qu.:0.5000
## Median :0.500 Median :0.6095 Median :0.1071 Median :0.5000
## Mean :0.277 Mean :0.5764 Mean :0.1885 Mean :0.7113
## 3rd Qu.:0.500 3rd Qu.:0.7238 3rd Qu.:0.3214 3rd Qu.:1.0000
## Max. :1.000 Max. :1.0000 Max. :1.0000 Max. :1.0000
## ca thall
## Min. :0.0000 Min. :0.0000
## 1st Qu.:0.0000 1st Qu.:0.6667
## Median :0.0000 Median :0.6667
## Mean :0.1702 Mean :0.7793
## 3rd Qu.:0.2500 3rd Qu.:1.0000
## Max. :1.0000 Max. :1.0000
Sebagai intuisi, dicoba dilakukan menggunakan k=5
library(class)
pred.knn <- knn(train=scaled.data.train, test=scaled.data.test,
cl=data.train$target, k=5)
Menampilkan hasil prediksi
print(pred.knn)
## [1] Health Not Health Health Not Health Not Health Health
## [7] Not Health Not Health Not Health Not Health Not Health Not Health
## [13] Not Health Not Health Not Health Not Health Not Health Not Health
## [19] Not Health Not Health Not Health Not Health Not Health Not Health
## [25] Not Health Not Health Not Health Not Health Not Health Not Health
## [31] Not Health Not Health Not Health Not Health Not Health Not Health
## [37] Not Health Health Not Health Health Health Not Health
## [43] Health Not Health Health Health Not Health Not Health
## [49] Not Health Health Not Health Health Not Health Health
## [55] Not Health Not Health Health Health Health Not Health
## [61] Health Health Health Not Health Not Health Health
## [67] Not Health Health Health Not Health Health Health
## [73] Health Health Health Not Health Health Not Health
## [79] Not Health Health Health Health Not Health Health
## [85] Health Not Health Health Health Health Not Health
## Levels: Health Not Health
Evaluasi Model
Evaluasi hasil prediksi pada data uji
conf_matrix <- confusionMatrix(pred.knn, data.test$target)
print(conf_matrix)
## Confusion Matrix and Statistics
##
## Reference
## Prediction Health Not Health
## Health 26 9
## Not Health 15 40
##
## Accuracy : 0.7333
## 95% CI : (0.6297, 0.8211)
## No Information Rate : 0.5444
## P-Value [Acc > NIR] : 0.0001788
##
## Kappa : 0.4559
##
## Mcnemar's Test P-Value : 0.3074342
##
## Sensitivity : 0.6341
## Specificity : 0.8163
## Pos Pred Value : 0.7429
## Neg Pred Value : 0.7273
## Prevalence : 0.4556
## Detection Rate : 0.2889
## Detection Prevalence : 0.3889
## Balanced Accuracy : 0.7252
##
## 'Positive' Class : Health
##
Akurasi prediksi yang diperoleh adalah 0,7333, atau algoritma KNN berhasil memprediksi dengan benar 73,33% dari data uji. Berdasarkan setiap kelas, dari 138 data uji berkelas “Health,” 26 diprediksi dengan benar dan 15 salah, menghasilkan nilai sensitivitas (prediksi benar untuk kelas positif) sebesar 0,6341. Sementara itu, dari 165 data uji berkelas “Not Health,” 9 diprediksi dengan benar dan 40 salah (spesifisitas sebesar 0,8163).
Library caret
dapat menghadirkan fungsi train yang dapat
dimanfaatkan untuk melakukan iterasi berbagai nilai k. Selain itu,
dengan fungsi train dapat dilakukan validasi silang sehingga hasil yang
diperoleh dapat dipercaya dan dapat mengurangi overfitting.
set.seed(100) # mengatur seed tertentu untuk hasil yang dapat direproduksi
# proses pengukuran menggunakan k-fold cv dengan 5 fold
control = trainControl(method = "cv", number = 5)
# pencarian dilakukan untuk k=1,2,...30
grid = expand.grid(k = 1:30)
knn.grid <- train(x=scaled.data.train,
y=data.train$target,
method = "knn",
trControl = control,
tuneGrid = grid)
# Menampilkan hasil model
print(knn.grid)
## k-Nearest Neighbors
##
## 213 samples
## 10 predictor
## 2 classes: 'Health', 'Not Health'
##
## No pre-processing
## Resampling: Cross-Validated (5 fold)
## Summary of sample sizes: 170, 171, 170, 170, 171
## Resampling results across tuning parameters:
##
## k Accuracy Kappa
## 1 0.7562569 0.5080950
## 2 0.7560354 0.5073106
## 3 0.8221484 0.6402496
## 4 0.8217054 0.6383932
## 5 0.8263566 0.6469285
## 6 0.8263566 0.6468774
## 7 0.8404208 0.6751247
## 8 0.8217054 0.6366528
## 9 0.8170543 0.6258825
## 10 0.8124031 0.6166919
## 11 0.8263566 0.6448862
## 12 0.8077519 0.6055443
## 13 0.8170543 0.6245499
## 14 0.8076412 0.6059186
## 15 0.7889258 0.5648034
## 16 0.7888151 0.5652375
## 17 0.7984496 0.5840741
## 18 0.7795127 0.5454707
## 19 0.7889258 0.5653755
## 20 0.7841639 0.5552673
## 21 0.7888151 0.5650840
## 22 0.7982281 0.5845022
## 23 0.7888151 0.5652290
## 24 0.7887043 0.5650403
## 25 0.7887043 0.5643130
## 26 0.7887043 0.5643168
## 27 0.7792913 0.5458184
## 28 0.7794020 0.5453074
## 29 0.7794020 0.5460348
## 30 0.7792913 0.5459310
##
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was k = 7.
Hasil di atas menunjukkan bahwa nilai k yang menghasilkan akurasi tertinggi dari validasi silang adalah k=7, dengan akurasi sebesar 0,8404208. Namun, perlu dicatat bahwa nilai ini berasal dari data latih. Oleh karena itu, performa model yang sesungguhnya perlu dievaluasi menggunakan data uji. Evaluasi pada data uji menunjukkan akurasi sebesar 0,7556, dengan nilai sensitivitas dan spesifisitas masing-masing sebesar 0,6341 dan 0,8571.
Line plot untuk melihat perubahan akurasi berdasarkan nilai K yang diuji.
ggplot(knn.grid$results, aes(x=k, y=Accuracy)) +
geom_line(color="darkblue") +
geom_point(color="red") +
labs(title="Performa Akurasi berdasarkan Nilai K", x="K", y="Accuracy")
Evaluasi Model
Membuat prediksi untuk data uji
predictions <- predict(knn.grid, newdata = scaled.data.test)
conf_matrix <- confusionMatrix(predictions, data.test$target)
print(conf_matrix)
## Confusion Matrix and Statistics
##
## Reference
## Prediction Health Not Health
## Health 26 7
## Not Health 15 42
##
## Accuracy : 0.7556
## 95% CI : (0.6536, 0.84)
## No Information Rate : 0.5444
## P-Value [Acc > NIR] : 2.879e-05
##
## Kappa : 0.4992
##
## Mcnemar's Test P-Value : 0.1356
##
## Sensitivity : 0.6341
## Specificity : 0.8571
## Pos Pred Value : 0.7879
## Neg Pred Value : 0.7368
## Prevalence : 0.4556
## Detection Rate : 0.2889
## Detection Prevalence : 0.3667
## Balanced Accuracy : 0.7456
##
## 'Positive' Class : Health
##
Kurva ROC KKN
ROC (Receiver Operating Characteristic) adalah kurva yang menunjukkan kinerja model klasifikasi pada berbagai ambang batas probabilitas. ROC curve dibentuk dengan memplot True Positive Rate (TPR) terhadap False Positive Rate (FPR) pada berbagai nilai ambang (threshold), untuk melihat bagaimana model melakukan klasifikasi pada tingkat probabilitas yang berbeda.
ROC curve menunjukkan kinerja model pada berbagai ambang threshold. Dapat dilakukan pemilihan threshold spesifik yang memberikan keseimbangan optimal antara TPR dan FPR.
# Menggunakan prediksi probabilitas untuk ROC Curve
library(pROC)
# Prediksi probabilitas kelas "Not Health" untuk data uji
pred_probs <- predict(knn.grid, newdata = scaled.data.test, type = "prob")
# Mengonversi data.test$target ke numerik untuk ROC (1 untuk Not Health, 0 untuk Health)
data.test$target_numeric <- ifelse(data.test$target == "Not Health", 1, 0)
# Membuat objek ROC curve
rocknn <- roc(data.test$target_numeric, pred_probs[, "Not Health"])
# Plot ROC curve
plot(rocknn, main = "Kurva ROC untuk KNN", col = "red")
ROC curve membantu melihat kekuatan model dalam memisahkan kelas, dan AUC memberikan metrik untuk menilai kinerja secara keseluruhan.
AUC menunjukkan kemampuan model dalam memisahkan kedua kelas pada berbagai threshold. Semakin besar AUC (mendekati 1), semakin baik kinerja model.
Nilai AUC sekitar 0.7–0.8 umumnya dianggap sebagai performa yang moderat, sedangkan 0.8–0.9 menunjukkan performa yang baik, dan nilai di atas 0.9 berarti performa sangat baik.
AUC rendah (mendekati 0.5) menunjukkan model yang lemah atau tidak mampu membedakan kelas dengan baik.
auc_value <- auc(rocknn)
print(auc_value)
## Area under the curve: 0.8591
ROC curve pada plot memiliki AUC sekitar 0.8591, ini berarti model KNN memiliki performa baik dalam membedakan antara pasien “Health” dan “Not Health”.
Perbandingan data aktual dan prediksi
results <- data.frame(data.test[,-14],
Actual = data.test$target,
Predicted = predictions
)
library(DT)
datatable(results,
options = list(pageLength = 10, # Menampilkan 10 baris per halaman
autoWidth = TRUE, # Menyesuaikan lebar kolom secara otomatis
scrollY = "400px", # Menetapkan tinggi scroll vertikal
scrollX = TRUE), # Mengaktifkan scroll horizontal
caption = 'Tabel: Nilai Aktual dan Prediksi')
https://sainsdata.id/machine-learning/8191/algoritma-k-nearest-neighbors-knn-dengan-r/
Direktorat Statistik Kesejahteraan Rakyat, BPS, saptahas@bps.go.id