1. Introduction

1. Business Understanding

Dewasa ini, mobil menjadi kebutuhan hidup bagi sebagian besar masyarakat perkotaan. Peranan yang penting tersebut membuat banyak orang berlomba-lomba membeli mobil dengan berbagai alasan. Tidak sedikit orang yang menyesal setelah membeli mobil karena alasan yang kurang tepat. Untuk mengatasi hal itu, kita perlu mempertimbangkan atribut-atribut yang terdapat pada suatu mobil sehingga kita dapat menilai apakah mobil tersebut layak untuk dibeli atau tidak layak dibeli. Namun tidak semua orang memiliki keahlian atau waktu untuk mempelajari setiap atribut dari suatu mobil terutama masyarakat perkotaan yang mengingkan sesuatu yang cepat. Untuk itu diperlukan suatu metode, dimana dengan menggunakan metode tersebut mampu memberikan hasil yang cepat dari banyak informasi dan metode Machine Learing menjadi solusi.

Salah satu kemampuan dari machine learning yaitu mampu mempelajari data-data yang belum pernah dilihat sebelumnnya dengan memanfaatkan probabilitas, statistika, data historis dll. Kemampuan tersebut dapat diaplikasikan untuk melakukan evaluasi data mobil, dimana melakukan suatu evaluasi sebelum melakukan pembelian mobil adalah cara yang bijak untuk terhindar dari kesalahan dalam pengambilan keputusan sebagai mana yang dilakukan dalam projek ini.

Projek ini menggunakan data yang berasal dari kaggle yang terdiri atas data klasifikasi class dan atribut-atribut mobil. Diharapkan dapat diketahui apakah suatu harga mobil layak atau tidak berdasarkan atribut kelas yang ada berdasarkan perbandingan beberapa metode machine learning .

2. Dataset

Dataset ini dapat diunduh melalui https://www.kaggle.com/datasets/elikplim/car-evaluation-data-set.

Berikut adalah kolom yang tersedia dalam dataset ini :
- buying_price: Buying Level or Capacity of the customer (Very High: vhigh, High: high, Low: low, Medium: med)
- maint_cost: Maintenence Level (Very High: vhigh, High: high, Low: low, Medium: med)
- doors: Number of doors in the car (2, 3, 4, and 5 or more)
- person_capacity: capacity in terms of persons to carry (2, 4, and more)
- lug_boot: The size of the Luggage Boot (small, med, big)
- safety: Safety Level of Car (high, med, low)
- class: Unacceptable, Acceptable, VeryGood, Good

2. Pembacaan dan Pemahaman Data

1.Pembacaan Data

Pembacaan data menggunakan fungsi read_csv dan penggunaan parameter stringsAsFactors dikarenakan seluruh datanya bertipe data faktor.

car <- read.csv('car_evaluation.csv', stringsAsFactors = T)
head(car, n=3)
##   vhigh vhigh.1 X2 X2.1 small  low unacc
## 1 vhigh   vhigh  2    2 small  med unacc
## 2 vhigh   vhigh  2    2 small high unacc
## 3 vhigh   vhigh  2    2   med  low unacc

insight:
- seluruh data merupakan data faktor

2. Pemahaman Data

  • dari tabel diatas diketahui dalam setiap barisnya adalah data evaluasi kelayakan harga mobil.
  • terdapat 6 atribut data prediktor dan 1 data target yaitu unacc

3. Data Wrangling

1. Ubah nama kolom menggunakan ketentuan berikut

Pengubahan nama kolom dilakukan agar nama kolom lebih informatif dengan memanfaat kan library dplyr pada fungsi mutate. Kemudian dalam penelitian ini levels class data diperkecil dari 4 class menjadi 2 class menggunakn fungsi recode().

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
car <- car |> 
  select(
    
    buying_price = vhigh,
    maintenance_cost = vhigh.1,
    doors = X2,
    person_capacity = X2.1,
    luggage_boot = small,
    safety = low,
    class = unacc
  ) |> 
  mutate(
    
    class = recode(class, 
                   'acc' = 'acceptable',
                   'good' = 'acceptable',
                   'vgood' = 'acceptable',
                   'unacc' = 'unaceptable')
    
  )

