Github : Hourly Demand Forecasting of Scotty Ride-sharing Service

"Demand Forecasting

1 Intro

Scotty Technologies Inc. (“Scotty”) adalah sebuah perusahaan start-up teknologi yang didirikan pada tahun 2017 di Istanbul, Turkey. Salah satu layanan utama dari Scotty adalah motorcycle ride-sharing atau yang akrab kita ketahui Ojek Online. Menurut informasi yang dilansir dari markets.businessinsider.com, Scotty berencana untuk menjadi super-app pertama di Turkey dengan tekonologi dan model bisnis yang distruptif, serta berencana untuk melanjutkan pertumbuhan yang menjanjikan dan mencapai profitabilitas dalam waktu dekat. Berbicara mengenai pertumbuhan, tentu saja tidak luput dari demand. Oleh karena itu, projek ini ditujukan untuk membuat model analisa dan memprediksi demand transaksi per-jam pada layanan motorcycle ride-sharing di Scotty. Dalam proses pembuatan model ini, kita menggunakan data transaksi Scotty dari 2017-10-01 sampai 2017-12-02.

2 Problem Identification

Problem yang diselesaikan yaitu membuat model untuk memprediksi demand transaksi per-jam pada layanan motorcycle ride-sharing di Scotty. Namun yang hendak diprediksi dalam rentang waktu berapa lama? Mari kita cek dulu supaya dapat membantu proses pemodelan yang akan kita buat.

#> Observations: 504
#> Variables: 3
#> $ src_sub_area <chr> "sxk97", "sxk97", "sxk97", "sxk97", "sxk97", "sxk97", ...
#> $ datetime     <dttm> 2017-12-03 00:00:00, 2017-12-03 01:00:00, 2017-12-03 ...
#> $ demand       <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA...

Struktut data diatas belum sesuai. Mari sesuaikan struktur datanya dan meilhat rentang waktu yang hendak diprediksi dahulu:

#>  src_sub_area          datetime                       demand   
#>  Length:504         Min.   :2017-12-03 00:00:00   Min.   : NA  
#>  Class :character   1st Qu.:2017-12-04 17:45:00   1st Qu.: NA  
#>  Mode  :character   Median :2017-12-06 11:30:00   Median : NA  
#>                     Mean   :2017-12-06 11:30:00   Mean   :NaN  
#>                     3rd Qu.:2017-12-08 05:15:00   3rd Qu.: NA  
#>                     Max.   :2017-12-09 23:00:00   Max.   : NA  
#>                                                   NA's   :504

Data diatas merupakan summary dari data yang hendak diprediksi. Berdasarkan informasi tersebut, dapat disimpullkan bawah masalah yang perlu kita selesaikan yaitu melakukan prediksi demand per-jam di sub area sxk97, sxk9e dan sxk9 dalam rentang waktu dari 2017-12-03 00:00 sampai 2017-12-09 23:00 atau 24 jam * 1 minggu. Oke, mari kita mulai proses nya.

3 Data Preparation

3.1 Read Data

Berikut ini adalah data yang kita punya untuk melakukan analisa dan prediksi.

5 Top Line Data


5 Bottom Line Data



3.2 Variable Description

Variable Description
id Transaction id
trip_id Trip id
driver_id Driver id
rider_id Rider id
start_time Transaction Time
src_lat Request source latitude
src_lon Request source longitude
src_area Request source area
src_sub_area Request source sub-area
dest_lat Requested destination latitude
dest_lon Requested destination longitude
dest_area Requested destination area
dest_sub_area Requested destination sub-area
distance Trip distance (in KM)
status Trip status (all status considered as a demand)
confirmed_time_sec Time different from request to confirmed (in seconds)


3.3 Missing & Duplicate Value

Missing Value
Jika dilihat terdapat missing value pada variabel trip_id dan driver_id, namun tidak menjadi masalah karena pada case ini kita butuh data waktu, sub area dan demand.

