library(dplyr) # manipulate data
library(ggplot2) # visualization
library(MLmetrics) # evaluation metrics

1 Study Case

transaction <- read.csv("data_input/sales.csv")
transaction

1.1 Data Preprocessing

Beberapa hal yang harus diperhatikan ketika melakukan analisis data time series, yaitu:

  1. Konversi tipe kolom date ke date
  2. Memastikan data terurut dengan baik (dari lampau ke terbaru)
  3. Membuat data menjadi interval tetap -> Hari, bulan, tahun
  4. Mempersiapkan data sesuai dengan format prophet

1.1.1 Konversi tipe kolom date

Kolom yang akan masuk ke dalam pemodelan prophet harus bertipe date dengan format YYYY-MM-DD untuk sebuah tanggal. Oleh sebab itu, wajib kita lakukan konversi tipe data terlebih dahulu.

# mengecek tipe data
glimpse(transaction)
#> Rows: 235,636
#> Columns: 9
#> $ X              <int> 1385158, 1385159, 1385160, 1385161, 1385162, 1385163, 1…
#> $ date           <chr> "03.01.2013", "02.01.2013", "11.01.2013", "26.01.2013",…
#> $ date_block_num <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
#> $ shop_id        <int> 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,…
#> $ shop_name      <chr> "Moscow shopping center \"Semenovsky\"", "Moscow shoppi…
#> $ item_id        <int> 4906, 4906, 4890, 4901, 4901, 4901, 4901, 4901, 4901, 4…
#> $ item_price     <dbl> 1794.00, 1789.00, 799.00, 1499.00, 1499.00, 1499.00, 16…
#> $ item_cnt_day   <int> 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1…
#> $ total_revenue  <dbl> 3588.0, 1789.0, 799.0, 1499.0, 1499.0, 1499.0, 1699.0, …

📌 Untuk melakukan konversi ini dengan efisien, kita dapat memanfaatkan library lubridate di R. Library ini menyediakan berbagai fungsi yang memudahkan manipulasi dan konversi tipe data tanggal. Salah satu fungsi yang sangat berguna adalah dmy(), yang dapat digunakan untuk mengubah string tanggal menjadi objek date dengan format yang tepat.

library(lubridate) # manipulate data

📅 Ingat kembali cheatsheet berikut: Cheatsheet Lubridate

# mengubah tipe data ke date
transaction <- transaction %>% 
  mutate(date = dmy(date))
  
glimpse(transaction)
#> Rows: 235,636
#> Columns: 9
#> $ X              <int> 1385158, 1385159, 1385160, 1385161, 1385162, 1385163, 1…
#> $ date           <date> 2013-01-03, 2013-01-02, 2013-01-11, 2013-01-26, 2013-0…
#> $ date_block_num <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
#> $ shop_id        <int> 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,…
#> $ shop_name      <chr> "Moscow shopping center \"Semenovsky\"", "Moscow shoppi…
#> $ item_id        <int> 4906, 4906, 4890, 4901, 4901, 4901, 4901, 4901, 4901, 4…
#> $ item_price     <dbl> 1794.00, 1789.00, 799.00, 1499.00, 1499.00, 1499.00, 16…
#> $ item_cnt_day   <int> 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1…
#> $ total_revenue  <dbl> 3588.0, 1789.0, 799.0, 1499.0, 1499.0, 1499.0, 1699.0, …

1.1.2 Memastikan data terurut

Salah satu syarat dalam membuat model Time Series adalah data tanggal harus terurut dengan baik dari data paling lampau sampai dengan data paling baru.

📌 Untuk mengurutkan data dengan dplyr kita dapat menggunakan fungsi arrange(), dimana secara default akan mengurutkan secara ascending

transaction <- transaction %>% 
  arrange(date)

1.1.3 Membuat data menjadi interval tetap

Hal yang penting dari sebuah data time series adalah memiliki interval waktu tiap baris yang tetap, seperti interval harian, mingguan ataupun bulanan. Data kita memiliki banyak transaksi yang terjadi dalam sehari. Oleh karena itu, data kita masih dalam interval tidak tetap.

head(transaction)
library(tidyr) # manipulate data
daily_transaction <- transaction %>% 
  group_by(date) %>% 
  summarise(item_cnt_day = sum(item_cnt_day),
            total_revenue = sum (total_revenue))

daily_transaction

Sebagai catatan, ketika melakukan agregasi data, kita hanya dapat mengubah ke bentuk data dari periode yang lebih singkat ke periode lebih panjang, sebagai contoh:

  • Periode jam ke harian
  • Periode harian ke mingguan
  • Periode harian ke bulanan
  • Periode bulanan ke tahunan, dst.