2. Cek Struktur Data

cek structure data untuk melihat kesesuaian tipe data.

glimpse(car)
## Rows: 1,727
## Columns: 7
## $ buying_price     <fct> vhigh, vhigh, vhigh, vhigh, vhigh, vhigh, vhigh, vhig…
## $ maintenance_cost <fct> vhigh, vhigh, vhigh, vhigh, vhigh, vhigh, vhigh, vhig…
## $ doors            <fct> 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,…
## $ person_capacity  <fct> 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, mo…
## $ luggage_boot     <fct> small, small, med, med, med, big, big, big, small, sm…
## $ safety           <fct> med, high, low, med, high, low, med, high, low, med, …
## $ class            <fct> unaceptable, unaceptable, unaceptable, unaceptable, u…

insight:
- seluruh data merupakan data faktor
_ data terdiri dari 1727 baris dan 7 kolom
- tidak ada data yang perlu diubah

3. Cek Missing Value

dilakukan untuk mengilangkan data yang tidak informatif dan dapat mempengaruhi hasil

anyNA(car)
## [1] FALSE

insight:
- dalam dataset ini tidak ada missing value

4. Exploratory Data Analisis

1. Lihat Summary

summary(car)
##  buying_price maintenance_cost   doors     person_capacity luggage_boot
##  high :432    high :432        2    :431   2   :575        big  :576   
##  low  :432    low  :432        3    :432   4   :576        med  :576   
##  med  :432    med  :432        4    :432   more:576        small:575   
##  vhigh:431    vhigh:431        5more:432                               
##   safety            class     
##  high:576   acceptable : 518  
##  low :575   unaceptable:1209  
##  med :576                     
## 

insight :
- pada kolom class, class data unaceptable lebih banyak daripada class data acceptable

2. Cek Proporsi Kelas

prop.table(table(car$class))
## 
##  acceptable unaceptable 
##   0.2999421   0.7000579

insight:
- dataset masih mengandung imbalance data pada data target

5. Cross Validation

Tahapan ini membagi data menjadi data test dan data train.

1. lakukan split data

Dilakukan proses spliting data dengan proporsi 80:20

RNGkind(sample.kind = "Rounding")
## Warning in RNGkind(sample.kind = "Rounding"): non-uniform 'Rounding' sampler
## used
set.seed(100)

# train-test splitting
index <- sample(nrow(car), nrow(car)*0.80)

car_train <- car[index,]
car_test <- car[-index,]

2. cek proporsi kelas

cek proporsi kelas pada data train sebagai data uji

prop.table(table(car_train$class))
## 
##  acceptable unaceptable 
##   0.3019551   0.6980449

insight:
- dataset masih mengandung imbalance data pada data train
- perlu dilakukan balancing data agar prediksi yang dilakukan tidak hanya mampu memprediksi dengan baik hanya salah satu kelas

3. Handling Imabalanced Data

balancing data dilakukan dengan menggunakan metode upsampling.

RNGkind(sample.kind = "Rounding")
## Warning in RNGkind(sample.kind = "Rounding"): non-uniform 'Rounding' sampler
## used
set.seed(100)
library(caret)
## Warning: package 'caret' was built under R version 4.3.1
## Loading required package: ggplot2
## Loading required package: lattice
car_train <- upSample(x = car_train %>% select(-class),
                       y = car_train$class,
                       yname = "class")

4.Cek kembali proporsi data train

prop.table(table(car_train$class))
## 
##  acceptable unaceptable 
##         0.5         0.5

insight:
- dataset sudah balance
- handling imbalance data hanya dilakukan pada data train, karena untuk menjaga originalitas data test

6. Pemodelan Naive Bayes

1. Membuat Model Naive Bayes

menggunakan fungsi naibeBayes dari library e1071. Dalam fungsi disertakan parameter laplace untuk mengatasi keterbatasan naiveBayes.

