Pengantar

Pada tahap ini, akan dilakukan forecast untuk sebuah perusahaan ride-sharing. Forecast akan dilakukan menggunakan time series dari data historis permintaan yang akan dianalisa menggunakan single seasonality dan multiple seasonality.

Library yang Digunakan

Pada tahap ini, saya akan memuat library-library yang dibutuhkan untuk analisa ini.

Membaca Dataset

Di tahap ini kita akan membaca dataset dan melakukan glimpse() untuk melihat gambaran mengenai data ini.

## Observations: 90,113
## Variables: 16
## $ id                 <fct> 59d005e1ffcfa261708ce9cd, 59d0066affcfa261708ceb11…
## $ trip_id            <fct> 59d005e9cb564761a8fe5d3e, NA, 59d006c131e39c618969…
## $ driver_id          <fct> 59a892c5568be44b2734f276, NA, 599dc0dfa5b4fd5471ad…
## $ rider_id           <fct> 59ad2d6efba75a581666b506, 59cd704bcf482f6ce2fadfdb…
## $ start_time         <fct> 2017-10-01T00:00:17Z, 2017-10-01T00:02:34Z, 2017-1…
## $ 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           <fct> sxk9, sxk9, sxk9, sxk9, sxk9, sxk9, sxk9, sxk9, sx…
## $ src_sub_area       <fct> sxk9s, sxk9e, sxk9s, sxk9e, sxk9e, sxk9s, sxk9s, s…
## $ 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          <fct> sxk9, sxk9, sxk9, sxk9, sxk9, sxk9, sxk9, sxk9, sx…
## $ dest_sub_area      <fct> sxk9u, sxk9e, sxk9e, sxk9e, sxk9q, sxk9e, sxk9e, s…
## $ distance           <dbl> 5.379250, 1.126098, 4.169492, 3.358296, 11.693573,…
## $ status             <fct> confirmed, nodrivers, confirmed, confirmed, nodriv…
## $ confirmed_time_sec <int> 8, 0, 32, 65, 0, 27, 32, 30, 0, 24, 9, 0, 118, 106…

Pada analisa ini, kita hanya akan menggunakan dua variabel, yaitu variabel area dan waktu.

Data summary
Name scotty
Number of rows 90113
Number of columns 2
_______________________
Column type frequency:
factor 2
________________________
Group variables None

Variable type: factor

skim_variable n_missing complete_rate ordered n_unique top_counts
start_time 0 1 FALSE 88729 201: 4, 201: 3, 201: 3, 201: 3
src_sub_area 0 1 FALSE 3 sxk: 40869, sxk: 24732, sxk: 24512

src_sub_area adalah variabel yang berisi lokasi dan dalam data ini hanya ada tiga macam saja. Sedangkan variabel start_time memiliki format yang salah. Oleh sebab itu, kita akan menggunakan fungsi dari lubridate untuk mengubah formatnya.

Data summary
Name scotty
Number of rows 90113
Number of columns 2
_______________________
Column type frequency:
factor 1
POSIXct 1
________________________
Group variables None

Variable type: factor

skim_variable n_missing complete_rate ordered n_unique top_counts
src_sub_area 0 1 FALSE 3 sxk: 40869, sxk: 24732, sxk: 24512

Variable type: POSIXct

skim_variable n_missing complete_rate min max median n_unique
start_time 0 1 2017-10-01 00:00:17 2017-12-02 23:59:20 2017-11-07 14:39:07 88729

Format POSIXctadalah format untuk tanggal. Jadi saat ini data kita sudah dalam format yang benar.

Variabel start_time adalah waktu ketika penumpang melakukan permintaan. Dalam data aslinya, data tanggal ini terdiri dari tanggal, bulan, tahun serta jam, menit, detik. Dalam tahap ini kita akan membuat variabel baru yaitu datetime dimana kita akan membulatkan komponen waktu ke dalam satuan jam. Hal ini berguna untuk memudahkan analisa data, karena tidak masuk akal untuk memprediksi dengan pola menit atau pola detik dalam hal ini.

Untuk membulatkan waktu ke dalam satuan jam, kita akan menggunakan fungsi floor_date() dari library lubridate. Lalu kita akan menjumlahkan pembulatan-pembulatan tadi sehingga tidak ada datetime yang ganda.