1.1.4 Mempersiapkan data sesuai dengan format prophet

Untuk menggunakan prophet, kita perlu mempersiapkan data time series ke dalam format yang spesifik sesuai kebutuhan library ini. prophet membutuhkan dataframe dengan 2 kolom berikut:

  • ds: kolom dengan informasi waktu, harus bertipe data date
  • y: nilai yang ingin di-forecast

Merubah nama kolom dengan fungsi rename(new_colname = old_colname)

daily_transaction <- daily_transaction %>% 
  rename("ds" = date,
         "y" = total_revenue)
daily_transaction

— Checkpoint —

Persiapan data di prophet (berurutan):

  1. Ubah tipe data untuk kolom tanggal
  2. Ubah formatnya ke dalam bentuk yang diinginkan (tahun, bulan-tahun, daily, quarterly, dst)
  3. Bisa dilihat apakah sudah berurutan belum tanggalnya (lampau - terbaru)
  4. Mengubah format date = ds dan target = y -> formating dari prophet

2 (NEW) Preprocessing Data dari 1 tahun yang lalu

Data dari 2 Januari 2013 hingga 31 Oktober 2015, kita ingin coba ambil data item_cnt_day satu tahun terakhir (data ini akan digunakan sebagai prediktor future)

daily_transaction %>% summary()
#>        ds              item_cnt_day          y          
#>  Min.   :2013-01-02   Min.   : 102.0   Min.   :  84199  
#>  1st Qu.:2013-09-16   1st Qu.: 221.5   1st Qu.: 150015  
#>  Median :2014-06-02   Median : 275.0   Median : 193096  
#>  Mean   :2014-06-01   Mean   : 301.4   Mean   : 228145  
#>  3rd Qu.:2015-02-15   3rd Qu.: 358.5   3rd Qu.: 256967  
#>  Max.   :2015-10-31   Max.   :1080.0   Max.   :2177573

Pertama, nilai item_cnt_day disimpan untuk nanti digunakan ketika ingin melakukan pembuatan dataframe future