#>                 id            trip_id          driver_id           rider_id 
#>                  0               4676               4676                  0 
#>         start_time            src_lat            src_lon           src_area 
#>                  0                  0                  0                  0 
#>       src_sub_area           dest_lat           dest_lon          dest_area 
#>                  0                  0                  0                  0 
#>      dest_sub_area           distance             status confirmed_time_sec 
#>                  0                  0                  0                  0


Duplicate Value
Dataset ini tidak memiliki data transaksi yang duplikat, sehingga bisa kita lanjutkan ke tahap pre-processing.



4 Data Pre-processing

4.1 Data Structure

#> Observations: 90,113
#> Variables: 16
#> $ id                 <chr> "59d005e1ffcfa261708ce9cd", "59d0066affcfa261708...
#> $ trip_id            <chr> "59d005e9cb564761a8fe5d3e", NA, "59d006c131e39c6...
#> $ driver_id          <chr> "59a892c5568be44b2734f276", NA, "599dc0dfa5b4fd5...
#> $ rider_id           <chr> "59ad2d6efba75a581666b506", "59cd704bcf482f6ce2f...
#> $ start_time         <dttm> 2017-10-01 00:00:17, 2017-10-01 00:02:34, 2017-...
#> $ src_lat            <dbl> 41.07047, 41.07487, 41.04995, 41.05287, 41.06760...
#> $ src_lon            <dbl> 29.01945, 28.99528, 29.03107, 28.99522, 28.98827...
#> $ src_area           <chr> "sxk9", "sxk9", "sxk9", "sxk9", "sxk9", "sxk9", ...
#> $ src_sub_area       <chr> "sxk9s", "sxk9e", "sxk9s", "sxk9e", "sxk9e", "sx...
#> $ dest_lat           <dbl> 41.11716, 41.08351, 41.04495, 41.08140, 41.02125...
#> $ dest_lon           <dbl> 29.03650, 29.00228, 28.98192, 28.98197, 29.11316...
#> $ dest_area          <chr> "sxk9", "sxk9", "sxk9", "sxk9", "sxk9", "sxk9", ...
#> $ dest_sub_area      <chr> "sxk9u", "sxk9e", "sxk9e", "sxk9e", "sxk9q", "sx...
#> $ distance           <dbl> 5.379250, 1.126098, 4.169492, 3.358296, 11.69357...
#> $ status             <chr> "confirmed", "nodrivers", "confirmed", "confirme...
#> $ confirmed_time_sec <dbl> 8, 0, 32, 65, 0, 27, 32, 30, 0, 24, 9, 0, 118, 1...

Dataset yang dimiliki terdiri dari 90.133 observasi dan 16 variabel. Jika dilihat dari struktur datanya, beberapa variabel memiliki tipe data yang belum sesuai. Namun, karena projek ini bertujuan untuk melakukan time-series forecasting terhadap demand per-jam pada layanan motorcycle ride-sharing di Scotty, maka data yang kita perlukan adalah waktu transaksi per-jam (start_time) dan demand (count(id)) pada setiap Sub-Ara (src_sub_area). Maka dari Berikut proses dan datanya:


4.2 Missing Sequential Datetime

Dalam melakukan analisa dan prediksi time series ada beberapa hal yang harus terpenuhi antara lain:
1. Data tidak boleh ada yang missing
2. Data harus terurut berdasarkan periode waktunya
3. Tidak boleh ada waktu atau periode yang terlewat/bolong

Terkait case ini, Jam opearasional Scotty yaitu 24jam/hari, sehingga kita harus memastikan seluruh data jam sudah lengkap secara sekuensial dari rentang tanggal minimum hingga tanggal maximum. Jika terdapat deret waktu yang kosong, maka dapat dilakukan imputasi data dengan asumsi bahwa pada waktu tersebut memang tidak terdapat transaksi, sehingga nilai demand = 0.

Berikut data hari yang tidak memiliki transaksi:

Data diatas menunjukan data waktu tanpa transaksi dari sub Area sxk97, sxk9e dan sxk9s. Total waktu tanpa transaksi yaitu 311 Jam dimana setiap baris merepresentasikan 1 jam.

4.3 Data Summary

#>  src_sub_area          datetime                       demand      
#>  Length:4536        Min.   :2017-10-01 00:00:00   Min.   :  0.00  
#>  Class :character   1st Qu.:2017-10-16 17:45:00   1st Qu.:  5.00  
#>  Mode  :character   Median :2017-11-01 11:30:00   Median : 15.00  
#>                     Mean   :2017-11-01 11:30:00   Mean   : 19.87  
#>                     3rd Qu.:2017-11-17 05:15:00   3rd Qu.: 27.00  
#>                     Max.   :2017-12-02 23:00:00   Max.   :217.00

Informasi di atas merupakan summary keseluruhan data yang kita butuhkan untuk melakukan melakukan forecasting. Dapat diketahui bahwa dataset ini merupakan data transaksi dari 3 sub area yaitu sxk97, sxk9e dan sxk9s yang terjadi dari 2017-10-01 00:00 sampai 2017-12-02 23:00 dengan jumlah minimum 0 demand dan jumlah maximum 217 demand. Demand 0 diasumsikan bahwa pada waktu tertentu memang tidak terdapat transaksi.



5 Exploratory Data Analysis

5.1 Demand Analysis by Sub-Area

Berikut ini visualisasi demand berdasarkan sub-area dari dari 2017-10-01 00:00 sampai 2017-12-02 23:00. Titik/Poin berwarna merah, hijau dan biru menandakan data deret waktu tersebut kosong atau tidak terdapat transaksi pada waktu tersebut.

Pada grafik diatas dapat dilihat bahwa jumlah demand dari ketiga sub-area relatif sama dan juga terdapat pola seasonal meskipun belum terlihat jelas detailnya. Selain itu, juga dapat dilihat demand pada Jumat,03 November 2017 pukul 18.00 dan Jumat, 24 November 2017 pukul 18.00 jauh lebih tinggi dari hari lainnya. Sayangnya data yang kita miliki belum dapat menjawab hal tersebut, namun mari kita fokus ke demand. Berikut adalah demand per-harinya:

# Daily Demand
polar_day <- scotty_demand %>% 
  mutate(day=weekdays(datetime)) %>% 
  group_by(src_sub_area,day) %>% 
  summarise(demand_per_day=sum(demand)) %>% 
  ungroup() %>% 
  mutate(
    day = ordered(day, levels=c("Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"))
  )

serror <- function(x) sqrt(var(x)/length(x)) 

ggplot(polar_day %>% filter(src_sub_area=="sxk97"), aes(x=day, y=demand_per_day, fill = day)) +
  geom_bar(width = 1, stat = "identity", color = "white", show.legend = FALSE) +
  geom_errorbar(aes(ymin = demand_per_day - serror(demand_per_day), 
                    ymax = demand_per_day + serror(demand_per_day), 
                    color = day), 
                    width = .2) + 
  scale_y_continuous(breaks = 0:nlevels(day)) +
  labs(
    title = "Daily Demand in Sub-Area sxk97"
  )+
 theme_minimal() +
  theme(axis.title = element_blank(),
        legend.position = "none",
        plot.title = element_text(hjust = 0.5, size=12, face="bold"),
        axis.text.y = element_blank(),
        axis.text.x=element_text(face="bold"))+
  coord_polar() -> day_polar1

ggplot(polar_day %>% filter(src_sub_area=="sxk9e"), aes(x=day, y=demand_per_day, fill = day)) +
  geom_bar(width = 1, stat = "identity", color = "white", show.legend = FALSE) +
  geom_errorbar(aes(ymin = demand_per_day - serror(demand_per_day), 
                    ymax = demand_per_day + serror(demand_per_day), 
                    color = day), 
                    width = .2) + 
  scale_y_continuous(breaks = 0:nlevels(day)) +
  labs(
    title = "Daily Demand in Sub-Area sxk9e"
  )+
  theme_minimal() +
  theme(axis.title = element_blank(),
        legend.position = "none",
         plot.title = element_text(hjust = 0.5, size=12, face="bold"),
        axis.text.y = element_blank(),
        axis.text.x=element_text(face="bold"))+
  coord_polar() ->day_polar2