## # A tibble: 4,225 x 3
## # Groups:   src_sub_area [3]
##    src_sub_area datetime            demand
##    <fct>        <dttm>               <int>
##  1 sxk97        2017-10-01 00:00:00      6
##  2 sxk97        2017-10-01 01:00:00      4
##  3 sxk97        2017-10-01 02:00:00      9
##  4 sxk97        2017-10-01 03:00:00      2
##  5 sxk97        2017-10-01 04:00:00      5
##  6 sxk97        2017-10-01 05:00:00      4
##  7 sxk97        2017-10-01 06:00:00      1
##  8 sxk97        2017-10-01 08:00:00      3
##  9 sxk97        2017-10-01 09:00:00      3
## 10 sxk97        2017-10-01 10:00:00      3
## # … with 4,215 more rows

Salah satu karakteristik untuk analisa data timeseries adalah periode waktu yang lengkap, tidak ada periode waktu yang tidak tersedia untuk masing-masing wilayah. Maka, kita harus memastikan jumlah data per masing-masing src_sub_area memiliki jumlah yang sama.

Jumlah data demand untuk tiap area memiliki jumlah yang berbeda. Data timeseries harus tersedia setiap jam supaya model menjadi akurat. Sehingga apabila data pada jam tertentu tidak tersedia, maka data yang kosong tersebut harus dilengkapi dengan mengimputasinya dengan menggunakan fungsi complete dan seq lalu apabila pada jam tertentu terdapat data yang NA, kita akan mengisinya dengan NOL (0).

Setelah melakukan imputasi dengan fungsi pad, jumlah data tiap kategori menjadi sama.

Salah satu kriteria data timeseries yang lain adalah data yang berurut berdasarkan datetime-nya. Oleh sebab itu kita akan mengurutkannya terlebih dahulu.

Tahap pre-process sudah selesai, tetapi kita akan memastikan sekali lagi untuk memastikan bahwa data kita sudah tidak ada yang NA.

## [1] 0

Cross Validation

Di tahap ini, kita akan membagi data kita menjadi data train dan data test, dimana data test adalah satu minggu terakhir dari data secara keseluruhan.

Setelah menentukan interval data train dan data test, kita akan membuat suatu kolom baru bernama split untuk memberi tanda jika observasi pada baris itu adalah train atau test.

Nesting untuk Train dan Test

Selanjutnya kita akan melakukan nesting pada data kita. Nesting itu seperti tabel di dalam tabel, seperti berlapis. Pada tahap ini kita akan melakukan nesting pada datetime dan demand lalu akan mengubahnya menjadi data format wide dengan fungsi pivot_wider.

Membuat List Model Time-Series

Pada Exploratory Data Analysis sebelumnya, kita bisa lihat bahwa data kita bisa direpresentasikan dengan sesonal harian ataupun seasonal mingguan. Pada tahap ini kita akan membuat suatu fungsi untuk merepresentasikan kedua jenis seasonal tad.

Menggabungkan menjadi satu dataframe.

## Joining, by = "src_sub_area"

Membuat List Model untuk Forecast

Seperti kita ketahui bahwa ada banyak jenis permodelan time-series yang bisa digunakan untuk memprediksi masa depan. Pada tahap ini, kita akan membuat suatu list yang berisi fungsi-fungsi yang umumnya digunakan untuk forecast.

Menggabungkan dengan dataframe sebelumnya, sehingga kita akan memiliki data, time-series dan permodelan dalam satu dataframe.

## Joining, by = "src_sub_area"

Permodelan autoarima dengan ets tidak bisa digunakan untuk multiple seasonality time-series, maka kita akan membuangnya.

Model Fitting

Pada tahap ini, kita akan mengeksekusi model dengan fungsi map() dan invoke_map() dari package purr.

Evaluasi Model

Disini kita akan melakukan forecast dan mengevaluasi model yang sudah dibuat menggunakan MAE (Mean Absolute Erorr) dengan fungsi mae_vec dari package yardstick.

Model Improvement di Data Preprocess Menggunakan RECIPES dan Outlier Treatment

Saya akan membaca data ulang supaya bisa mulai dari data fresh lalu mengubahnya ke bentuk data wide.