library(e1071)
model_nb <- naiveBayes( formula = class ~ .,
                        data = car_train,
                        laplace = 1)

2. Membuat Pemodelan Prediksi

melakukan pemodelan prediksi pada data test

car_test$pred_label <- predict(object = model_nb,
                                newdata = car_test,
                                   type = 'class')

3. Membuat Pemodelan Evaluasi

melakukan evaluasi model menggunakan confusion matrix.

library(caret)
# confusion matrix
eval_naive_test <- confusionMatrix(data = car_test$pred_label, reference=car_test$class)
eval_naive_test
## Confusion Matrix and Statistics
## 
##              Reference
## Prediction    acceptable unaceptable
##   acceptable         101          37
##   unaceptable          0         208
##                                                
##                Accuracy : 0.8931               
##                  95% CI : (0.8556, 0.9236)     
##     No Information Rate : 0.7081               
##     P-Value [Acc > NIR] : < 0.00000000000000022
##                                                
##                   Kappa : 0.7665               
##                                                
##  Mcnemar's Test P-Value : 0.000000003252       
##                                                
##             Sensitivity : 1.0000               
##             Specificity : 0.8490               
##          Pos Pred Value : 0.7319               
##          Neg Pred Value : 1.0000               
##              Prevalence : 0.2919               
##          Detection Rate : 0.2919               
##    Detection Prevalence : 0.3988               
##       Balanced Accuracy : 0.9245               
##                                                
##        'Positive' Class : acceptable           
## 

4. Menghitung Nilai ROC

Nilai ROC digunakan sebagai metrics evaluasi pada data-data yang belum balance. Dalam hal ini dilakukan opsional karena sudah dilakukan balancing data.

# ambil hasil prediksi dalam bentuk probability
car_pred_prob <- predict(object = model_nb, 
                          newdata = car_test,
                          type = "raw")

head(car_pred_prob)
##          acceptable unaceptable
## [1,] 0.000001274331   0.9999987
## [2,] 0.515808467572   0.4841915
## [3,] 0.000514274723   0.9994857
## [4,] 0.476847591227   0.5231524
## [5,] 0.453431631749   0.5465684
## [6,] 0.440627962633   0.5593720
data_roc_naive <- data.frame(pred_prob = car_pred_prob[,"acceptable"],
                       actual = ifelse(car_test$class == "acceptable", 1, 0))


head(data_roc_naive)
##        pred_prob actual
## 1 0.000001274331      0
## 2 0.515808467572      0
## 3 0.000514274723      0
## 4 0.476847591227      0
## 5 0.453431631749      0
## 6 0.440627962633      0

5. Menghitung nilai AUC

library(ROCR)
## Warning: package 'ROCR' was built under R version 4.3.1
#object prediction

naive_roc <- prediction(predictions = data_roc_naive$pred_prob,
                      labels = data_roc_naive$actual)

# nilai AUC

naive_auc <- performance(naive_roc, measure = "auc")
naive_auc@y.values[[1]]
## [1] 0.9952314

6. Menampilkan Plot Hasil Nilai AUC

plot(performance(naive_roc, "tpr", "fpr"))
abline(0, 1, lty = 2)
text(0.4, 0.6, paste("AUC = ", round(naive_auc@y.values[[1]], 2)))

7. Pemodelan Decision Tree

1. Membuat Pemodelan Decision Tree

library(partykit)
## Loading required package: grid
## Loading required package: libcoin
## Loading required package: mvtnorm
model_dt <- ctree(formula = car_train$class ~.,
                  data = car_train %>% select(-class),
                  control = ctree_control(mincriterion=0.99))

plot(model_dt, type = "simple")

2. Membuat Pemodelan Prediksi dan Evaluasi