future_item_cnt <- daily_transaction$item_cnt_day
future_item_cnt
#>    [1]  568  423  431  415  435  312  354  262  254  315  488  313  228  246
#>   [15]  274  242  365  497  327  240  256  283  286  407  461  347  299  281
#>   [29]  268  296  362  465  364  265  325  297  398  459  566  415  282  378
#>   [43]  321  381  454  454  414  396  446  377  496  749  614  395  241  265
#>   [57]  313  268  378  453  374  323  447  416  803  619  421  379  293  416
#>   [71]  383  338  439  542  443  291  302  355  340  433  529  370  295  308
#>   [85]  342  261  449  535  393  212  286  277  262  388  513  374  254  320
#>   [99]  247  277  366  483  296  227  195  208  234  399  395  310  225  262
#>  [113]  215  293  392  468  397  309  537  375  224  264  314  247  298  273
#>  [127]  382  212  202  217  256  195  170  240  233  416  386  329  246  329
#>  [141]  322  329  440  438  401  272  310  251  411  441  406  380  291  336
#>  [155]  265  367  421  378  330  270  383  377  275  463  283  263  253  283
#>  [169]  259  290  401  353  271  325  308  399  351  416  422  253  348  303
#>  [183]  328  321  456  352  282  220  332  272  363  412  388  332  289  337
#>  [197]  324  241  323  361  249  277  268  228  276  346  293  266  201  284
#>  [211]  228  303  442  351  285  275  317  341  279  336  302  221  243  230
#>  [225]  249  284  345  356  255  268  256  283  313  378  401  355  338  310
#>  [239]  325  375  423  443  478  319  440  352  309  414  482  455  223  248
#>  [253]  321  264  356  432  394  254  785  312  316  457  488  329  185  280
#>  [267]  317  376  467  536  352  263  291  307  226  391  462  374  330  313
#>  [281]  339  361  498  502  348  237  302  263  312  400  472  367  207  213
#>  [295]  240  275  472  507  367  265  414  316  383  425  480  460  330  460
#>  [309]  307  287  483  524  413  312  263  245  256  429  561  467  270  341
#>  [323]  335  412  461  508  358  266  324  325  296  575  537  376  325  348
#>  [337]  330  300  482  575  392  233  345  304  363  455  642  415  340  395
#>  [351]  453  406  526  705  550  459  485  646  691  861 1028  962 1035  891
#>  [365]  557  520  434  380  440  408  362  304  270  488  344  210  276  226
#>  [379]  239  292  402  303  217  284  223  264  404  388  307  242  230  323
#>  [393]  283  397  442  352  286  222  336  285  316  428  365  254  312  245
#>  [407]  237  490  417  387  289  311  327  378  556  673  480  200  226  241
#>  [421]  245  428  516  274  236  286  272  346  562  421  346  278  194  166
#>  [435]  198  417  417  367  266  236  283  229  455  389  356  193  182  225
#>  [449]  265  297  410  315  246  208  250  280  394  405  300  202  253  247
#>  [463]  269  360  450  300  310  437  256  322  348  387  236  162  223  213
#>  [477]  240  335  367  274  189  257  423  212  194  249  224  168  213  244
#>  [491]  327  149  173  262  158  143  200  179  324  321  232  161  195  268
#>  [505]  243  325  291  202  250  481  312  310  418  398  218  215  218  234
#>  [519]  233  372  320  228  236  324  325  335  287  218  173  207  281  220
#>  [533]  220  267  399  219  167  205  260  241  313  335  204  227  217  202
#>  [547]  234  267  235  212  184  237  237  239  290  289  180  238  198  192
#>  [561]  254  223  210  207  176  260  252  210  252  206  263  188  210  277
#>  [575]  217  295  226  228  222  229  243  237  307  291  241  239  235  222
#>  [589]  232  278  322  265  207  246  262  232  321  342  246  204  270  234
#>  [603]  294  311  402  365  253  235  207  266  374  483  252  225  284  268
#>  [617]  315  341  295  327  240  233  225  259  327  291  282  197  232  229
#>  [631]  357  372  448  284  221  261  266  330  357  386  279  221  316  247
#>  [645]  255  391  401  253  200  207  221  248  289  420  226  178  256  198
#>  [659]  222  268  389  307  175  182  214  246  309  394  286  310  253  200
#>  [673]  305  317  421  269  208  253  264  393  370  476  315  217  482  285
#>  [687]  358  465  410  308  290  229  245  236  403  561  342  237  302  248
#>  [701]  275  359  458  368  268  318  233  322  418  492  403  354  311  356
#>  [715]  315  516  577  527  424  427  454  473  712  942  690  839 1080  912
#>  [729]  549  409  365  460  523  409  296  281  297  284  191  266  188  247
#>  [743]  311  395  278  213  174  246  173  267  377  262  172  199  169  208
#>  [757]  293  313  252  188  162  185  199  337  298  293  212  217  195  163
#>  [771]  316  434  238  194  234  239  250  475  377  340  352  201  216  201
#>  [785]  282  354  262  182  199  274  298  384  447  323  233  228  179  210
#>  [799]  208  301  209  115  181  138  171  263  288  254  182  194  256  203
#>  [813]  255  306  236  154  161  134  163  274  316  252  234  197  226  164
#>  [827]  294  337  222  188  459  283  188  344  289  295  229  232  160  225
#>  [841]  281  307  228  147  113  213  347  307  171  111  143  162  213  177
#>  [855]  199  129  155  191  130  114  121  256  252  172  133  514  326  192
#>  [869]  259  313  233  160  174  199  189  206  236  190  151  216  208  177
#>  [883]  271  219  173  131  197  259  325  262  163  102  201  241  230  263
#>  [897]  180  244  231  168  251  193  173  192  230  151  176  182  185  202
#>  [911]  247  191  145  219  181  207  222  225  227  197  188  200  184  152
#>  [925]  266  244  165  182  194  172  139  215  243  139  167  143  159  206
#>  [939]  181  182  112  161  160  196  195  184  162  140  138  167  170  160
#>  [953]  187  142  145  196  168  155  231  225  218  183  195  179  204  212
#>  [967]  241  297  225  184  315  170  209  232  290  233  193  206  184  218
#>  [981]  277  380  240  129  141  156  188  259  198  156  104  346  244  193
#>  [995]  258  233  198  137  182  236  205  245  238  208  131  158  232  143
#> [1009]  259  249  207  140  192  184  167  259  234  180  156  183  140  165
#> [1023]  239  291  265  134  123  117  152  267  249

2.1 Extract data item count selain 1 tahun terakhir

Kita akan ambil data item_cnt_day diatas 1 tahun terakhir, yaitu dari 2013-01-02 hingga 2014-10-31, yang nantinya akan kita gunakan sebagai prediktor untuk memprediksi data 2014-01-02 hingga 2015-10-31 (ceritanya data masa lalu yang digunakan sebagai prediktor di masa kini).