ggplot(polar_day %>% filter(src_sub_area=="sxk9s"), aes(x=day, y=demand_per_day, fill = day)) +
  geom_bar(width = 1, stat = "identity", color = "white", show.legend = FALSE) +
  geom_errorbar(aes(ymin = demand_per_day - serror(demand_per_day), 
                    ymax = demand_per_day + serror(demand_per_day), 
                    color = day), 
                    width = .2) + 
  scale_y_continuous(breaks = 0:nlevels(day)) +
  labs(
    title = "Daily Demand in Sub-Area sxk9s"
  )+
 theme_minimal() +
  theme(axis.title = element_blank(),
        legend.position = "none",
        plot.title = element_text(hjust = 0.5,size=12, face="bold"),
        axis.text.y = element_blank(),
        axis.text.x=element_text(face="bold"))+
  coord_polar() ->day_polar3


grid.arrange(day_polar1,day_polar2,day_polar3, ncol = 3)

Grafik diatas memberikan informasi total demmand per-hari dari masing-masing Sub-Area. Secara keseluruhan, ketiga Sub-Area menunjukan pola demand per-hari yang sama, dimana total demand paling besar yaitu pada hari Jumat dan total demand paling kecil yaitu pada hari Minggu. Mari kita lihat data demand per-jamnya berikut:

# Hourly Demand
polar_hourly <- scotty_demand %>% 
  mutate(hour=hour(datetime)) %>% 
  group_by(src_sub_area,hour) %>% 
  summarise(demand_per_hour=sum(demand)) %>% 
  ungroup() %>% 
  mutate(
    hour = ordered(hour,levels=c("0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17",
                              "18","19","20","21","22","23"))
  )

ggplot(polar_hourly %>% filter(src_sub_area=="sxk97"), aes(x=hour, y=demand_per_hour, fill = hour)) +
  geom_bar(width = 1, stat = "identity", color = "white", show.legend = FALSE) +
  geom_errorbar(aes(ymin = demand_per_hour - serror(demand_per_hour), 
                    ymax = demand_per_hour + serror(demand_per_hour), 
                    color = hour), 
                    width = .2) + 
  scale_y_continuous(breaks = 0:nlevels(hour)) +
  labs(
    title = "Hourly Demand in Sub-Area sxk97",
    subtitle = "24 Hour Format"
  )+
 theme_minimal() +
  theme(axis.title = element_blank(),
        legend.position = "none",
        plot.title = element_text(hjust = 0.5,size=12, face="bold"),
        plot.subtitle = element_text(hjust = 0.5,size=11),
        axis.text.y = element_blank(),
        axis.text.x=element_text(size=11, face="bold"))+
  coord_polar() -> hour_polar1


ggplot(polar_hourly %>% filter(src_sub_area=="sxk9e"), aes(x=hour, y=demand_per_hour, fill = hour)) +
  geom_bar(width = 1, stat = "identity", color = "white", show.legend = FALSE) +
  geom_errorbar(aes(ymin = demand_per_hour - serror(demand_per_hour), 
                    ymax = demand_per_hour + serror(demand_per_hour), 
                    color = hour), 
                    width = .2) + 
  scale_y_continuous(breaks = 0:nlevels(hour)) +
  labs(
    title = "Hourly Demand in Sub-Area sxk9e",
    subtitle = "24 Hour Format"
  )+
 theme_minimal() +
  theme(axis.title = element_blank(),
        legend.position = "none",
        plot.title = element_text(hjust = 0.5,size=12, face="bold"),
        plot.subtitle = element_text(hjust = 0.5,size=11),
        axis.text.y = element_blank(),
        axis.text.x=element_text(size=11, face="bold"))+
  coord_polar() -> hour_polar2


