September 27, 2026
Forecasting Harga dengan Exponential Smoothing.
Dasar time series, perbedaan SES, Holt (DES), dan Holt-Winters, cara memilih parameter, metrik evaluasi, sampai pipeline data dari API SISKAPERBAPO ke Streamlit — dirangkum dari skripsi prediksi harga beras di Jawa Timur.
Catatan ini merangkum apa yang aku pelajari selama mengerjakan skripsi S1 di UDINUS: Implementasi Metode Eksponensial Smoothing untuk Prediksi Harga Beras di Jawa Timur. Skripsinya masih dalam proses — naskahnya masih berupa draf revisi (abstrak, beberapa tabel statistik, dan tanggal sidang masih kosong), jadi angka di catatan ini hanya yang sudah benar-benar tertulis di draf dan di kode.
Cerita perjalanan kuliahnya ada di Perjalanan Akademik , sedangkan ringkasan project-nya di Price Prediction System .
Dasar Time Series
Time series (deret waktu) adalah data yang dicatat berurutan pada interval waktu tertentu — misalnya harga beras per hari di satu kota. Yang membedakannya dari data tabular biasa: urutan itu penting. Nilai bulan ini hampir selalu berhubungan dengan nilai bulan lalu.
Deret waktu biasanya dibaca sebagai gabungan beberapa komponen:
- Level — “tingkat dasar” nilainya saat ini.
- Tren — kecenderungan naik atau turun dalam jangka panjang.
- Musiman (seasonality) — pola yang berulang dengan periode tetap, misalnya tiap 12 bulan.
- Noise — fluktuasi acak yang tidak bisa diprediksi.
Pemilihan metode forecasting pada dasarnya adalah pertanyaan: komponen mana yang ada di data? Di sinilah keluarga Exponential Smoothing jadi menarik, karena tiap variannya menangani kombinasi komponen yang berbeda.
Ide Dasar Exponential Smoothing
Exponential Smoothing memberi bobot yang menurun secara eksponensial ke observasi masa lalu: data terbaru berpengaruh paling besar, data lama makin lama makin kecil pengaruhnya. Dibanding rata-rata bergerak biasa (semua data dalam jendela berbobot sama), pendekatan ini lebih responsif terhadap perubahan terbaru tanpa membuang sejarah sepenuhnya.
Ada tiga varian utama:
| Metode | Komponen | Parameter | Cocok untuk |
|---|---|---|---|
| Single Exponential Smoothing (SES) | Level | α | Data yang berfluktuasi di sekitar rata-rata, tanpa tren jelas |
| Double Exponential Smoothing (Holt / DES) | Level + tren | α, β | Data dengan tren naik/turun, tanpa musiman kuat |
| Triple Exponential Smoothing (Holt-Winters / TES) | Level + tren + musiman | α, β, γ | Data dengan tren dan pola musiman berulang |
Skripsiku hanya membandingkan SES dan DES, dengan alasan harga beras dianggap punya tren sederhana tanpa pola musiman yang kuat. Holt-Winters dibahas di landasan teori dan masuk saran penelitian selanjutnya.
Single Exponential Smoothing (SES)
Rumus yang dipakai di skripsi:
S(t+1) = α · X(t) + (1 − α) · S(t)
S(t) = nilai smoothing (ramalan) periode t
X(t) = data aktual periode t
α = konstanta smoothing, 0 < α < 1
Artinya, ramalan berikutnya adalah campuran antara data aktual terakhir dan ramalan sebelumnya.
- α mendekati 1 → model sangat peka ke data terbaru, cepat bereaksi tapi ikut “goyang” oleh noise.
- α mendekati 0 → model halus dan stabil, tapi lambat merespons perubahan mendadak.
Kelemahan utamanya: ramalan SES untuk beberapa periode ke depan selalu datar (satu nilai yang sama). Kalau datanya punya tren, ramalannya akan terus tertinggal di belakang data aktual.
Double Exponential Smoothing (Holt)
Holt menambahkan komponen tren ke SES. Ada dua persamaan smoothing plus satu persamaan ramalan:
Level : L(t) = α · X(t) + (1 − α) · (L(t−1) + T(t−1))
Tren : T(t) = β · (L(t) − L(t−1)) + (1 − β) · T(t−1)
Ramal : F(t+m) = L(t) + m · T(t)
β = konstanta smoothing tren, 0 < β < 1
m = jumlah periode ke depan yang diramal
- α mengatur seberapa cepat level mengikuti data baru.
- β mengatur seberapa cepat model menyesuaikan diri kalau arah tren berubah. β besar = cepat berbalik arah tapi sensitif; β kecil = tren lebih halus dan jangka panjang.
Karena ramalannya L + m·T, DES bisa “melanjutkan” kemiringan data — tidak datar seperti SES. Konsekuensinya, untuk horizon yang panjang, tren bisa terekstrapolasi terlalu jauh.
Triple Exponential Smoothing (Holt-Winters)
Holt-Winters menambahkan komponen musiman dengan parameter ketiga, γ. Versi aditifnya:
Level : L(t) = α · (X(t) − S(t−s)) + (1 − α) · (L(t−1) + T(t−1))
Tren : T(t) = β · (L(t) − L(t−1)) + (1 − β) · T(t−1)
Musiman : S(t) = γ · (X(t) − L(t)) + (1 − γ) · S(t−s)
Ramal : F(t+m) = L(t) + m · T(t) + S(t−s+m)
s = panjang satu siklus musiman (misalnya 12 untuk data bulanan)
Ada dua bentuk: aditif (amplitudo musiman tetap) dan multiplikatif (amplitudo musiman ikut membesar bersama level). Kalau datanya tidak benar-benar punya pola musiman, TES cenderung terlalu kompleks dan tidak memberi keuntungan dibanding model yang lebih sederhana — dan untuk mengestimasi pola musiman dengan layak, dibutuhkan data yang mencakup beberapa siklus penuh.
Memilih Parameter
Ada dua cara yang aku temui:
1. Nilai tetap untuk perhitungan manual. Di skripsi, contoh perhitungan manual memakai α = 0,5 untuk SES dan α = β = 0,5 untuk DES, dengan nilai awal diambil dari data aktual bulan pertama. Tujuannya menunjukkan cara kerja rumus langkah demi langkah, bukan mencari parameter terbaik.
2. Optimasi otomatis dengan statsmodels. Di kode, α dan β tidak ditentukan manual — fit() mencari nilai yang meminimalkan error pada data training, dan initialization_method="estimated" membuat level/tren awal ikut diestimasi:
from statsmodels.tsa.holtwinters import SimpleExpSmoothing, Holt
def fit_ses(train_series, forecast_steps: int):
if train_series.index.freq is None:
train_series = train_series.asfreq("MS") # frekuensi bulanan
fit = SimpleExpSmoothing(train_series, initialization_method="estimated").fit()
return fit, fit.forecast(forecast_steps)
def fit_des(train_series, forecast_steps: int):
if train_series.index.freq is None:
train_series = train_series.asfreq("MS")
fit = Holt(train_series, initialization_method="estimated").fit()
return fit, fit.forecast(forecast_steps)
Parameter hasil optimasi bisa dilihat di fit.params (misalnya smoothing_level dan smoothing_trend).
Setelah SES dan DES sama-sama dilatih, pemilihan modelnya otomatis: keduanya diuji ke data test, lalu model dengan metrik terendah (default MAPE, bisa diganti MAE atau RMSE) yang dipilih.
if ses_score <= des_score:
return {"model": "SES", ...}
else:
return {"model": "DES", ...}
Metrik Evaluasi
Tiga metrik yang dihitung di kode (skripsi fokus ke MAPE dan RMSE):
MAE = (1/n) · Σ |X(t) − F(t)|
MAPE = (100% / n) · Σ |(X(t) − F(t)) / X(t)|
RMSE = √[ (1/n) · Σ (X(t) − F(t))² ]
import numpy as np
def mae(y_true, y_pred):
return np.mean(np.abs(y_true - y_pred))
def mape(y_true, y_pred):
return np.mean(np.abs((y_true - y_pred) / y_true)) * 100
def rmse(y_true, y_pred):
return np.sqrt(np.mean((y_true - y_pred) ** 2))
- MAE — rata-rata selisih absolut, dalam satuan data asli (rupiah). Paling mudah dibaca.
- MAPE — rata-rata selisih dalam persen. Enak untuk membandingkan antar kota atau komoditas yang harganya beda jauh, tapi tidak terdefinisi kalau ada nilai aktual nol dan membengkak kalau nilai aktualnya kecil.
- RMSE — juga dalam satuan asli, tapi karena error dikuadratkan, satu-dua prediksi yang meleset jauh akan menaikkan nilainya secara signifikan.
Skripsi memakai kriteria interpretasi MAPE yang umum: di bawah 10% sangat baik, 10–20% baik, 20–50% cukup, di atas 50% buruk.
Pipeline Data
Model forecasting-nya cuma beberapa baris. Yang makan waktu justru pipeline di sekitarnya:
API SISKAPERBAPO (Pemprov Jatim)
│ request per tanggal × komoditas
Extract ─ Transform ─ Load (Python + pandas)
│ bersihkan "Rp" & titik, buang harga kosong/≤ 0
MySQL
├── history_data_komoditas (harga harian)
├── history_data_komoditas_monthly (rata-rata/min/max bulanan, agregasi SQL)
└── forecast_harga_komoditas (hasil SES/DES + MAE/MAPE/RMSE)
│
Forecast (statsmodels)
│ split berurutan → min-max scaling → SES & DES → evaluasi → pilih model
Dashboard Streamlit
Beberapa detailnya:
- Extract. Data diambil dari endpoint peta harga di
siskaperbapo.jatimprov.go.id, satu request per tanggal per komoditas. Awalnya hanya beras premium dan medium, lalu pipeline-nya aku generalisasi dengankomoditas_idsupaya bisa menampung komoditas lain. - Transform. Harga yang masih berupa teks seperti
Rp 15.000dibersihkan jadi angka, nilai kosong ditangani, dan harga tidak valid dibuang. - Load idempoten. Setiap tabel punya kunci unik
(misalnya kota + komoditas + tanggal) dan di-upsert dengan
ON DUPLICATE KEY UPDATE, jadi ETL bisa dijalankan ulang tanpa data ganda. - Agregasi bulanan. Data harian digabung jadi rata-rata per bulan langsung dengan
GROUP BYdi SQL . Ini sesuai batasan skripsi: peramalan dilakukan dalam satuan bulanan. - Split berbasis waktu. Data bulanan dipotong berurutan: bagian awal untuk training, bagian akhir untuk testing — bukan acak.
- Normalisasi min-max di-fit pada data training saja, lalu dipakai ulang untuk data test dan untuk mengembalikan hasil ramalan ke rupiah.
- Streamlit menyediakan halaman fetch data, analitik harian, agregasi bulanan, forecast (pilihan Auto/SES/DES dan slider persentase data training 60–90%, default 80%), dan perbandingan antar kota.
Hasil Sementara di Skripsi
Menurut draf skripsi, data yang dikumpulkan adalah harga harian beras medium dan premium dari 38 kabupaten/kota di Jawa Timur periode Januari 2023 – Juni 2025, lebih dari 100.000 record mentah. Setelah diagregasi, setiap kombinasi kota × jenis beras menjadi sekitar 30 titik data bulanan. Skema pembagian yang dipakai di Bab IV adalah 80% training, 20% testing — dengan 30 data bulanan, berarti 24 bulan training dan 6 bulan testing.
Hasil yang tertulis berasal dari perhitungan manual untuk beras medium di Kabupaten Malang (α = 0,5; DES dengan α = β = 0,5), dengan error dirata-rata atas 30 periode:
| Metode | MAPE | RMSE |
|---|---|---|
| SES | 1,9673% | 288,7080 |
| DES | 1,8558% | 277,6937 |
Keduanya masuk kategori “sangat baik” (MAPE di bawah 10%), dan DES sedikit lebih unggul — RMSE-nya berarti rata-rata penyimpangan sekitar ±277 rupiah, dibanding ±288 rupiah untuk SES. Kesimpulan draf skripsinya pun hati-hati: ini hasil satu kota, dan tidak ada model yang selalu unggul. Di data yang relatif stabil SES tetap kompetitif, sedangkan ketika ada tren, DES berpotensi lebih baik.
Karena naskahnya belum final, angka-angka ini masih bisa berubah setelah revisi dan sidang.
Jebakan yang Aku Temui
- Kebocoran data lewat normalisasi. Kalau min dan max dihitung dari seluruh data, informasi dari masa depan (data test) ikut “bocor” ke training. Makanya scaler hanya di-fit di data train.
- Split acak untuk time series. Mengacak data berarti model belajar dari masa depan untuk memprediksi masa lalu. Split harus berurutan.
- MAPE pada data yang sudah dinormalisasi. Setelah min-max scaling, nilai bisa mendekati 0 — atau bahkan negatif untuk data test yang lebih rendah dari minimum training. MAPE di ruang ini bisa membengkak dan tidak bisa dibaca sebagai “persen dari harga”. Untuk laporan, lebih aman menghitung MAPE setelah hasil dikembalikan ke rupiah. Ini catatan untuk diriku sendiri juga, karena skrip forecast di repo saat ini masih menghitung metrik di skala normalisasi.
- Data sedikit, test set kecil. Sekitar 30 titik bulanan berarti hanya 6 bulan untuk testing. Satu bulan yang anomali sudah cukup untuk menggeser metrik, jadi perbandingan SES vs DES per kota perlu dibaca dengan hati-hati.
- Bulan yang bolong.
asfreq("MS")akan menyisipkanNaNkalau ada bulan yang hilang, dan model akan gagal atau menghasilkanNaN. Kelengkapan data harus dicek sebelum fitting. - Agregasi menyembunyikan lonjakan. Rata-rata bulanan membuat deret lebih stabil, tapi lonjakan harga beberapa hari bisa tertutup. Kolom min/max bulanan membantu melihat apa yang hilang.
- Ramalan datar vs tren berlebihan. SES selalu menghasilkan ramalan datar, sedangkan DES bisa melanjutkan tren terlalu jauh untuk horizon panjang. Semakin jauh ke depan, semakin lebar ketidakpastiannya.
- Model univariat hanya melihat harganya sendiri. Faktor seperti produksi padi, cuaca, atau distribusi tidak masuk ke model. Draf skripsi juga menyebut ini sebagai saran penelitian selanjutnya, bersama Holt-Winters, ARIMA, atau LSTM sebagai pembanding.
Kesimpulan
Exponential Smoothing itu sederhana: satu sampai tiga parameter, rumusnya bisa dihitung manual di spreadsheet, dan statsmodels menyediakan implementasi yang siap pakai. Justru karena modelnya sederhana, kualitas hasil sangat ditentukan oleh hal-hal di sekitarnya — data yang bersih, split yang benar, normalisasi yang disiplin, dan metrik yang dihitung di skala yang tepat.
Kalau skripsinya sudah selesai, catatan ini akan aku perbarui dengan hasil akhirnya.

Hey! I’m Fanny, the software engineer tending to this digital garden. You can read more about me, or subscribe by email.