past_transaction <- daily_transaction %>% filter(ds <= "2014-10-31")
past_transaction

simpan data item_cnt_day masa lalu

past_item_cnt <- past_transaction$item_cnt_day
past_item_cnt
#>   [1]  568  423  431  415  435  312  354  262  254  315  488  313  228  246  274
#>  [16]  242  365  497  327  240  256  283  286  407  461  347  299  281  268  296
#>  [31]  362  465  364  265  325  297  398  459  566  415  282  378  321  381  454
#>  [46]  454  414  396  446  377  496  749  614  395  241  265  313  268  378  453
#>  [61]  374  323  447  416  803  619  421  379  293  416  383  338  439  542  443
#>  [76]  291  302  355  340  433  529  370  295  308  342  261  449  535  393  212
#>  [91]  286  277  262  388  513  374  254  320  247  277  366  483  296  227  195
#> [106]  208  234  399  395  310  225  262  215  293  392  468  397  309  537  375
#> [121]  224  264  314  247  298  273  382  212  202  217  256  195  170  240  233
#> [136]  416  386  329  246  329  322  329  440  438  401  272  310  251  411  441
#> [151]  406  380  291  336  265  367  421  378  330  270  383  377  275  463  283
#> [166]  263  253  283  259  290  401  353  271  325  308  399  351  416  422  253
#> [181]  348  303  328  321  456  352  282  220  332  272  363  412  388  332  289
#> [196]  337  324  241  323  361  249  277  268  228  276  346  293  266  201  284
#> [211]  228  303  442  351  285  275  317  341  279  336  302  221  243  230  249
#> [226]  284  345  356  255  268  256  283  313  378  401  355  338  310  325  375
#> [241]  423  443  478  319  440  352  309  414  482  455  223  248  321  264  356
#> [256]  432  394  254  785  312  316  457  488  329  185  280  317  376  467  536
#> [271]  352  263  291  307  226  391  462  374  330  313  339  361  498  502  348
#> [286]  237  302  263  312  400  472  367  207  213  240  275  472  507  367  265
#> [301]  414  316  383  425  480  460  330  460  307  287  483  524  413  312  263
#> [316]  245  256  429  561  467  270  341  335  412  461  508  358  266  324  325
#> [331]  296  575  537  376  325  348  330  300  482  575  392  233  345  304  363
#> [346]  455  642  415  340  395  453  406  526  705  550  459  485  646  691  861
#> [361] 1028  962 1035  891  557  520  434  380  440  408  362  304  270  488  344
#> [376]  210  276  226  239  292  402  303  217  284  223  264  404  388  307  242
#> [391]  230  323  283  397  442  352  286  222  336  285  316  428  365  254  312
#> [406]  245  237  490  417  387  289  311  327  378  556  673  480  200  226  241
#> [421]  245  428  516  274  236  286  272  346  562  421  346  278  194  166  198
#> [436]  417  417  367  266  236  283  229  455  389  356  193  182  225  265  297
#> [451]  410  315  246  208  250  280  394  405  300  202  253  247  269  360  450
#> [466]  300  310  437  256  322  348  387  236  162  223  213  240  335  367  274
#> [481]  189  257  423  212  194  249  224  168  213  244  327  149  173  262  158
#> [496]  143  200  179  324  321  232  161  195  268  243  325  291  202  250  481
#> [511]  312  310  418  398  218  215  218  234  233  372  320  228  236  324  325
#> [526]  335  287  218  173  207  281  220  220  267  399  219  167  205  260  241
#> [541]  313  335  204  227  217  202  234  267  235  212  184  237  237  239  290
#> [556]  289  180  238  198  192  254  223  210  207  176  260  252  210  252  206
#> [571]  263  188  210  277  217  295  226  228  222  229  243  237  307  291  241
#> [586]  239  235  222  232  278  322  265  207  246  262  232  321  342  246  204
#> [601]  270  234  294  311  402  365  253  235  207  266  374  483  252  225  284
#> [616]  268  315  341  295  327  240  233  225  259  327  291  282  197  232  229
#> [631]  357  372  448  284  221  261  266  330  357  386  279  221  316  247  255
#> [646]  391  401  253  200  207  221  248  289  420  226  178  256  198  222  268
#> [661]  389  307  175  182  214  246  309

2.2 Geser data item count 1 tahun kedepan

Kemudian, kita akan ubah data item_cnt_day dari transaksi 1 tahun setelah transaksi pertama terjadi (setelah 2014-01-02) dengan past_item_cnt yang merupakan transaksi 1 tahun sebelumnya.