ggplot(polar_hourly %>% filter(src_sub_area=="sxk9s"), aes(x=hour, y=demand_per_hour, fill = hour)) +
  geom_bar(width = 1, stat = "identity", color = "white", show.legend = FALSE) +
  geom_errorbar(aes(ymin = demand_per_hour - serror(demand_per_hour), 
                    ymax = demand_per_hour + serror(demand_per_hour), 
                    color = hour), 
                    width = .2) + 
  scale_y_continuous(breaks = 0:nlevels(hour)) +
  labs(
    title = "Hourly Demand in Sub-Area sxk9s",
    subtitle = "24 Hour Format"
  )+
 theme_minimal() +
  theme(axis.title = element_blank(),
        legend.position = "none",
        plot.title = element_text(hjust = 0.5,size=12, face="bold"),
        plot.subtitle = element_text(hjust = 0.5,size=11),
        axis.text.y = element_blank(),
        axis.text.x=element_text(size=11, face="bold"))+
  coord_polar() -> hour_polar3


grid.arrange(hour_polar1,hour_polar2,hour_polar3, ncol = 3)

Grafik diatas memberikan informasi total demmand per-jam dari masing-masing Sub-Area. Secara keseluruhan, ketiga Sub-Area menunjukan pola demand per-jam yang sama, dimana total demand relatif tinggi dari pukul 17:00 hingga pukul 19:00 dan total demmand relatif rendah dari pukul 01:00 hingga pukul 05:00. Namun, yang berbeda adalah total demand sub-area sxk9e juga cukup tinggi pada pukul 08:00, hal ini berbeda dengan sub-area lainnya.



5.2 Seasonal Analysis

Sebelum melakukan forecasting kita perlu mengetahui informasi tren dan seasonal yang ada pada data. Salah satu cara untuk mengetahuinya yaitu melakukan Decompose. Decompose merupakan tahapan dalam analisis time series yang digunakan untuk menguraikan beberapa komponen yang berupa:

  1. Data : data objek time-series yang diamati.
  2. Trend : pola data secara general, cenderung untuk naik atau turun.
  3. Seasonal : pola musiman yang membentuk pola berulang pada periode waktu yang tetap.
  4. Residual/Error : pola yang tidak dapat ditangkap dalam trend dan seasonal.

Jika pada hasil decompose, trend masih membentuk sebuah pola maka dapat dicurigai masih ada seasonality yang belum ditangkap oleh frekuensi data. Seharusnya trend cenderung naik atau cendurung turun atau membentuk garis yang smooth.

Sesuai dengan masalah yang hendak diselesaikan yaitu melakukan prediksi demand per-jam pada masing-masing sub-area selama 1 minggu, maka mari kita coba decompose secara harian dan mingguan terhadap salah satu sub-area.

Hasil decompose objek single sesonal time-series diatas menggunakan frekuensi Mingguan pada data 1 bulan terkahir. Beberapa informasi yang bisa kita dapat yaitu:

  1. Panel data merupakan data observasi time-series scotty pada data 1 bulan terkahir. Data ini sebenarnya sama seperti yang ditampilkan pada poin 5.1 Demand Analysis, namun karena pola kurang terlihat pada poin 5.1 tersebut maka kita perkecil analisanya menggunakan data 1 bulan terakhir.
  2. Panel seasonal merupakan komponen seasonal. Jika dilihat terdapat 4 bagian grafik yang memuncak paling tinggi dan yang lainnya rendah. Berhubung data yang kita time-series yang kita buat menggunakan frekuensi mingguan, maka hal ini dapat mengindikasikan bahwa pola seasonal yang terjadi sebenarnya secara harian dan mingguan.
  3. Panel trend merupakan komponen trend. Pola trend yang ditampilkan masih belum smooth dan terkesan masih membentuk pola pada beberapa bagian, mungkin saja terdapat pola seasonal yang belum ditangkap. Hal ini masuk akal dengan yang ditampilkan pada panel seasonal, sehingga mengindikasikan bahwa data kita merupakan multiple seasonal time series object.
  4. Panel Remainder/Error merupakan komponen error yang menunjukan pola yang tidak dapat ditangkap dalam trend dan seasonal.

