Bangalore Online Food Delivery Preferences - Prediksi
Pengantar
Latar Belakang
Di era perkembangan teknologi sekarang ini berdampak ke seluruh elemen kehidupan masyarakat termasuk industri kuliner. Jika kita ingin memesan sebuah makanan kita tak perlu lagi datang ke restoran tersebut. Cukup hanya dengan menggunakan aplikasi layanan pesan antar makanan yang berbasis online. Banyak negara sudah menggunakan aplikasi tersebut. Salah satunya India di kota Bangalore. Permintaan akan layanan pesan antar makanan online meningkat di sana.
Kita dapat menggunakan dataset Bangalore Online Food Delivery Preferences untuk mengetahui konsumen yang akan melakukan pemesanan kembali dengan menggunakan model machine learning. Kita akan membuat model Decision Tree dan Random Forest untuk mengetahui performa kedua model tersebut.
Tentang Dataset
Dataset Bangalore Online Food Delivery Preferences merupakan data hasil survei yang mencakup data pribadi dan pendapat konsumen pengguna aplikasi layanan pesan antar makanan online di kota Bangalore. Dataset tersebut memiliki 388 baris dan 55 kolom yang beberapa di antaranya.
Age
: Umur dalam tahun Gender
: Jenis
kelamin Marital.Status
: Status
Occupation
: Pekerjaan Monthly.Income
:
Pendapatan per bulan Educational.Qualifications
:
Pendidikan terakhir Family.size
: Jumlah anggota
keluarga latitude
: Garis lintang tempat tinggal
longitude
: Garis bujur tempat tinggal
Pin.code
: Kode pos tempat tinggal
Set Up
Yang pertama kita lakukan adalah memanggil library yang dibutuhkan.
library(tidyverse)
library(GGally)
library(reactable)
library(MLmetrics)
library(lmtest)
library(car)
library(caret)
library(class)
library(e1071)
library(partykit)
library(randomForest)
Data Wrangling
Selanjutnya kita input dataset Bangalore Online Food Delivery
Preferences menggunakan fungsi read.csv()
dan
menyimpannya ke dalam variabel deliv
. Kita perlu
menggunakan parameter stringsAsFactors
untuk mengubah tipe
data character menjadi factor.
<- read.csv("onlinedeliverydata.csv",
deliv stringsAsFactors = T)
reactable(deliv, defaultPageSize = 5)
Kita menggunakan fungsi glimpse()
untuk melihat apakah
masih ada tipe data yang belum sesuai.
glimpse(deliv)
## Rows: 388
## Columns: 55
## $ Age <int> 20, 24, 22, 22, 22, 27, 22, …
## $ Gender <fct> Female, Female, Male, Female…
## $ Marital.Status <fct> Single, Single, Single, Sing…
## $ Occupation <fct> Student, Student, Student, S…
## $ Monthly.Income <fct> No Income, Below Rs.10000, B…
## $ Educational.Qualifications <fct> Post Graduate, Graduate, Pos…
## $ Family.size <int> 4, 3, 3, 6, 4, 2, 3, 3, 2, 4…
## $ latitude <dbl> 12.9766, 12.9770, 12.9551, 1…
## $ longitude <dbl> 77.5993, 77.5773, 77.6593, 7…
## $ Pin.code <int> 560001, 560009, 560017, 5600…
## $ Medium..P1. <fct> Food delivery apps, Food del…
## $ Medium..P2. <fct> Web browser, Web browser, …
## $ Meal.P1. <fct> Breakfast, Snacks, Lunch, Sn…
## $ Meal.P2. <fct> Lunch, Dinner, Snacks, D…
## $ Perference.P1. <fct> Non Veg foods (Lunch / Dinne…
## $ Perference.P2. <fct> Bakery items (snacks), Veg…
## $ Ease.and.convenient <fct> Neutral, Strongly agree, Str…
## $ Time.saving <fct> Neutral, Strongly agree, Str…
## $ More.restaurant.choices <fct> Neutral, Strongly agree, Str…
## $ Easy.Payment.option <fct> Neutral, Strongly agree, Neu…
## $ More.Offers.and.Discount <fct> Neutral, Strongly agree, Neu…
## $ Good.Food.quality <fct> Neutral, Neutral, Disagree, …
## $ Good.Tracking.system <fct> Neutral, Agree, Neutral, Agr…
## $ Self.Cooking <fct> Neutral, Strongly agree, Dis…
## $ Health.Concern <fct> Neutral, Strongly agree, Neu…
## $ Late.Delivery <fct> Neutral, Agree, Neutral, Neu…
## $ Poor.Hygiene <fct> Neutral, Strongly agree, Agr…
## $ Bad.past.experience <fct> Neutral, Strongly agree, Agr…
## $ Unavailability <fct> Neutral, Strongly agree, Agr…
## $ Unaffordable <fct> Neutral, Strongly agree, Agr…
## $ Long.delivery.time <fct> Agree, Strongly agree, Agree…
## $ Delay.of.delivery.person.getting.assigned <fct> Agree, Strongly agree, Agree…
## $ Delay.of.delivery.person.picking.up.food <fct> Agree, Strongly agree, Agree…
## $ Wrong.order.delivered <fct> Agree, Strongly agree, Stron…
## $ Missing.item <fct> Agree, Strongly agree, Agree…
## $ Order.placed.by.mistake <fct> Agree, Strongly agree, Neutr…
## $ Influence.of.time <fct> Yes, Yes, Yes, Yes, Yes, Yes…
## $ Order.Time <fct> Weekend (Sat & Sun), Anytime…
## $ Maximum.wait.time <fct> 30 minutes, 30 minutes, 45 m…
## $ Residence.in.busy.location <fct> Agree, Strongly Agree, Agree…
## $ Google.Maps.Accuracy <fct> Neutral, Neutral, Strongly A…
## $ Good.Road.Condition <fct> Neutral, Disagree, Neutral, …
## $ Low.quantity.low.time <fct> Neutral, Strongly disagree, …
## $ Delivery.person.ability <fct> Neutral, Agree, Agree, Agree…
## $ Influence.of.rating <fct> Yes, Yes, Yes, Yes, Yes, Yes…
## $ Less.Delivery.time <fct> Moderately Important, Very I…
## $ High.Quality.of.package <fct> Moderately Important, Very I…
## $ Number.of.calls <fct> Moderately Important, Very I…
## $ Politeness <fct> Moderately Important, Very I…
## $ Freshness <fct> Moderately Important, Very I…
## $ Temperature <fct> Moderately Important, Very I…
## $ Good.Taste <fct> Moderately Important, Very I…
## $ Good.Quantity <fct> Moderately Important, Very I…
## $ Output <fct> Yes, Yes, Yes, Yes, Yes, Yes…
## $ Reviews <fct> "Nil\n", "Nil", "Many a time…
Kita perlu mengubah tipe data kolom Family.size
dan
Pin.code
menjadi factor untuk mempermudah model dalam
melakukan prediksi.
<-
deliv %>%
deliv mutate(Family.size = as.factor(Family.size),
Pin.code = as.factor(Pin.code))
Selanjutnya kita perlu membuang beberapa kolom yaitu
latitude
, longitude
, dan Reviews
karena kita tidak melakukan analisa teks dan geospasial.
<-
deliv %>%
deliv select(-c(55, 8, 9))
Kita juga perlu melihat apakah terdapat missing value pada dataset
kita dengan menggunakan fungsi anyNA()
.
anyNA(deliv)
## [1] FALSE
Tidak terdapat missing value pada dataset kita.
Exploratory Data Analysis
Sebelum kita melakukan Cross Validation kita perlu melihat apakah variabel target sudah seimbang atau belum.
table(deliv$Output)
##
## No Yes
## 87 301
Hasil di atas menunjukkan jika variabel target yang kita punya belum
seimbang. Untuk mengatasi hal tersebut kita akan menggunakan metode
Down Sample. Kita akan mengurangi kelas mayoritas
hingga seimbang dengan kelas minoritas dengan menggunakan fungsi
downSample()
.
RNGkind(sample.kind = "Rounding")
set.seed(100)
<- downSample(x = deliv %>% select(-Output),
deliv y = deliv$Output,
yname = "Output")
Kita perlu melihat kembali proporsi variabel target yang kita punya.
table(deliv$Output)
##
## No Yes
## 87 87
Variabel target pada data kita sudah seimbang.
Data Preprocessing
Kita akan melakukan Cross Validation dengan proporsi data train 80% dan sisanya menjadi data test.
RNGkind(sample.kind = "Rounding")
set.seed(100)
<- sample(nrow(deliv), nrow(deliv)*0.8)
index
<- deliv[index,]
deliv_train <- deliv[-index,] deliv_test
Modeling
Model pertama yang akan kita buat adalah Decision
Tree. Decision Tree merupakan model yang
memiliki bentuk seperti pohon keputusan yang dapat digunakan untuk
regresi maupun klasifikasi. Kita akan membuat model Decision
Tree dengan menggunakan fungsi ctree()
dan
menyimpannya ke dalam variabel dtree_model
. Di dalam fungsi
ctree()
terdapat parameter control
yang harus
kita isi seperti mincriterion
, minsplit
, dan
minbucket
. Kita akan mengisi 0,95 pada
mincriterion
karena kita ingin memilih prediktor signifikan
yang memiliki nilai p kurang dari 0,05. Pada parameter
minsplit
dan minbucket
kita isi nol agar model
tidak memiliki nilai minimal jumlah observasi dalam membuat cabang pada
interior node dan terminal node.
<- ctree(formula = Output ~.,
dtree_model data = deliv_train,
control = ctree_control(mincriterion = 0.95,
minsplit = 0,
minbucket = 0))
Selanjutnya kita membuat plot dari model yang telah kita buat.
plot(dtree_model, type = "simple")
Kita dapat melihat nilai p, n, dan err. Nilai p adalah tingkat signifikansi variabel prediktor. Nilai n adalah jumlah observasi pada terminal node. Dan nilai err adalah jumlah error dalam persentasi.
Kita akan melakukan prediksi dengan menggunakan fungsi
predict()
pada data deliv_train
dan
deliv_test
. Hal ini kita lakukan untuk mengetahui apakah
model overfitting, underfitting, atau sudah baik. Prediksi pada data
train dan data test kita simpan ke dalam variabel
train_pred
dan test_pred
.
<- predict(object = dtree_model,
train_pred newdata = deliv_train,
type = "response")
<- predict(object = dtree_model,
test_pred newdata = deliv_test,
type = "response")
Untuk mengetahui apakah model overfitting, underfitting, atau sudah
baik bisa dengan menggunakan fungsi confusionMatrix()
pada
hasil kedua prediksi yang tadi telah kita buat.
confusionMatrix(data = train_pred,
reference = deliv_train$Output,
positive = "Yes")
## Confusion Matrix and Statistics
##
## Reference
## Prediction No Yes
## No 51 1
## Yes 21 66
##
## Accuracy : 0.8417
## 95% CI : (0.7702, 0.8981)
## No Information Rate : 0.518
## P-Value [Acc > NIR] : 1.076e-15
##
## Kappa : 0.6863
##
## Mcnemar's Test P-Value : 5.104e-05
##
## Sensitivity : 0.9851
## Specificity : 0.7083
## Pos Pred Value : 0.7586
## Neg Pred Value : 0.9808
## Prevalence : 0.4820
## Detection Rate : 0.4748
## Detection Prevalence : 0.6259
## Balanced Accuracy : 0.8467
##
## 'Positive' Class : Yes
##
confusionMatrix(data = test_pred,
reference = deliv_test$Output,
positive = "Yes")
## Confusion Matrix and Statistics
##
## Reference
## Prediction No Yes
## No 9 1
## Yes 6 19
##
## Accuracy : 0.8
## 95% CI : (0.6306, 0.9156)
## No Information Rate : 0.5714
## P-Value [Acc > NIR] : 0.003999
##
## Kappa : 0.5739
##
## Mcnemar's Test P-Value : 0.130570
##
## Sensitivity : 0.9500
## Specificity : 0.6000
## Pos Pred Value : 0.7600
## Neg Pred Value : 0.9000
## Prevalence : 0.5714
## Detection Rate : 0.5429
## Detection Prevalence : 0.7143
## Balanced Accuracy : 0.7750
##
## 'Positive' Class : Yes
##
Kita dapat melihat nilai Accuracy pada kedua hasil prediksi relatif cukup dekat. Hal ini mengindikasikan model yang sudah kita buat tidak mengalami overfitting maupun underfitting atau sudah baik.
Tetapi kita coba mengevaluasi model dengan mengisi parameter
minsplit
20 dan minbucket
tujuh. Kita ingin
memberi minimal batasan observasi pada model untuk membuat cabang di
interior node maupun terminal node. Model tersebut kita simpan ke dalam
variabel dtree_model2
.
<- ctree(formula = Output ~.,
dtree_model2 data = deliv_train,
control = ctree_control(mincriterion = 0.95,
minsplit = 20,
minbucket = 7))
Kita melakukan prediksi kembali pada data train dan data test. Kita
simpan ke dalam variabel train_pred2
dan
test_pred2
.
<- predict(object = dtree_model2,
train_pred2 newdata = deliv_train,
type = "response")
<- predict(object = dtree_model2,
test_pred2 newdata = deliv_test,
type = "response")
Kita akan melihat performa model yang telah kita buat dengan
menggunakan fungsi confusionMatrix()
sekaligus memastikan
model kita tidak overfitting maupun underfitting.
confusionMatrix(data = train_pred2,
reference = deliv_train$Output,
positive = "Yes")
## Confusion Matrix and Statistics
##
## Reference
## Prediction No Yes
## No 51 5
## Yes 21 62
##
## Accuracy : 0.8129
## 95% CI : (0.7381, 0.874)
## No Information Rate : 0.518
## P-Value [Acc > NIR] : 4.23e-13
##
## Kappa : 0.6285
##
## Mcnemar's Test P-Value : 0.003264
##
## Sensitivity : 0.9254
## Specificity : 0.7083
## Pos Pred Value : 0.7470
## Neg Pred Value : 0.9107
## Prevalence : 0.4820
## Detection Rate : 0.4460
## Detection Prevalence : 0.5971
## Balanced Accuracy : 0.8169
##
## 'Positive' Class : Yes
##
confusionMatrix(data = test_pred2,
reference = deliv_test$Output,
positive = "Yes")
## Confusion Matrix and Statistics
##
## Reference
## Prediction No Yes
## No 10 1
## Yes 5 19
##
## Accuracy : 0.8286
## 95% CI : (0.6635, 0.9344)
## No Information Rate : 0.5714
## P-Value [Acc > NIR] : 0.001202
##
## Kappa : 0.6379
##
## Mcnemar's Test P-Value : 0.220671
##
## Sensitivity : 0.9500
## Specificity : 0.6667
## Pos Pred Value : 0.7917
## Neg Pred Value : 0.9091
## Prevalence : 0.5714
## Detection Rate : 0.5429
## Detection Prevalence : 0.6857
## Balanced Accuracy : 0.8083
##
## 'Positive' Class : Yes
##
Berdasarkan hasil di atas selisih nilai Accuracy pada kedua hasil prediksi semakin kecil. Ini menandakan model yang kedua yang telah kita buat lebih baik dari model yang pertama. Kita juga dapat melihat nilai Accuracy, Specificity, dan Pos Pred Value pada prediksi data test model kedua juga lebih baik dari model yang pertama.
Model Evaluation
Setelah membuat model Decision Tree kita akan membuat model Random Forest. Model Random Forest adalah model yang terdiri dari banyak Decision Tree. Sama seperti Decision Tree, Random Forest juga dapat digunakan pada klasifikasi maupun regresi.
Sebelum membuat model kita perlu melakukan K-Fold Cross
Validation terlebih dahulu. Kita melakukan K-Fold Cross
Validation menggunakan fungsi trainControl()
dengan beberapa parameter yang perlu diisi. Parameter
method
kita isi dengan “repeatedcv” yang merupakan metode
K-Fold Cross Validation yang ingin kita gunakan.
Parameter number
kita isi lima karena kita ingin model
melakukan cross validation sebanyak lima kali. Dan parameter
repeats
kita isi tiga karena kita ingin model melakukan
tiga kali repetisi. Setelah itu kita simpan ke dalam variabel
ctrl
.
RNGkind(sample.kind = "Rounding")
set.seed(100)
<- trainControl(method = "repeatedcv",
ctrl number = 5,
repeats = 3)
Kita akan membuat model Random Forest menggunakan
fungsi train()
pada data deliv_train
dengan
semua variabel selain Output
untuk dijadikan prediktor.
Pada parameter method
kita isi dengan “rf” karena kita
ingin membuat sebuah model Random Forest. Sedangkan
parameter trControl
kita isi ctrl
hasil
K-Fold Cross Validation sebelumnya. Model yang sudah
kita buat kita simpan ke dalam variabel train_rf
.
<- train(Output ~ .,
train_rf data = deliv_train,
method = "rf",
trControl = ctrl)
train_rf
## Random Forest
##
## 139 samples
## 51 predictor
## 2 classes: 'No', 'Yes'
##
## No pre-processing
## Resampling: Cross-Validated (5 fold, repeated 3 times)
## Summary of sample sizes: 111, 111, 112, 112, 110, 111, ...
## Resampling results across tuning parameters:
##
## mtry Accuracy Kappa
## 2 0.8272639 0.6534159
## 128 0.8825731 0.7658541
## 254 0.8682692 0.7373202
##
## Accuracy was used to select the optimal model using the largest value.
## The final value used for the model was mtry = 128.
Setelah membuat model kita akan melakukan prediksi menggunakan data
deliv_test
dan menyimpannya ke dalam variabel
train_pred_rf
.
<- predict(object = train_rf,
train_pred_rf newdata = deliv_test,
type = "raw")
head(train_pred_rf)
## [1] No No No No No Yes
## Levels: No Yes
Kita dapat melihat hasil prediksi pada enam data teratas.
Kita kembali menggunakan confusionMatrix()
untuk melihat
performa model yang telah kita buat.
confusionMatrix(data = train_pred_rf,
reference = as.factor(deliv_test$Output))
## Confusion Matrix and Statistics
##
## Reference
## Prediction No Yes
## No 13 2
## Yes 2 18
##
## Accuracy : 0.8857
## 95% CI : (0.7326, 0.968)
## No Information Rate : 0.5714
## P-Value [Acc > NIR] : 6.136e-05
##
## Kappa : 0.7667
##
## Mcnemar's Test P-Value : 1
##
## Sensitivity : 0.8667
## Specificity : 0.9000
## Pos Pred Value : 0.8667
## Neg Pred Value : 0.9000
## Prevalence : 0.4286
## Detection Rate : 0.3714
## Detection Prevalence : 0.4286
## Balanced Accuracy : 0.8833
##
## 'Positive' Class : No
##
Nilai Accuracy, Specificity, dan Pos Pred Value pada model Random Forest lebih baik dari model Decision Tree sebelumnya. Tetapi pada model Decision Tree nilai Sensitivity yang didapat adalah 0,95 sedangkan pada model Random Forest 0,87.
Kita akan melakukan interpretasi menggunakan metode Variable
Importance dengan fungsi varImp()
. Dengan metode
tersebut kita dapat melihat variabel prediktor yang berperan penting
dalam pembuatan model Random Forest kita.
varImp(train_rf)
## rf variable importance
##
## only 20 most important variables shown (out of 254)
##
## Overall
## Ease.and.convenientDisagree 100.000
## Time.savingDisagree 77.622
## More.restaurant.choicesDisagree 47.950
## Age 17.424
## PolitenessVery Important 12.567
## More.Offers.and.DiscountDisagree 11.249
## Missing.itemNeutral 10.922
## Pin.code560024 7.108
## Pin.code560064 6.151
## Delay.of.delivery.person.getting.assignedNeutral 5.516
## Good.Road.ConditionStrongly Agree 5.239
## Pin.code560029 5.059
## Pin.code560066 4.658
## Good.Food.qualityStrongly agree 4.325
## Pin.code560041 4.079
## Good.Tracking.systemNeutral 3.950
## Late.DeliveryStrongly agree 3.858
## Perference.P2. Sweets 3.853
## Pin.code560028 3.510
## Poor.HygieneNeutral 3.503
Kita dapat melihat dua variabel prediktor yang berperan penting dalam
pembuatan model kita yaitu Ease.and.convenient
untuk kelas
Disagree dan Time.saving
untuk kelas Disagree. Sedangkan
variabel prediktor yang berperan cukup penting yaitu
More.restaurant.choices
untuk kelas Disagree dan variabel
numerik Age
.
Kesimpulan
Kita sudah membuat model Decision Tree dan Random Forest. Model Decision Tree sudah cukup robust dalam melakukan prediksi. Model tersebut juga dapat diinterpretasi. Tetapi model Random Forest lebih robust dalam melakukan prediksi karena merupakan gabungan dari banyak Decision Tree. Kita dapat menginterpretasi model Random Forest dengan menggunakan metode Variable Importance.