daily_transaction <- daily_transaction %>% 
  filter(ds >= "2014-01-02") %>% 
  mutate(item_cnt_day = past_item_cnt)

daily_transaction

3 Modeling using prophet

Konsep dasar dalam pemahaman kasus time series adalah dengan melakukan pemecahan komponen dari data time series (decompose) dimana komponen terbagi menjadi 2, yaitu:

  • Trend (\(T\)): Pergerakan data secara general
  • Seasonality (\(S\)): Pola yang berulang akibat kondisi musiman

Decompose time series ini banyak macamnya dan yang digunakan oleh Prophet secara default adalah metode General Additive Model (GAM). Ide di balik GAM adalah ketiga komponen di atas dijumlahkan untuk menyusun data time series kita, secara matematis:

\[ Y(t) = T(t) + S(t) \]

Dimana \(Y(t)\) adalah observasi nilai time series kita.

Pada konsep time series, peramalan/forecasting time series didasari oleh kondisi/perilaku di masa lalu. Jika disimpulkan, maka konsep untuk melakukan forecasting nilai masa depan adalah memperhatikan kondisi trend dan seasonal dari data time series.

3.1 Model Fitting

🔻 Setelah semua persiapan data selesai. Kini kita tugaskan prophet untuk mencari pola dari data time series kita.

  1. Import library prophet
  2. Membuat inisialisasi model prophet dengan fungsi prophet()
  3. Menginput dataset yang akan dipelajari dengan method fit.prophet()
library(prophet) # modeling ts

Tambahkan regressor kolom item_cnt_day

# model fitting
model_prophet <- prophet(yearly.seasonality=TRUE)
model_prophet <- model_prophet %>% add_regressor('item_cnt_day')
model_prophet <- model_prophet %>% fit.prophet(daily_transaction)

3.2 Forecast

Karena model kita telah belajar dari pola data masa lalu, kita akan coba meramal/forecast data kita untuk 1 tahun ke depan. Untuk itu kita harus mempersipakan dataframe baru yang berisi informasi waktu yang ingin di-forecast. Hal ini dapat dilakukan menggunakan method .make_future_dataframe() dari objek model kita, yaitu model_prophet.

Parameter fungsi make_future_dataframe():

  • m : nama model
  • periods: Banyaknya data yang ingin di-forecast
  • freq: interval data. "day", "month", dan lainnya.
future_item_cnt %>% length()
#> [1] 1031
future <- make_future_dataframe(m = model_prophet,
                                periods = 364,
                                freq = "day")

future

Tambahkan kolom item_cnt_day pada future dataframe dengan data item_cnt_day masa lalu hingga masa depan yang sebelumnya sudah disimpan

future$item_cnt_day <- future_item_cnt
future

Dataframe future berisi informasi tanggal tepat 364 hari setelah data fitting berakhir. Kita akan menggunakan dataframe ini untuk melakukan forecasting menggunakan method predict().

forecast <- predict(model_prophet, future)

Dari berbagai nilai di atas kita akan mengambil beberapa informasi saja, yaitu:

  • ds: Tanggal
  • trend: Nilai trend (pola general)
  • weekly: Nilai seasonal weekly (pola mingguan)
  • yearly: Nilai seasonal yearly (pola tahunan)
  • yhat: Nilai hasil forecast
forecast %>% 
  select(ds, trend, weekly, yearly, yhat) %>% tail()

3.3 Visualization

Setelah membuat model dan forecast, dapat dilakukan visualisasi dengan method plot() dengan memasukan parameter sebagai berikut:

  • x = object model prophet model_prophet
  • fcst = dataframe hasil dari fungsi predict() yaitu object forecast
# code here
plot(model_prophet, forecast)

Keterangan:

  • Titik hitam pada plot menunjukkan data aktual
  • Garis biru gelap menunjukkan hasil “fitted” atau prediksinya beserta nilai-nilai ramalannya 365 hari ke depan.
  • Area yang berwarna biru muda adalah interval prediksi dari modelnya.

3.4 Model Interpretation

Dalam melakukan interpretasi model prophet, kita dapat memvisualisasikan setiap komponennya menggunakan fungsi prophet_plot_components()

  • m = object model prophet model_prophet
  • fcst = dataframe hasil dari fungsi predict() yaitu object forecast
# code here
prophet_plot_components(model_prophet, forecast)

Terdapat components baru setelah dilakukan add regressor yaitu nilai item_cnt_day