Prediksi Orang Yang Overweight/Obese
Pada kesempatan kali ini, saya akan mencoba melakukan prediksi mengenai seseorang yang Overweight dengan berdasarkan kategori dari beberapa variabel penunjangnya. Algoritma yang akan saya gunakan yaitu menggunakan logistik regression dan k-nearest neighbor yang termasuk dalam supervised learning.
Sebelum itu kita harus melakukan install.package() pada package dplyr, gtools, gmodels, ggplot2, class, tidyr, caret pada R Studio. Apabila telah ter-install, maka lakukan pengaktifan package menggunakan library().
##
## 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
## Warning: package 'gtools' was built under R version 4.3.1
## Warning: package 'gmodels' was built under R version 4.3.1
## Warning: package 'tidyr' was built under R version 4.3.1
## Warning: package 'caret' was built under R version 4.3.1
## Loading required package: lattice
Dataset yang akan saya gunakan yaitu data mengenai seseorang yang diprediksi Overweight/Obese. berdasarkan beberapa karakteristik yang menyertai serta dapat anda unduh langsung pada Kaggle.
## 'data.frame': 374 obs. of 13 variables:
## $ Person.ID : int 1 2 3 4 5 6 7 8 9 10 ...
## $ Gender : chr "Male" "Male" "Male" "Male" ...
## $ Age : int 27 28 28 28 28 28 29 29 29 29 ...
## $ Occupation : chr "Software Engineer" "Doctor" "Doctor" "Sales Representative" ...
## $ Sleep.Duration : num 6.1 6.2 6.2 5.9 5.9 5.9 6.3 7.8 7.8 7.8 ...
## $ Quality.of.Sleep : int 6 6 6 4 4 4 6 7 7 7 ...
## $ Physical.Activity.Level: int 42 60 60 30 30 30 40 75 75 75 ...
## $ Stress.Level : int 6 8 8 8 8 8 7 6 6 6 ...
## $ BMI.Category : chr "Overweight" "Normal" "Normal" "Obese" ...
## $ Blood.Pressure : chr "126/83" "125/80" "125/80" "140/90" ...
## $ Heart.Rate : int 77 75 75 85 85 85 82 70 70 70 ...
## $ Daily.Steps : int 4200 10000 10000 3000 3000 3000 3500 8000 8000 8000 ...
## $ Sleep.Disorder : chr "None" "None" "None" "Sleep Apnea" ...
Informasi penting dalam data :
Person.ID : Pengidentifikasi untuk setiap individu.
Gender : Jenis kelamin orang (Pria / Wanita).
Age : Usia seseorang dalam tahun.
Occupation : Pekerjaan atau profesi orang tersebut.
Sleep.Duration : Jumlah jam orang tidur per hari.
Quality.of.Sleep : Peringkat subyektif dari kualitas tidur, mulai dari 1 hingga 10.
Physical.Activity.Level : Jumlah menit seseorang melakukan aktivitas fisik setiap hari.
Stress.Level : Peringkat subyektif dari tingkat stres yang dialami oleh orang tersebut, mulai dari 1 hingga 10.
BMI.Category : Kategori BMI orang tersebut (misalnya, Berat Badan Kurang, Normal, Kelebihan Berat Badan).
Blood.Pressure : Pengukuran tekanan darah seseorang, diindikasikan sebagai tekanan sistolik di atas tekanan diastolik.
Heart.Rate : Detak jantung istirahat seseorang dalam detak per menit.
Daily.Steps : Jumlah langkah yang dilakukan seseorang per hari.
Sleep.Disorder : Ada atau tidaknya gangguan tidur pada seseorang (None, Insomnia, Sleep Apnea).
Berikut ini sedikit gambaran pada data yang digunakan.
Pada beberapa variabel yang digunakan, terdapat ketidaksesuaian tipe data, oleh karena itu yang perlu kita lakukan adalah melakukan penyesuaian tipe data pada beberapa variabel yang ada.
heal <- heal %>%
mutate(Gender = as.factor(Gender),
Occupation = as.factor(Occupation),
BMI.Category = as.factor(BMI.Category),
Blood.Pressure = as.factor(Blood.Pressure),
Sleep.Disorder = as.factor(Sleep.Disorder)) %>%
select(-Person.ID)
head(heal)Selanjutnya yaitu melakukan pengecekan terhadap missing value. Missing value perlu kita cek terlebih dahulu agar tidak mengganggu dalam melakukan pemodelan nantinya.
## Gender Age Occupation
## 0 0 0
## Sleep.Duration Quality.of.Sleep Physical.Activity.Level
## 0 0 0
## Stress.Level BMI.Category Blood.Pressure
## 0 0 0
## Heart.Rate Daily.Steps Sleep.Disorder
## 0 0 0
Sebelum melakukan pemodelan, kita perlu melihat terlebih dahulu proporsi dari target variabel yang kita miliki pada kolom BMI.Category.
##
## Normal Normal Weight Obese Overweight
## 0.52139037 0.05614973 0.02673797 0.39572193
Menggabungkan kategori “Normal”&“Normal Weight” = “Normal” Menggabungkan kategori “Obese”&“Overweight” = “Overweight”
heal$New_BMI_Category <- ifelse(heal$BMI.Category %in% c("Normal", "Normal Weight"), "Normal", "Overweight")
heal$New_BMI_Category <- as.factor(heal$New_BMI_Category)cek kembali proporsi dari target variabel yang sudah digabungkan dengan nama kolom baru New_BMI_Category.
##
## Normal Overweight
## 0.5775401 0.4224599
##
## Normal Overweight
## 216 158
Jika dilihat dari proporsi kedua kelas, sudah cukup seimbang, sehingga kita tidak terlalu membutuhkan pre-processing tambahan untuk menyeimbangkan proporsi antar dua kelas target variabel.
Langkah selanjutnya yaitu melakukan splitting train test data. Tujuannya yaitu pada data train akan kita gunakan untuk modeling, sedangkan data test akan kita gunakan sebagai penguji model yang sudah kita buat jika dihadapkan dengan unseen data. Selain itu hal ini dapat digunakan untuk melihat kemampuan model yang kita buat dalam menghadapi unseen data.
set.seed(123)
index <- sample(x = nrow(heal), size = nrow(heal)*0.8)
heal_train <- heal[index,]
heal_test <- heal[-index,]
nrow(heal_train)## [1] 299
## [1] 75
Proporsi 0.8/0.2 tidak mutlak, tergantung kebutuhan kita. Umumnya yang lebih banyak adalah untuk data train, supaya model punya data yang banyak untuk training.
Melakukan pemodelan menggunakan regresi logistik. Pemodelan menggunakan fungsi glm() dalam memodelkan menggunakan regresi logistik. Variabel yang digunakan adalah beberapa variabel yang kita anggap mempengaruhi target variabel, dimana variabel target menjadi variabel responnya.
model_heal <- glm(formula = New_BMI_Category ~ Age + Quality.of.Sleep+Stress.Level + Physical.Activity.Level + Heart.Rate,
data = heal_train,
family = "binomial")
summary(model_heal)##
## Call:
## glm(formula = New_BMI_Category ~ Age + Quality.of.Sleep + Stress.Level +
## Physical.Activity.Level + Heart.Rate, family = "binomial",
## data = heal_train)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 6.62112 9.95588 0.665 0.50602
## Age 0.46986 0.06169 7.617 2.60e-14 ***
## Quality.of.Sleep -5.63114 0.78929 -7.134 9.72e-13 ***
## Stress.Level -2.37241 0.52178 -4.547 5.45e-06 ***
## Physical.Activity.Level 0.02316 0.01507 1.537 0.12423
## Heart.Rate 0.36299 0.12150 2.987 0.00281 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 409.401 on 298 degrees of freedom
## Residual deviance: 82.641 on 293 degrees of freedom
## AIC: 94.641
##
## Number of Fisher Scoring iterations: 7
Pada pemodelan yang pertama, masih banyak variabel prediktor yang tidak signifikan terhadap target variabel, oleh karena itu kita akan coba melakukan model fitting menggunakan metode stepwise.
##
## Call:
## glm(formula = New_BMI_Category ~ Age + Quality.of.Sleep + Stress.Level +
## Physical.Activity.Level + Heart.Rate, family = "binomial",
## data = heal_train)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 6.62112 9.95588 0.665 0.50602
## Age 0.46986 0.06169 7.617 2.60e-14 ***
## Quality.of.Sleep -5.63114 0.78929 -7.134 9.72e-13 ***
## Stress.Level -2.37241 0.52178 -4.547 5.45e-06 ***
## Physical.Activity.Level 0.02316 0.01507 1.537 0.12423
## Heart.Rate 0.36299 0.12150 2.987 0.00281 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 409.401 on 298 degrees of freedom
## Residual deviance: 82.641 on 293 degrees of freedom
## AIC: 94.641
##
## Number of Fisher Scoring iterations: 7
Dengan menggunakan model_step hasil dari stepwise, kita akan coba prediksi menggunakan data test yang sudah kita miliki.
Kita akan coba melihat sebaran peluang prediksi data.
ggplot(heal_test, aes(x=prob_heal)) +
geom_density(lwd=0.5) +
labs(title = "Distribution of Probability Prediction Data") +
theme_minimal()Pada grafik diatas, dapat diinterpretasikan bahwa hasil prediksi yang dilakukan lebih condong ke arah 1 yang artinya Overweight.
heal_test$pred_heal <- factor(ifelse(heal_test$prob_heal > 0.5, "Overweight","Normal"))
heal_test[4:14,
c("pred_heal", "New_BMI_Category")]Dalam syntax diatas, ketika probabilitas data test lebih dari 0.5, artinya dia normal atau tidak kegemukan.
Untuk mengevaluasi model yang telah kita buat, kita akan menggunakan confusion matrix.
library(caret)
log_conf <- confusionMatrix(heal_test$pred_heal, heal_test$New_BMI_Category, positive = "Normal")
log_conf## Confusion Matrix and Statistics
##
## Reference
## Prediction Normal Overweight
## Normal 45 1
## Overweight 2 27
##
## Accuracy : 0.96
## 95% CI : (0.8875, 0.9917)
## No Information Rate : 0.6267
## P-Value [Acc > NIR] : 9.176e-12
##
## Kappa : 0.9151
##
## Mcnemar's Test P-Value : 1
##
## Sensitivity : 0.9574
## Specificity : 0.9643
## Pos Pred Value : 0.9783
## Neg Pred Value : 0.9310
## Prevalence : 0.6267
## Detection Rate : 0.6000
## Detection Prevalence : 0.6133
## Balanced Accuracy : 0.9609
##
## 'Positive' Class : Normal
##
Re-call/Sensitivity = dari semua data aktual yang positif, seberapa mampu proporsi model saya menebak benar.v
Specificity = dari semua data aktual yang negatif, seberapa mampu proporsi model saya menebak yang benar.
Accuracy = seberapa mampu model saya menebak dengan benar target Y. v
Precision = dari semua hasil prediksi, seberapa mampu model saya dapat menebak benar kelas positif.
Digunakan untuk mengetahui threshold maksimum dari apa yang akan kita teliti.
performa <- function(cutoff, prob, ref, postarget, negtarget)
{
predict <- 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")
return(mat)
}
co <- seq(0.01,0.80,length=100)
result <- matrix(0,100,4)
for(i in 1:100){
result[i,] = performa(cutoff = co[i],
prob = heal_test$prob_heal,
ref = heal_test$New_BMI_Category,
postarget = "Overweight",
negtarget = "Normal")
}
data_frame("Recall" = result[,1],
"Accuracy" = result[,2],
"Precision" = result[,3],
"Specificity" = result[,4],
"Cutoff" = co) %>%
gather(key = "performa", value = "value", 1:4) %>%
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 perfomance") +
theme_minimal() +
theme(legend.position = "top",
panel.grid.minor.y = element_blank(),
panel.grid.minor.x = element_blank())## Warning: `data_frame()` was deprecated in tibble 1.1.0.
## ℹ Please use `tibble()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.
Berdasarkan Tradeoff model performance diatas, dapat kita tahu bahwa dengan cutoff 0.5 kita memperoleh nilai Recall dan Accuracy yang agak tinggi, namun nilai Specificity dan nilai Precision agak rendah.
Membuat variabel dummy dari data-data kategori yang akan digunakan dalam klasifikasi.
dmy <- dummyVars(" ~New_BMI_Category+Age+Quality.of.Sleep+Stress.Level+Physical.Activity.Level+Heart.Rate",
data = heal)
dmy <- data.frame(predict(dmy, newdata = heal))
str(dmy)## 'data.frame': 374 obs. of 7 variables:
## $ New_BMI_Category.Normal : num 0 1 1 0 0 0 0 1 1 1 ...
## $ New_BMI_Category.Overweight: num 1 0 0 1 1 1 1 0 0 0 ...
## $ Age : num 27 28 28 28 28 28 29 29 29 29 ...
## $ Quality.of.Sleep : num 6 6 6 4 4 4 6 7 7 7 ...
## $ Stress.Level : num 6 8 8 8 8 8 7 6 6 6 ...
## $ Physical.Activity.Level : num 42 60 60 30 30 30 40 75 75 75 ...
## $ Heart.Rate : num 77 75 75 85 85 85 82 70 70 70 ...
Menghapus variabel dummy yang variabel sebelumnya hanya terdapat 2 kategori.
Mengetahui nama-nama dari variabel dummy yang terbentuk.
## [1] "New_BMI_Category.Overweight" "Age"
## [3] "Quality.of.Sleep" "Stress.Level"
## [5] "Physical.Activity.Level" "Heart.Rate"
Membentuk data training dan data testing dari data dmy yang telah terbentuk dan menggunakan fungsi sample dengan proporsi 70% untuk memilih indeks data yang akan digunakan sebagai data pelatihan.
set.seed(123)
train_indices <- sample(1:nrow(dmy), nrow(dmy) * 0.7)
dmy_train <- dmy[train_indices, 2:6]
dmy_test <- dmy[-train_indices, 2:6]Melakukan prediksi dengan K-NN
Membuat confusion matriks dari prediski K-NN
## Confusion Matrix and Statistics
##
## Reference
## Prediction 0 1
## 0 63 6
## 1 4 40
##
## Accuracy : 0.9115
## 95% CI : (0.8433, 0.9567)
## No Information Rate : 0.5929
## P-Value [Acc > NIR] : 3.739e-14
##
## Kappa : 0.8154
##
## Mcnemar's Test P-Value : 0.7518
##
## Sensitivity : 0.8696
## Specificity : 0.9403
## Pos Pred Value : 0.9091
## Neg Pred Value : 0.9130
## Prevalence : 0.4071
## Detection Rate : 0.3540
## Detection Prevalence : 0.3894
## Balanced Accuracy : 0.9049
##
## 'Positive' Class : 1
##
Berdasarkan hasil confusion matrix diatas, dapat kita ketahui bahwa kemampuan model dalam menebak target Y sebesar 92,9%. Sedangkan berdasarkan data aktual orang yang memiliki status Overweight, model dapat menebak dengan benar sebesar 98,4%. berdasarkan data aktual orang yang memiliki status Normal, model dapat menebak dengan benar sebesar 85,8%. Dari keseluruhan hasil prediksi yang mampu ditebak oleh model, model mampu menebak benar kelas positif sebesar 97,7%.
eval_logit <- data_frame(Accuracy = log_conf$overall[1],
Recall = log_conf$byClass[1],
Specificity = log_conf$byClass[2],
Precision = log_conf$byClass[3])
eval_knn <- data_frame(Accuracy = pred_knn_conf$overall[1],
Recall = pred_knn_conf$byClass[1],
Specificity = log_conf$byClass[2],
Precision = pred_knn_conf$byClass[3])Model Evaluation Logit
Model Evaluation K-NN
Jika dilihat dari kedua metode tersebut, yaitu dengan menggunakan Regresi Logistik dan K-NN, kemampupuan model dalam memprediksi benar dari data aktual orang yang Overweight lebih baik dengan menggunakan metode Regresi Logistik karena memiliki nilai lebih besar dari pada menggunakan metode K-NN.