Hasil decompose diatas mengindikasikan bahwa data time-series yang kita observasi memiliki pola Multi-Seasonal. Berdasarkan pola seasonalnya menunjukan bahwa data ini memiliki seasonal harian dan mingguan, sehingga mari kita mebuat data multiple seasonal time series dan melakukan decompose kembali.

Pada hasil decompose objek multiple seasonal time-series diatas, dapat dilihat pola tren terlihat lebih smooth dan tidak membentuk pola. Hal ini mengindikasikan bahwa kemungkinan besar keseluruhan seasonal sudah ditangkap. Untuk lebih jelasnya, berikut ini visualisasi seasonal perjam-nya dalam harian dan mingguan pada data observasi kita:

Pada chart diatas dapat dilihat bahwa high season terjadi pada setiap pukul 17:00 sampai 19:00 dan setiap hari Jumat.



6 Cross Validation

Dalam case ini kita akan mencoba membuat beberapa model, tentunya kita akan membagi seluruh data yang kita punya menjadi data train dan data test. Problem yang diselesaikan yaitu memprediksi demand perjam dalam 1 minggu sehingga saya akan membagi data test menggunakan data 1 minggu terakhir dan sisanya sebagai data train. Berikut interval waktu di data train dan data test:

Berikut ini visualisasi pembagian data train dan data test dalam deret waktu:

Dapat dilihat pada visualisasi di atas, dimana jumlah demand di 3 Sub-Area berbeda-beda dan pada waktu-waktu tertentu memiliki selisih jauh berbeda. Hal ini dapat mengindikasikan outlier, sehingga perlu dilakukan scaling data train pada masing-masing Sub-Area sebelum memulai pemodelan supaya tidak sensitif pada data outlier. Berikut prosesnya:

1. Convert data to wide format

2. Scalling data
Sebelumya saya sudah mencoba scalling menggunakan transformasi Log. Memang outlier-nya lebih minimum, namun hasil evaluasi errornya lebih besar dibandingkan jika scalling menggunakan transformasi square root. Sehingga, saya memutuskan scalling menggunakan transformasi square root. Berikut sampel hasil scalling:

Data yang sudah di-scalling tentunya nanti perlu dikembalikan lagi menjadi nilai aslinya, maka dibuat fungsi revert_back:

3. Convert data to long format
Berikut ini merupakan struktur data kita setelah dilakukan cross validation train-test dan scalling:

variabel datetime merupakan data waktu transaksi. variabel sample merupakan data tipe factor yang terbagi train dan test, sehingga kita dapat mengidentifikasi data yang hendak digunakan dalam pemodelan dan evaluasi model. variabel src_sub_area merupakan data nama dari Sub-Area. variabel demand merupakan data demand.

7 Nested Modelling

Pada pemodelan ini kita akan menggunakan fungsi purr dan untuk menggunakan fungsi purr, maka kita perlu merubah dataset kita yang sebelumnya berbentuk table menjadi nested table, berikut lebih jelasnya:

Data diatas merupakan format nested table, variabel src_sub_area merupakan nama sub-area, variabel train merupakan bentuk nested table yang isinya merupakan data datetime dan demand untuk data train, dan variabel test juga merupakan bentuk nested table yang isinya merupakan data datetime dan demand untuk data test.

7.1 Data Time-Series Object List

