Sales Forecast
Introduction
What are Time Series
Time series adalah suatu object dalam statistik dimana object tersebut berhubungan dengan suatu deret waktu tertentu. Objek ini banyak ditemui dalam kehidupan sehari-hari, contoh: harga daging sapi harian, curah hujan bulanan, kuantitas penumpang bulanan, pendapatan tahunan, dll.
Difference with Regression
Perbedaan mendasar antara time series dengan regresi adalah jika pada regresi untuk memprediksi suatu nilai Y dipengaruhi oleh beberapa faktor yaitu x1,x2,..,xn. Sedangkan jika time series, untuk memprediksi suatu nilai Y dipengaruhi oleh nilai Y itu sendiri pada masa lampau (Yt−1).
Analisis time series berhubungan dengan suatu data yang memiliki nilai numerik pada interval waktu tertentu. Proses untuk memprediksi nilai pada anilisis time series disebut sebagai peramalan atau forecasting. Ide utama dalam melakukan forecasting itu adalah korelasi dari data numerik.
Business Problems
PT Mobil Jaya Tbk. merupakan perusahaan otomotif mobil dari Indonesia. Perusahaan ingin mengetahui berapa perkiraan penjualan yang akan terjadi di masa mendatang. Harapannya dengan mengetahui perkiraan penjualan dimasa mendatang, perusahaan dapat mengatur target penjualan yang tepat dan realistis. Kemudian dapat mempersiapkan berbagai rencana untuk menyediakan ketersedian mobil dimasa mendatang. Lalu dapat mengatur strategi agar dapat merealisasikan nilai perkiraaan tersebut dan masih banyak lagi pemanfaatannya. Jadi kita sebagai Data Scientist diminta untuk dapat melakukan forecasting penjualan mobil domestik.
Import Libraries
library(tidyverse)
library(lubridate)
library(forecast)
library(plotly)
library(TTR)
library(fpp)Data Preprocessing
1. Read Dataset
dautonsa <- read.csv("DAUTONSA.csv")
dautonsa %>% head()glimpse(dautonsa)#> Rows: 630
#> Columns: 2
#> $ DATE <chr> "1967-01-01", "1967-02-01", "1967-03-01", "1967-04-01", "1967…
#> $ DAUTONSA <dbl> 564.1, 509.1, 670.4, 710.2, 744.8, 780.2, 627.2, 517.2, 547.3…
2. Subset Dataset
dautonsa <- dautonsa %>%
mutate(DATE = as.Date(DATE)) %>%
filter(DATE >= "2015-01-01")3. Range Dataset
dautonsa$DATE %>% range()#> [1] "2015-01-01" "2019-06-01"
4. Missing Value
is.na(dautonsa) %>% colSums()#> DATE DAUTONSA
#> 0 0
5. Duplicate Data
anyDuplicated(dautonsa)#> [1] 0
6. Checking Dataset Structure
glimpse(dautonsa)#> Rows: 54
#> Columns: 2
#> $ DATE <date> 2015-01-01, 2015-02-01, 2015-03-01, 2015-04-01, 2015-05-01, …
#> $ DAUTONSA <dbl> 386.010, 419.495, 525.027, 477.314, 563.176, 491.397, 481.665…
Time Series Model Preparation
1. Creating Time Series Object
ts_dautonsa <- ts(data = dautonsa$DAUTONSA,
start = c(2015,1),
end = c(2019,6),
frequency = 12)
ts_dautonsa %>% autoplot()2. Decomposition
Decomposition adalah suatu tahapan dalam time series analisis yang digunakan untuk menguraikan beberapa komponen dalam time series data.
💡 Komponen dalam time series :
- Trend : pola data secara general, cenderung untuk naik atau turun. Jika ada trend masih terdapat pola artinya masih ada pola yang belum terurai dengan baik.
- Seasonal : pola musiman yang membentuk pola berulang pada periode waktu yang tetap
- Error/Reminder/Random : pola yang tidak dapat ditangkap dalam trend dan seasonal Sebelum melakukan modeling forecasting kita perlu mengamati objek timeseries dari hasil decompose. Ide utama dari decompose adalah untuk menguraikan ketiga komponen dari objek ts (trend, seasonal, residual).
decom_dautonse <- decompose(ts_dautonsa)decom_dautonse %>% autoplot() %>% ggplotly() Insight :
- Trend data kita cenderung menurun.
- Data kita memiliki pola seasonal.
- Pola dataset kita dapat dikategorikan sebagai additive seasonal karena kenaikan yang relatif konstan, tidak multiplikasi.
3. Cross Validation
# test menggunakan `tail()`
test_dautonse <- tail(ts_dautonsa, 18)
test_dautonse %>% autoplot()Data test kita terdiri dari Januari 2018 hingga Juli 2019 (1,5 tahun atau 18 bulan).
# train menggunakan `head()`
train_dautonse <- head(ts_dautonsa, -length(test_dautonse))
train_dautonse %>% autoplot()Data train kita terdiri dari Januari 2015 hingga Desember 2018 (3 tahun atau 36 bulan).
Simple Moving Average
Metode yang menggunakan rataan beregerak untuk melakukan forecasting. Karena menggunakan rataan, bobot yang digunakan sama untuk setiap observasi di masa lalu. Metode ini sering digunakan untuk data yang tidak mengandung trend dan seasonal (datanya bergerak disekitar rata-rata).
1. Model Building
model_sma <- SMA(x = train_dautonse, n = 5)2. Forecasting
forecast_sma <- forecast(object = model_sma, h = 18)
forecast_sma#> Point Forecast Lo 80 Hi 80 Lo 95 Hi 95
#> Jan 2018 343.7615 338.6416 348.8815 335.9312 351.5919
#> Feb 2018 337.2671 332.2438 342.2905 329.5846 344.9497
#> Mar 2018 347.6975 342.5187 352.8762 339.7772 355.6177
#> Apr 2018 354.9979 349.7102 360.2855 346.9111 363.0846
#> May 2018 351.8024 346.5623 357.0425 343.7883 359.8165
#> Jun 2018 367.0229 361.5559 372.4899 358.6619 375.3839
#> Jul 2018 369.1836 363.6843 374.6828 360.7732 377.5940
#> Aug 2018 359.8292 354.4691 365.1892 351.6317 368.0267
#> Sep 2018 356.5469 351.2356 361.8582 348.4239 364.6698
#> Oct 2018 340.6335 335.5591 345.7079 332.8729 348.3941
#> Nov 2018 328.1200 323.2319 333.0081 320.6443 335.5957
#> Dec 2018 326.2385 321.3783 331.0987 318.8055 333.6716
#> Jan 2019 303.8766 298.7670 308.9862 296.0621 311.6911
#> Feb 2019 297.7536 292.7468 302.7605 290.0964 305.4109
#> Mar 2019 306.5603 301.4053 311.7154 298.6764 314.4443
#> Apr 2019 312.5788 307.3224 317.8352 304.5398 320.6178
#> May 2019 309.3424 304.1402 314.5445 301.3863 317.2984
#> Jun 2019 322.2758 316.8559 327.6957 313.9868 330.5648
- Point Forecast: nilai forecast untuk periode yang ingin kita forecast
- Lo 80 & Hi 80 : rentang tebakan dari hasil forecast untuk confidence level 80%
- Lo 95 & Hi 95 : rentang tebakan dari hasil forecast untuk confidence level 95%
3. Visualization
train_dautonse %>%
autoplot() +
autolayer(test_dautonse, series = "Test") + # data test
autolayer(forecast_sma$mean, series = "Forecast Data Test") + # hasil forecast model
autolayer(model_sma, series = "Fitted Values") # fitted value data train4. Evaluation
# model additive
accuracy(model_sma, train_dautonse)#> ME RMSE MAE MPE MAPE ACF1 Theil's U
#> Test set -7.131075 38.49832 30.63705 -2.526263 7.435305 0.02793552 0.6443543
# evaluasi data test
accuracy(forecast_sma$mean, test_dautonse)#> ME RMSE MAE MPE MAPE ACF1 Theil's U
#> Test set -2.429805 35.46845 29.25938 -1.6535 8.718635 -0.1693064 0.6429105
Hasil forecast model Simple Moving Average dengan ordo 5 belum dapat mengikuti pola data aktualnya. Cenderung hasil prediksi masih berada ditengah kumpulam data (belum baik) meskipun selisih MAPE antara data train dan test relatif rendah dan mendekati.
Holt’s Exponential Smoothing
Metode SMA hanya mempertimbangkan n observasi di masa lampau untuk melakukan forecast baik pola trend maupun seasonal cenderung tidak tertangkap sehingga dibutuhkan metode lain yang memperhitungkan keseluruhan data di masa lampau, yaitu metode eksponensial.
Berhubung dataset kita memiliki trends dan seasonal sehingga model eksponential yang dipilih adalah Holt’s Winters Exponential. Model Holt’s Winters Exponential (Triple Exponential Smoothing) merupakan metode forecasting yang tepat digunakan untuk data yang memiliki efek trend dan seasonal.
1. Model Building
model_holt <- HoltWinters(x = train_dautonse)
model_holt#> Holt-Winters exponential smoothing with trend and additive seasonal component.
#>
#> Call:
#> HoltWinters(x = train_dautonse)
#>
#> Smoothing parameters:
#> alpha: 0.2283017
#> beta : 0.007234357
#> gamma: 1
#>
#> Coefficients:
#> [,1]
#> a 363.171163
#> b -3.785401
#> s1 -102.778660
#> s2 -35.887200
#> s3 47.149162
#> s4 12.797192
#> s5 37.324159
#> s6 9.540481
#> s7 -5.618603
#> s8 15.832135
#> s9 24.632602
#> s10 -33.029591
#> s11 -35.162948
#> s12 -1.112163
2. Forecasting
forecast_holt <- forecast(object = model_holt, h = 18)
forecast_holt#> Point Forecast Lo 80 Hi 80 Lo 95 Hi 95
#> Jan 2018 256.6071 233.2549 279.9593 220.8929 292.3213
#> Feb 2018 319.7132 295.7515 343.6749 283.0669 356.3594
#> Mar 2018 398.9641 374.3996 423.5286 361.3960 436.5323
#> Apr 2018 360.8268 335.6656 385.9879 322.3460 399.3075
#> May 2018 381.5683 355.8161 407.3205 342.1837 420.9530
#> Jun 2018 349.9992 323.6612 376.3373 309.7186 390.2798
#> Jul 2018 331.0548 304.1356 357.9739 289.8855 372.2240
#> Aug 2018 348.7201 321.2244 376.2158 306.6691 390.7711
#> Sep 2018 353.7352 325.6670 381.8033 310.8086 396.6617
#> Oct 2018 292.2876 263.6508 320.9244 248.4913 336.0838
#> Nov 2018 286.3688 257.1669 315.5707 241.7083 331.0293
#> Dec 2018 316.6342 286.8705 346.3979 271.1145 362.1539
#> Jan 2019 211.1823 173.0636 249.3010 152.8848 269.4798
#> Feb 2019 274.2884 235.7260 312.8507 215.3123 333.2644
#> Mar 2019 353.5393 314.5325 392.5461 293.8836 413.1950
#> Apr 2019 315.4019 275.9500 354.8539 255.0654 375.7385
#> May 2019 336.1435 296.2456 376.0414 275.1250 397.1620
#> Jun 2019 304.5744 264.2299 344.9190 242.8727 366.2761
- Point Forecast: nilai forecast untuk periode yang ingin kita forecast
- Lo 80 & Hi 80 : rentang tebakan dari hasil forecast untuk confidence level 80%
- Lo 95 & Hi 95 : rentang tebakan dari hasil forecast untuk confidence level 95%
3. Visualization
train_dautonse %>%
autoplot() +
autolayer(test_dautonse, series = "test") + # data test
autolayer(forecast_holt$mean, series = "forecast data test") + # hasil forecast model
autolayer(model_holt$fitted[,1], series = "fitted values") # fitted value data train4. Evaluation
# model additive
accuracy(model_holt$fitted[,1], train_dautonse)#> ME RMSE MAE MPE MAPE ACF1 Theil's U
#> Test set -3.580492 18.19397 14.62881 -0.8975071 3.658825 -0.07230681 0.3786306
# evaluasi data test
accuracy(forecast_holt$mean, test_dautonse)#> ME RMSE MAE MPE MAPE ACF1 Theil's U
#> Test set 11.11905 25.78153 22.05057 3.465379 6.868374 0.06610927 0.4726422
Hasil forecast model Holt’s Exponential Smoothing sudah dapat mengikuti pola data aktualnya. Cenderung hasil prediksi sudah mengikuti pola data meskipun masih terdapat beberapa titik yang belum mendekati data aktual. Selain itu, ukuran MAPE yang dihasilkan sudah baik, train (3.65%) dan test (5.88%) serta selisih keduanya sekitar 2% alias tidak underfitting dan overfitting.
Seasonal ARIMA
ARIMA (Autoregressive Integrated Moving Average), ARIMA adalah gabungan antara dua metode, yaitu Auto Regressive (AR) dan Moving Average (MA). I nya menjelaskan Integrated. Tujuan utama dari ARIMA adalah melakukan autocorrelation pada data.. Seasonal arima adalah metode arima dimana object time series yang ada memiliki pola seasonal.
1. Stationarity Check
adf.test(train_dautonse)#>
#> Augmented Dickey-Fuller Test
#>
#> data: train_dautonse
#> Dickey-Fuller = -3.42, Lag order = 3, p-value = 0.07034
#> alternative hypothesis: stationary
karena p-value < alpha, dimana 0.040 < 0.05 artinya data sudah stasioner.
2. Model Building
membuat model SARIMA menggunakan auto.arima() SARIMA(p,d,q)(P,D,Q)[seasonal/freq]
model_auto_sarima <- auto.arima(train_dautonse, seasonal = T)
model_auto_sarima#> Series: train_dautonse
#> ARIMA(0,0,0)(0,1,0)[12] with drift
#>
#> Coefficients:
#> drift
#> -3.4797
#> s.e. 0.3872
#>
#> sigma^2 = 540.9: log likelihood = -109.06
#> AIC=222.12 AICc=222.69 BIC=224.48
model_auto_sarima$aic#> [1] 222.1198
3. Forecasting
# forecast, h = 18
length(test_dautonse)#> [1] 18
forecast_sarima <- forecast(model_auto_sarima, h=18)4. Visualization
# menggunakan autoplot dan autolayer
train_dautonse %>%
autoplot()+
autolayer(test_dautonse)+
autolayer(forecast_sarima$mean)5. Evaluation
menggunakan accuracy() dari package forecast dan melihat nilai MAPEnya
# accuracy
accuracy(model_auto_sarima$fitted, train_dautonse) # error train#> ME RMSE MAE MPE MAPE ACF1 Theil's U
#> Test set 0.1629594 18.58972 12.34508 -0.008536156 3.052588 0.0431331 0.3643444
accuracy(forecast_sarima$mean, test_dautonse) # error test#> ME RMSE MAE MPE MAPE ACF1 Theil's U
#> Test set 1.853778 22.78772 17.60102 0.6124365 5.520045 0.04267141 0.409262
Hasil forecast model Seasonal ARIMA sudah dapat mengikuti pola data aktualnya. Cenderung hasil prediksi sudah mengikuti pola data meskipun masih terdapat beberapa titik yang belum mendekati data aktual. Selain itu, ukuran MAPE yang dihasilkan sudah lebih baik, train (3.05%) dan test (5.52%) serta selisih keduanya sekitar 2% alias tidak underfitting dan overfitting.
STLM
Seasonal Trend with Loess Model (STLM). Apabila dalam decompose biasa, dalam mendapatkan komponen trend dengan cara central moving average (CMA) dimana secara konsep setiap data yang ingin dirata-ratakan diberikan bobot yang sama sesuai ordo yang ditetapkan. Karena merata-ratakan data tengahnya hasilnya kita kehilangan data awal dan data akhirnya, sehingga ada beberapa informasi yang hilang. Ada salah satu cara untuk mendapatkan decompose data namun tetap mempertahankan informasi dari seluruh data yang kita miliki yaitu dengan menggunakan STL(Seasonal Trend with Loess). STL secara konsep akan melakukan smoothing terhadap data tetangga setiap masing-masing observasi dengan memberikan bobot yang lebih berat terhadap data yang dekat dengan observed data. Kekurangan dari STL hanya bisa melakukan decompose pada additive data, apabila terdapat multiplicative data dapat menggunakan transformasi log()[^12].
Untuk memodelkan hasil STL, kita bisa menerapkan STLM(Seasonal Trend with Loess Model) dimana kita bisa menerapkan metode exponential smoothing (ETS) dan ARIMA. Selain itu, STLM dapat digunakan sebagai alternative cara untuk menangkap seasonal yang belum bisa ditangkap oleh metode ETS dan ARIMA biasa.
1. Model Building
Dengan menggunakan informasi data train sales_train dan test sales_test, kita akan coba untuk membuat model dengan menggunakan metode stlm() dengan mengatur parameter method = “ets”.
Parameter stlm() yang harus di atur:
- y : object time series
- s.window : pola seasonal yang ingin ditangkap
- method : metode forecast yang akan digunakan, tersedia ets dan arima
- modeling
model_stlm <- stlm(y = train_dautonse, s.window = 12, method = "ets")2. Forecasting
Melakukan forecast untuk 18 bulan mendatang
forecast_stlm <- forecast(model_stlm, h = 18)3. Visualization
ts_dautonsa %>% autoplot(series = "actual") +
autolayer(test_dautonse, series = "test data") +
autolayer(forecast_stlm$mean, series = "forecast") +
scale_color_manual(values = c("black", "blue", "firebrick"))4. Evaluation
accuracy(model_stlm$fitted, train_dautonse) # error train#> ME RMSE MAE MPE MAPE ACF1 Theil's U
#> Test set -0.854042 15.3851 11.42279 -0.2259621 2.695135 0.003772181 0.2841413
accuracy(forecast_stlm$mean, test_dautonse)#> ME RMSE MAE MPE MAPE ACF1 Theil's U
#> Test set -16.43207 26.5702 21.59139 -4.906387 6.676358 0.2433919 0.4982757
Hasil forecast model STLM (Seasonal Trend with Loess Model) sudah dapat mengikuti pola data aktualnya. Cenderung hasil prediksi sudah mengikuti pola data meskipun masih terdapat beberapa titik yang belum mendekati data aktual. Selain itu, ukuran MAPE yang dihasilkan sudah lebih baik, train (2.69%) dan test (6.67%) serta selisih keduanya sekitar 4% alias tidak underfitting dan overfitting.
Choose The Best Model
Pada kesempatan ini kita fokus membandingkan model dengan metrik Mean Absolute Percentage Error (MAPE), alasannya relatif mudah dibandingkan karena satuannya persen. Kita mesti mempertimbangkan nilai MAPE ketika train dan test serta selisih keduanya.
data.frame(
Model = c("Model Simple Moving Average",
"Holt’s Exponential Smoothing",
"Seasonal ARIMA",
"STLM"),
Train_MAPE = c(7.43, 3.65, 3.05, 2.69),
Test_MAPE = c(8.71, 5.88, 5.52, 6.67),
Selisih = c(8.71-7.43, 5.88-3.65, 5.52-3.05, 6.67-2.69)
)Kalau kita lihat cenderung model
model_auto_sarimamemiliki MAPE train (3.05%) dan test (5.52%) relatif kecil dan selisih keduanya cukup baik. Jadi kita memutuskan untuk menggunakan algoritma SARIMA yaitu modelmodel_auto_sarima.
Assumption
Asumsi pada time series diujikan untuk mengukur apakah residual yang peroleh dari hasil modeling sudah cukup baik untuk menggambarkan dan menangkap informasi pada data. Mengapa menggunakan residual data? Karena dengan menggunakan residual data, kita dapat mendapatkan informasi dari data aktual maupun dari hasil prediksi menggunakan model. Metode forecasting yang baik menghasilkan nilai residual berikut ini:
Residual yang tidak berkorelasi. Apabila terdapat residual yang berkorelasi, artinya masih terdapat informasi yang tertinggal yang seharusnya digunakan untuk menghitung hasil forecast. Residual memiliki rata-rata 0. Untuk memastikan bahwa residual yang dihasilkan memiliki kriteria diatas, maka terdapat asumsi untuk mengujinya.
1. No-autocorrelation Residual
Untuk mengecek ada/tidaknya autokorelasi pada hasil forecasting time series bisa menggunakan :
uji Ljung-box dengan menggunakan fungsi Box.test(residual model, type = “Ljung-Box)
H0: residual has no-autocorrelation H1: residual has autocorrelation
yang diinginkan p-value > 0.05 (alpha), no-autocorrelation
# menggunakan Ljung-Box test
Box.test(model_auto_sarima$residuals, type = "Ljung-Box")#>
#> Box-Ljung test
#>
#> data: model_auto_sarima$residuals
#> X-squared = 0.072718, df = 1, p-value = 0.7874
p-value > 0.05 (alpha) artinya gagal tolak H0, artinya residual/error pada data tidak terdapat autocorrelation.
2. Normality of Residual
H0: residual menyebar normal H1: residual tidak menyebar normal
yang diinginkan p-value > 0.05 (alpha), residual menyebar normal
Untuk mengecek normality residual pada hasil forecasting time series kita bisa melakukan uji normality (shapiro test) dengan menggunakan fungsi shapiro.test(residual model)
shapiro.test(model_auto_sarima$residuals)#>
#> Shapiro-Wilk normality test
#>
#> data: model_auto_sarima$residuals
#> W = 0.93241, p-value = 0.02964
hist(model_auto_sarima$residuals, xlab = "Residuals", main = "Residual Model SARIMA")p-value < alpha artinya tolak H0, artinya residual/error pada data tidak menyebar normal.
Conclusion
Kita telah melalui berbagai proses dalam pembuatan Model Forecasting yang dapat memperkirakan berapa nilai penjualan dimasa mendatang. Proses ini meliputi pendahuluan, persiapan dataset, persiapan model time series, mencoba 4 model time series, mengevaluasinya, mengecek asumsi hingga memutuskan model Seasonal ARIMA sebagai model forecasting kita. Harapannya melalui projek ini dapat menjadi referensi dalam membangun Machine Learning untuk melakukan forecasting sehingga dapat menjadi dasar dalam pengambilan keputusan secara efektif dan efisien. Sekian terima kasih banyak yang telah melihat projek ini. Saya juga menyukai masukan dan kolaborasi sehingga apabila ada masukan, saran, kritik atau untuk berkolaborasi dapat menghubungi lewat Linkedin.