# prediction to data test
pred_test_dt <- predict(model_dt, newdata = car_test)
eval_dt_test <- confusionMatrix(pred_test_dt, reference = car_test$class, positive = "acceptable")
eval_dt_test
## Confusion Matrix and Statistics
## 
##              Reference
## Prediction    acceptable unaceptable
##   acceptable         101          10
##   unaceptable          0         235
##                                                
##                Accuracy : 0.9711               
##                  95% CI : (0.9475, 0.9861)     
##     No Information Rate : 0.7081               
##     P-Value [Acc > NIR] : < 0.00000000000000022
##                                                
##                   Kappa : 0.9321               
##                                                
##  Mcnemar's Test P-Value : 0.004427             
##                                                
##             Sensitivity : 1.0000               
##             Specificity : 0.9592               
##          Pos Pred Value : 0.9099               
##          Neg Pred Value : 1.0000               
##              Prevalence : 0.2919               
##          Detection Rate : 0.2919               
##    Detection Prevalence : 0.3208               
##       Balanced Accuracy : 0.9796               
##                                                
##        'Positive' Class : acceptable           
## 

3. Menghitung Nilai ROC

# ambil hasil prediksi dalam bentuk probability
car_pred_prob <- predict(object = model_dt, 
                          newdata = car_test,
                          type = "prob")
# menyiapkan pred vs actual
data_roc_dt <- data.frame(pred_prob = car_pred_prob[,"acceptable"],
                       actual = ifelse(car_test$class == "acceptable", 1, 0))


head(data_roc_dt)
##    pred_prob actual
## 6          0      0
## 14         0      0
## 18         0      0
## 38         0      0
## 47         0      0
## 49         0      0

4. Menghitung Nilai AUC

#object prediction

dt_roc <- prediction(predictions = data_roc_dt$pred_prob,
                      labels = data_roc_dt$actual)

# nilai AUC

dt_auc <- performance(dt_roc, measure = "auc")
dt_auc@y.values[[1]]
## [1] 0.9872702

5. Menampilkan Plot Hasil Nilai AUC

plot(performance(dt_roc, "tpr", "fpr"))
abline(0, 1, lty = 2)
text(0.4, 0.6, paste("AUC = ", round(dt_auc@y.values[[1]], 2)))

8. Pemodelan Random Forest

1. Membuat Pemodelan Random Forest

set.seed(417)

ctrl <- trainControl(method = "repeatedcv",
                      number = 5, # k-fold
                      repeats = 3) # repetisi
car_forest <- train(class ~ .,
                    data = car_train,
                    method = "rf", # random forest
                    trControl = ctrl)

2. Membuat Pemodelan Evaluasi

#confusion matrix data test
pred_test_rf <- predict(car_forest, newdata = car_test)
eval_rf_test <- confusionMatrix(pred_test_rf, reference = car_test$class, positive = "acceptable")
eval_rf_test
## Confusion Matrix and Statistics
## 
##              Reference
## Prediction    acceptable unaceptable
##   acceptable         100          10
##   unaceptable          1         235
##                                               
##                Accuracy : 0.9682              
##                  95% CI : (0.9438, 0.984)     
##     No Information Rate : 0.7081              
##     P-Value [Acc > NIR] : < 0.0000000000000002
##                                               
##                   Kappa : 0.9251              
##                                               
##  Mcnemar's Test P-Value : 0.01586             
##                                               
##             Sensitivity : 0.9901              
##             Specificity : 0.9592              
##          Pos Pred Value : 0.9091              
##          Neg Pred Value : 0.9958              
##              Prevalence : 0.2919              
##          Detection Rate : 0.2890              
##    Detection Prevalence : 0.3179              
##       Balanced Accuracy : 0.9746              
##                                               
##        'Positive' Class : acceptable          
## 

3. Menghitung Nilai ROC

# ambil hasil prediksi dalam bentuk probability
car_pred_prob <- predict(object = car_forest, 
                          newdata = car_test,
                          type = "prob")