Berdasarkan proses decompose sebelumnya, kita sudah melihat bahwa data kita memiliki Mutiple Seasonal sehingga data kita akan lebih cocok apabila dikonversi menjadi objek msts dengan seasonal harian dan mingguan, namun dengan menggunakan konsep nested table kita juga dapat mengecek dengan mudah apabila data kita dikonversi ke objek ts. Hal tersebut akan sangat membantu kita untuk membdandingkan hasilnya. Sehingga, kita akan mencoba melakukan pemodelan menggunakan single seasonal time-series dengan frequency harian dan multiple seasonal time-series dengan seasonal harian dan mingguan pada masing-masing Sub-Area.

Data diatas menunjukan data time-series pada masing-masing sub-area. mari kita satukan dengan nested table sebelumnya untuk mempermudah pemodelan.

Data diatas merupakan nested table antara data train dan data test pada masing-masing sub-area yang digabungkan dengan objek time series yang sudah dibuat. Sampai dengan proses ini, berarti kita sudah memiliki objek single seasonal time-series dengan frequency harian dan multiple seasonal time-series dengan seasonal harian dan mingguan pada masing-masing Sub-Area. Mari kita siapkan model-model untuk masing-masing objek.

7.2 Time Series Model List

objek time-series yang kita buat memiliki efek seasonal, sehingga seharusnya hanya metode yang dapat mengatasi efek seasonal yang kita gunakan, namun mari kita coba beberapa model dan diterapkan pada setiap objek time-series yang sudah dibuat. Metode yang akan digunakan yaitu:

  1. ETS
  2. Holt Winter Exponential Smoothing
  3. Seasonal ARIMA
  4. STLM method=“ets”
  5. STLM method=“arima”
  6. TBATS

seluruh model diatas akan kita join/merge dengan nested table dari objek time-series yang sudah kita buat sebelumnya supaya dapat di generate dengan mudah. Berikut proses dan hasilnya:

7.3 Execute Nested Model Fitting

Chunk di bawah ini digunakan untuk menggenerate seluruh model yang dibuat. Chunk ini di-set eval=FALSE karena cukup memakan waktu. Hasil generate model disimpan dengan format file .rds.

Mari kita breakdown dulu. Nested table yang sebelumnya kita buat terdiri dari 3 Sub-Area, dan setiap sub-area dibuat menjadi 2 objek time series yaitu single seasonal dan multiple seasonal, kemudian kita membuat 6 model yang akan digunakan berdasarkan sub-area dan jenis objek time-series nya. Sehingga, totalnya kita sudah membuat 36 model time-series dimana 18 model untuk single seasonal object dan 18 model untuk multiple seasonal object. Berikut ini hasil generate seluruh model yang sudah dibuat dan siap untuk digunakan.


7.4 Model Evaluation

Berikut ini adalah hasil evaluasi seluruh model berdasarkan nilai Mean Absolute Error (MAE). Jika dilihat secara manual, nilai MAE paling kecil pada setiap sub-area dihasilkan oleh model TBATS menggunakan objek Multiple Seasonal Time Series. Berikut ini data MAE jika kita melakukan forecasting terhadap data train dan juga data test yang sudah kita siapkan.


7.5 Forecasting on Data Test

Berikut ini adalah proses dan hasil forecasting dari seluruh model terhadap data test:


Untuk lebih jelasnya, berikut visualisasi terhadap hasil forecasting diatas:


Pada chart diatas, garis abu-abu menunjukan data test yang hendak di-forecast, garis berwarna lainnya merupakan representasi hasil forecast dari model yang sudah kita buat dan titik/poin merepresentasikan hasil forecast dengan demand = 0 (waktu tanpa transaksi). Jika dilihat secara Visual, model TBATS (msts-tbats) menggunakan objek Multiple Seasonal Time Series terlihat cenderung paling bisa mengikuti pola garis dari data test pada setiap sub-area. Namun mari kita cek model terbaik dari setiap sub-area.


7.6 Automated Best Model Selection

Berikut ini adalah model terbaik yang dapat diterapkan pada ketiga sub-area:

