Menganalisis data deret waktu: mendekomposisi tren & seasonality, meramalkan penjualan dengan Prophet dan ARIMA, serta mengevaluasi forecast dengan metrik yang tepat — dilengkapi praktik forecast penjualan lengkap

Setelah di episode 14 kita menguasai NLP, pada episode ini kita menghadapi jenis data yang paling banyak dipakai untuk keputusan bisnis: data deret waktu (time series). Penjualan bulanan, permintaan stok harian, trafik situs per jam — semuanya berurutan waktu, dan hampir semua perencanaan bisnis (stok, staffing, budget) bergantung pada peramalannya.
Mengapa time series perlu perlakuan khusus? Karena data urut waktu melanggar asumsi dasar ML biasa: observasi tidak independen. Penjualan bulan Maret sangat dipengaruhi Februari; split acak biasa akan merusak urutan temporal. Time series butuh metode sendiri — dan memahami dekomposisi tren/musiman serta metrik forecast yang benar adalah intinya.
Sebuah deret waktu bisa dipecah menjadi tiga komponen:
| Komponen | Arti | Contoh |
|---|---|---|
| Tren | Arah jangka panjang | Penjualan naik 5%/tahun |
| Seasonality | Pola berulang pada periode tetap | Lonjakan Desember, akhir pekan |
| Noise | Variasi acak yang tak dapat dijelaskan | Event tak terduga |
Mari kita mulai dengan mendekomposisi deret penjualan bulanan:
import pandas as pd
from statsmodels.tsa.seasonal import seasonal_decompose
sales = pd.read_csv("sales_monthly.csv",
parse_dates=["month"], index_col="month")
decomp = seasonal_decompose(sales["value"], model="additive", period=12)
print(decomp.trend.dropna().tail(3))
print(decomp.seasonal.head(12))seasonal_decompose memisahkan tren, musiman, dan residu. Dari output kalian bisa melihat: tren naik/turun? pola musiman tiap bulan berapa? residu besar di bulan apa? Jawaban ini langsung memberi tahu metode forecasting mana yang cocok.
Cross-validation biasa melanggar urutan waktu. Gunakan TimeSeriesSplit — setiap lipatan hanya dilatih pada data masa lalu:
from sklearn.model_selection import TimeSeriesSplit
tscv = TimeSeriesSplit(n_splits=3)
for train_idx, test_idx in tscv.split(sales):
print("train sampai:", sales.index[train_idx][-1].date(),
"| test:", sales.index[test_idx][0].date(),
"sampai", sales.index[test_idx][-1].date())Prinsipnya sederhana: model tidak boleh melihat masa depan saat dilatih. Ini konsep yang sama dengan "leakage" di episode 9, hanya versi temporalnya.
Prophet (Meta) sangat ramah pengguna: otomatis menangkap tren, musiman tahunan/mingguan/harian, dan hari libur.
pip install prophetfrom prophet import Prophet
import pandas as pd
sales = pd.read_csv("sales_monthly.csv",
parse_dates=["month"], index_col="month")
df = sales.reset_index().rename(columns={"month": "ds", "value": "y"})
model = Prophet(yearly_seasonality=True, weekly_seasonality=False)
model.fit(df)
future = model.make_future_dataframe(periods=6, freq="MS")
forecast = model.predict(future)
print(forecast[["ds", "yhat", "yhat_lower", "yhat_upper"]].tail(6))Output yhat (prediksi), yhat_lower, yhat_upper (interval ketidakpastian). Interval ini penting — bisnis perlu tahu rentang, bukan angka tunggal. freq="MS" memastikan 6 periode = 6 bulan pertama.
ARIMA (AutoRegressive Integrated Moving Average) adalah pendekatan statistik klasik. Untuk data musiman, gunakan SARIMA:
import pandas as pd
from statsmodels.tsa.statespace.sarimax import SARIMAX
sales = pd.read_csv("sales_monthly.csv",
parse_dates=["month"], index_col="month")
train = sales.iloc[:-6]
test = sales.iloc[-6:]
model = SARIMAX(train["value"],
order=(1, 1, 1),
seasonal_order=(1, 1, 1, 12),
enforce_stationarity=False,
enforce_invertibility=False)
result = model.fit(disp=False)
print(result.summary())
pred = result.forecast(steps=6)
print(pred)order dan seasonal_order menentukan struktur AR/MA — bisa diotomatiskan dengan auto_arima dari library pmdarima. Untuk series ini, intinya: SARIMA cocok untuk data stabil dan musiman; Prophet lebih fleksibel untuk tren kompleks dan liburan.
Metrik klasik (MAE, RMSE) tetap berlaku, tetapi ada dua metrik khusus forecast:
| Metrik | Arti |
|---|---|
| MAE / RMSE | Error absolut, dihitung pada test time series |
| MAPE | Error sebagai persentase — mudah dipahami bisnis |
import numpy as np
from sklearn.metrics import mean_absolute_error, mean_absolute_percentage_error
mae = mean_absolute_error(test["value"], pred)
mape = mean_absolute_percentage_error(test["value"], pred)
print(f"MAE : {mae:,.0f} unit")
print(f"MAPE: {mape:.1%}")MAPE 4% artinya rata-rata peramalan meleset 4% dari nilai aktual — bahasa yang langsung dipahami manajemen untuk menilai kualitas forecast. Bandingkan dengan baseline naif (memprediksi nilai bulan lalu): forecast hanya berguna jika mengalahkan baseline.
Note
Selalu bandingkan forecast dengan baseline naif: prediksi "nilai bulan lalu" (naive) atau "rata-rata musiman". Sering kali baseline ini sudah cukup baik — dan kalian hanya berhak mengklaim model forecast unggul jika mengalahkannya secara konsisten.
| Pitfall | Dampak | Solusi |
|---|---|---|
| Split acak pada data waktu | Leakage masa depan | TimeSeriesSplit |
| Forecast tanpa baseline | Klaim performa palsu | Bandingkan dengan naive |
| Mengabaikan seasonality | Forecast bias | Dekomposisi & periksa period |
| Hanya angka titik, tanpa interval | Keputusan salah | Laporkan interval prediksi |
| Melatih ulang di produksi jarang | Drift tak tertangkap | Refit terjadwal (ep. 24) |
Inti yang harus dibawa pulang:
TimeSeriesSplit) mencegah leakage masa depan.Di episode 16 selanjutnya kita akan membahas A/B testing & causal inference — merancang eksperimen, menguji signifikansi, dan mengukur uplift — untuk membuktikan kausalitas, bukan sekadar korelasi. Sampai jumpa di episode 16!