head(car_pred_prob)
##    acceptable unaceptable
## 6       0.000       1.000
## 14      0.000       1.000
## 18      0.000       1.000
## 38      0.000       1.000
## 47      0.194       0.806
## 49      0.000       1.000
# menyiapkan pred vs actual
data_roc_rf <- data.frame(pred_prob = car_pred_prob[,"acceptable"],
                       actual = ifelse(car_test$class == "acceptable", 1, 0))


head(data_roc_rf)
##   pred_prob actual
## 1     0.000      0
## 2     0.000      0
## 3     0.000      0
## 4     0.000      0
## 5     0.194      0
## 6     0.000      0

4. Menghitung Nilai AUC

library(ROCR)

#object prediction

rf_roc <- prediction(predictions = data_roc_rf$pred_prob,
                      labels = data_roc_rf$actual)

# nilai AUC

rf_auc <- performance(rf_roc, measure = "auc")
rf_auc@y.values[[1]]
## [1] 0.9920388
plot(performance(rf_roc, "tpr", "fpr"))
abline(0, 1, lty = 2)
text(0.4, 0.6, paste("AUC = ", round(rf_auc@y.values[[1]], 2)))

9. Kesimpulan

1. Komparasi antar Model

eval_naive <- data_frame(Accuracy = eval_naive_test$overall[1],
           Recall = eval_naive_test$byClass[1],
           Specificity = eval_naive_test$byClass[2],
           Precision = eval_naive_test$byClass[3],
           AUC=naive_auc@y.values[[1]])
## 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.
eval_dt <- data_frame(Accuracy = eval_dt_test$overall[1],
           Recall = eval_dt_test$byClass[1],
           Specificity = eval_dt_test$byClass[2],
           Precision = eval_dt_test$byClass[3],
           AUC=dt_auc@y.values[[1]])

eval_rf <- data_frame(Accuracy = eval_rf_test$overall[1],
           Recall = eval_rf_test$byClass[1],
           Specificity = eval_rf_test$byClass[2],
           Precision = eval_rf_test$byClass[3],
           AUC=rf_auc@y.values[[1]])
eval_all <- rbind("Naive Bayes" = eval_naive, "Decision Tree" = eval_dt, "Random Forest" = eval_rf)
eval_all <- eval_all |> mutate(
  name = c('Naive Bayes','Decision Tree', 'Random Forest')
) |> select(
  name, Accuracy, Recall, Specificity, Precision, AUC
)

eval_all
## # A tibble: 3 × 6
##   name          Accuracy Recall Specificity Precision   AUC
##   <chr>            <dbl>  <dbl>       <dbl>     <dbl> <dbl>
## 1 Naive Bayes      0.893  1           0.849     0.732 0.995
## 2 Decision Tree    0.971  1           0.959     0.910 0.987
## 3 Random Forest    0.968  0.990       0.959     0.909 0.992

insight :
- dari tabel diatas diketahui bahwa nilai Precision tertinggi diperoleh oleh model decision tree, Decision Tree mampu memprediksi dengan baik nilai Recall dibandingkan nilai lain
- nilai AUC sangat baik hampir mendekati sempurna, hal ini disebabkan proporsi data yang digunakan telah dilakukan upsampling

2. Interpretasi metrics yang digunakan

  • Target variabel = class
  • Kelas positif = acceptable
  • Metrics = Precision
  • metode yang dipilih = Decision Tree karena mampu memberikan nilai precision tertinggi mengacu pada table diatas

FN: model memprediksi unacceptable(harga mobil tidak layak beli), padahal aslinya acceptable(harga mobil layak beli).
- Resiko: Customer kehilangan kesempatan membeli mobil murah.

FP: model memprediksi acceptable(harga mobil layak beli), padahal unacceptable(harga mobil tidak layak beli).
- Resiko: Customer rugi karena mendapat barang yang kurang sesuai.

Risiko yang concerning adalah jika terjadi kejadian FP sehingga ambil metriks evaluasi Precision

-sebagai sebagai seorang data scientist saya akan menggunakan metode Decision Tree untuk melakukan klasifikasi kelas evaluasi harga mobil dan menggunakan Recall untuk mengehindari potensi kerugian setelah pembelian.