Informasi diatas menunjukan bahwa error paling kecil pada ketiga sub-area dihasilkan oleh model TBATS menggunakan objek Multiple Seasonal Time Series dengan nilai MAE < 10, sehingga model inilah yang paling tepat untuk kita terapkan. Namun, perlu dingat sebelumnya kita membuat model ini menggunakan data train dan melakukan splitting menjadi tranning set dan test set untuk menentukan model terbaik. Untuk melakukan forecast terhadap data aktual maka kita harus menggabungkannya kembali seluruh datanya supaya deret waktu lengkap dan baru bisa menerapkan ketiga model terbaik diatas. Maka, berikut prosesnya:

Chunk di bawah ini digunakan untuk menggenerate final model yang dibuat. Chunk ini di-set eval=FALSE karena cukup memakan waktu. Hasil generate model disimpan dengan format file .rds.



8 Forecasting Result

Berdasarkan problem yang ingin diselesaikan maka kita perlu akan melakukan prediksi demand per-jam di sub area sxk97, sxk9e dan sxk9 dalam rentang waktu 1 minggu kedepan atau dari 2017-12-03 00:00 sampai 2017-12-09 23:00. Berikut ini hasil forecastingnya:


Berikut visualisasi forecast dari 2017-12-03 00:00 sampai 2017-12-09 23:00


Berdasarkan hasil forecasting untuk 3 Desember 2017 pukul 00:00 sampai 9 Desember 2017 pukul 23:00, demand paling tinggi pada ketiga sub-area terjadi pada 8 Desember 2017 pukul 18:00, dimana demand di sxk87 sebanyak 85, demand di sxk9e sebanyak 106 dan demand di sxk9s sebanyak 91.



9 Conclusion

9.1 Assumption Checking

Assumption Checking : Auto Correlation and Normality of Residual

Jika dilihat pada plot ACF residual ketiga sub-area, masih terdapat lag yang keluar dari garis biru. Hal ini dapat disimpulkan masih ada residual yang berkorelasi sehingga menunjukan bahwa masih terdapat informasi yang tertinggal yang seharusnya digunakan untuk menghitung hasil forecast. Kemudian, jika dilihat pada histogram residual pada ketiga sub-area, menunjukan bahwa residual kurang berdisitribusi normal. Memang residual cukup berpusat ditengah, namun masih terdapat data residual pada bagian kiri dan kanan yang cukup banyak. Terkait hal ini kita bisa melakukan improvement misalkan memilih train-test set dengan periode waktu berbeda, differencing, imputasi null value menggunakan nilai rata-rata, melakukan teknik scalling berbeda atau mengeksplorasi teknik-teknik pemodelan time-series forecasting lainnya.

9.1.1 Sub-Area sxk97

Residual from TBATS method applied for Sub-Area sxk97

9.1.2 Sub-Area sxk9e

Residual from TBATS method applied for Sub-Area sxk9e

9.1.3 Sub-Area sxk9s

Residual from TBATS method applied for Sub-Area sxk9s


9.2 Summary

Hasil analisa data time-series yang sudah dilakukan menunjukan layanan Ride-sharing service Scotty di sub-area sxk97, sxk9e dan sxk9s memiliki seasonal harian dan mingguan yang dimana high season terjadi pada pada setiap pukul 17:00 sampai 19:00 dan setiap hari Jumat. Selain itu, dari hasil pemodelan yang menggunakan metode ETS, HoltWinter, SARIMA, STLM dan TBATS yang sudah di-uji, model TBATS menggunakan objek Multiple Seasonal Time Series menghasilkan nilai error paling kecil dengan nilai MAE < 10. Jika dilakukan forecasting menggunakan model TBATS untuk 3 Desember 2017 pukul 00:00 sampai 9 Desember 2017 pukul 23:00, hasilnya menunjukan demand paling tinggi pada ketiga sub-area terjadi pada 8 Desember 2017 pukul 18:00, dimana demand di sxk87 sebanyak 85, demand di sxk9e sebanyak 106 dan demand di sxk9s sebanyak 91.