Outlier Treatment

Disini saya akan melakukan treatment untuk outlier dengan teknik Winsorize, yaitu memotong data ekstrim dan menggantinya dengan angka yang kurang ekstrim. Dalam kasus ini, saya memotong 2.5% demand tertinggi dari data train dan menggantinya dengan angka pada data ke 97.5%. Winsorizing akan dilakukan menggunakan fungsi Winsorize() dari package DescTools.

Pada tahap ini saya akan mencoba untuk melakukan preprocessing data yang sebelumnya belum dilakukan. Pada tahap ini akan digunakan package recipes untuk melakukan preprocessing data.

Disini saya mendefinisikan preprocess recipe() dan bake() pada data kita dengan recipe yang didefinisikan. Data akan dinormalisai dengan mengakarkan data, lalu center dan scale.

Lalu mengaplikasikan recipe pada data.

Disini kita akan membuat fungsi untuk membalikkan data yang sudah dinormalisasi. Maksudnya, ketika kita melakukan forecast pada data yang sudah dinormalisasi, kita harus membalikkan data tersebut seperti skala yang sebenarnya.

Disini kita akan melakukan nesting data train dan data test, lalu membuat list fungsi time-series dan permodelan seperti pada tahap sebelumnya. Tidak ada yang spesial atau berbeda dari tahap ini. Tahap ini sama seperti tahap sebelumnya.

## Joining, by = "src_sub_area"
## Warning: Column `src_sub_area` joining character vector and factor, coercing
## into character vector
## Joining, by = "src_sub_area"
## Warning in HoltWinters(x): optimization difficulties: ERROR:
## ABNORMAL_TERMINATION_IN_LNSRCH

Automatic Model Selection

Di tahap ini kita akan memilih model secara otomatis, yaitu yang mempunyai nilai error terkecil.

## Warning: attributes are not identical across measure variables;
## they will be dropped

Forecast untuk Validation Set

Setelah mempunyai model terbaik, maka tahap selanjutnya adalah melakukan forecast untuk data validation set, yaitu data yang akan digunakan untuk submit capstone ini.

## Warning: attributes are not identical across measure variables;
## they will be dropped

Menyimpan prediksi untuk tanggal 3 Desember sampai dengan 9 Desember 2017.

Plotting Final Forecast

Untuk melihat hasil forecast.

Berikut di bawah ini adalah hasil leaderboard score yang didapat setelah saya submit file hasil prediksi.

Mengecek Asumsi

Salah satu kriteria model time-series yang bagus adalah tidak adanya Autocorrelation dan Normality Residual

Area sxk97

## 
##  Box-Ljung test
## 
## data:  residuals(scotty_improve_best$fitted[[1]])
## X-squared = 1.5942, df = 1, p-value = 0.2067

Area sxk9e

## 
##  Box-Ljung test
## 
## data:  residuals(scotty_improve_best$fitted[[2]])
## X-squared = 0.45287, df = 1, p-value = 0.501

Area sxk9s

## 
##  Box-Ljung test
## 
## data:  residuals(scotty_improve_best$fitted[[3]])
## X-squared = 2.7548e-06, df = 1, p-value = 0.9987

Untuk Box-Ljung, asumsinya adalah:

H0: no-autocorrelation, p-value > 0.05

H1: autocorrelation, p-value < 0.05

Karena p-value dari semua area adalah > 0.05, maka artinya model kita tidak mengandung autokorelasi

Di bawah ini adalah gradik sebaran normal untuk pengujian asumsi normalitas.

Dari grafik, kita bisa lihat secara visual bahwa data error kita terdistribusi dengan normal.

Kesimpulan

  1. Pada kasus ini, forecast menggunakan multiple seasonality menghasilkan prediksi yang lebih baik.

  2. Pada ketiga lokasi, forecast menggunakan permodelan TBATS menghasilkan prediksi yang lebih baik.

  3. Dengan melakukan outlier treatment, yaitu memotong data yang ekstrim dengan metode Winsorize serta normalisasi data dengan pengakaran, center dan scale, dapat menghasilkan prediksi yang lebih baik.

  4. Permodelan yang dilakukan, yaitu TBATS dengan tipe data multiple seasonality time series, error dari model menghasilkan error yang memenuhi asumsi.