Dekomposisi Time Series: Tren, Musiman, dan Sisa pada Data Penjualan
TL;DR
Dekomposisi time series misahin satu deret angka jadi tiga bagian: tren (arah jangka panjang), musiman (pola yang berulang di periode tetap), dan sisa (yang gak kejelasin dua-duanya). Model aditif dipakai kalau ayunan musimannya tetap dalam satuan rupiah, model multiplikatif kalau ayunannya membesar seiring naiknya level penjualan. Di Python, dua baris pakai seasonal_decompose dari statsmodels udah cukup buat lihat ketiganya.
Dekomposisi time series adalah cara misahin satu deret angka berurut waktu jadi tiga bagian: tren, pola musiman, dan sisa yang gak kejelasin dua-duanya.
Gunanya buat jawab pertanyaan yang sering bikin ribut di rapat. Penjualan bulan ini naik 12 persen, itu beneran tumbuh atau cuma karena bulan ini ada dua tanggal gajian?
Di sini aku tunjukin cara misahin ketiganya pakai Python, cara milih model yang benar, dan gimana tiap komponen dipakai buat keputusan.
Apa itu dekomposisi time series?
Dekomposisi time series memecah satu deret waktu jadi komponen yang bisa ditafsirkan terpisah. Tren nunjukin arah jangka panjang, musiman nunjukin pola yang berulang di jarak tetap, dan sisa nampung sesuatu yang gak masuk dua kategori itu. Dengan misahin ketiganya, kamu bisa lihat pertumbuhan asli tanpa terganggu ayunan musiman.
Ada dua bentuk rumus yang dipakai:
Aditif : Y = Tren + Musiman + Sisa
Multiplikatif : Y = Tren x Musiman x Sisa
Bedanya di cara komponen musimannya diukur. Aditif nganggap efek Sabtu itu tambahan Rp 1,2 juta, berapa pun besar penjualan hariannya.
Multiplikatif nganggap efek Sabtu itu 1,34 kali hari biasa. Kalau penjualan tumbuh, tambahan rupiahnya ikut membesar.
| Ciri data | Model yang cocok |
|---|---|
| Lebar ayunan musiman tetap dari awal sampai akhir | Aditif |
| Ayunan makin lebar seiring level naik | Multiplikatif |
| Ada nilai nol atau negatif | Aditif (multiplikatif gak bisa) |
| Penjualan tumbuh stabil bertahun-tahun | Multiplikatif |
Gimana nyiapin data sebelum didekomposisi?
Dekomposisi minta deret yang rapat dan berjarak sama. Satu tanggal bolong bikin perhitungan periodenya meleset, dan hasilnya kelihatan acak padahal datanya sebenernya berpola.
Empat langkah persiapan yang wajib:
- Agregasi transaksi jadi satu baris per tanggal
- Ubah kolom tanggal jadi tipe datetime lalu jadiin index
- Isi tanggal yang bolong, pakai nol kalau toko tutup atau interpolasi kalau datanya emang hilang
- Tentukan panjang satu putaran musiman
import pandas as pd
df = pd.read_csv("toko_berkah_transaksi.csv", parse_dates=["tanggal"])
harian = (
df.groupby("tanggal")["nominal"]
.sum()
.sort_index()
)
# rapatkan deret, tanggal yang gak ada transaksi diisi nol
harian = harian.asfreq("D", fill_value=0)
print(harian.head())
print(f"Jumlah hari: {len(harian)}")
Metode asfreq("D") bikin index tanggalnya berurut tiap hari tanpa lompatan. Dokumentasi lengkap soal frekuensi dan penanganan deret waktu di pandas ada di panduan time series pandas.
Pengisian nol cuma benar kalau toko emang tutup. Kalau datanya hilang gara-gara sistem error, pakai interpolate(), soalnya nol palsu bakal narik tren turun.
Gimana cara dekomposisi time series di Python?
Fungsi seasonal_decompose dari statsmodels ngerjain semuanya dalam satu panggilan. Kamu cuma perlu nentuin model dan panjang periodenya.
from statsmodels.tsa.seasonal import seasonal_decompose
import matplotlib.pyplot as plt
hasil = seasonal_decompose(harian, model="additive", period=7)
fig = hasil.plot()
fig.set_size_inches(11, 8)
plt.tight_layout()
plt.show()
# tiap komponen bisa diambil terpisah
tren = hasil.trend
musiman = hasil.seasonal
sisa = hasil.resid
Argumen period=7 artinya satu putaran musiman panjangnya 7 titik data. Buat data harian dengan pola mingguan, ini angkanya.
Buat data bulanan dengan pola tahunan, isi 12. Buat data harian dengan pola tahunan, isi 365, tapi kamu butuh minimal dua tahun data.
Kenapa ujung garis trennya kosong
Tren dihitung pakai rata-rata bergerak terpusat sepanjang satu periode. Buat period 7, dia butuh 3 titik sebelum dan 3 titik sesudah.
Jadi 3 hari pertama dan 3 hari terakhir gak punya nilai tren. Ini normal, bukan tanda ada yang salah.
Cara baca komponen musimannya
import numpy as np
pola = musiman.groupby(musiman.index.dayofweek).mean()
pola.index = ["Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu", "Minggu"]
print(pola.round(0))
Di model aditif, angka positif artinya hari itu biasanya di atas rata-rata sebesar sekian rupiah. Di model multiplikatif, angka 1,34 artinya hari itu biasanya 34 persen di atas hari normal.
Kapan pakai STL dibanding seasonal_decompose?
STL adalah metode dekomposisi yang pakai penghalusan bertahap, jadi pola musimannya boleh berubah pelan-pelan sepanjang waktu. Dia juga lebih tahan terhadap satu dua nilai ekstrem kalau opsi robust diaktifkan. Buat data penjualan Indonesia yang polanya bergeser tiap Ramadan, STL biasanya kasih hasil yang lebih masuk akal.
from statsmodels.tsa.seasonal import STL
stl = STL(harian, period=7, robust=True)
hasil_stl = stl.fit()
fig = hasil_stl.plot()
fig.set_size_inches(11, 8)
plt.show()
Opsi robust=True ngurangin pengaruh hari-hari ekstrem kayak H-1 Lebaran. Tanpa itu, satu hari dengan penjualan tiga kali lipat bisa ngerusak estimasi pola musiman buat seluruh deret.
| Aspek | seasonal_decompose | STL |
|---|---|---|
| Pola musiman | Dianggap tetap sepanjang waktu | Boleh berubah bertahap |
| Tahan nilai ekstrem | Gak | Ya, kalau robust=True |
| Ujung deret | Kosong sepanjang setengah periode | Terisi penuh |
| Kecepatan | Cepat banget | Sedikit lebih lambat |
| Model multiplikatif | Tersedia lewat argumen model | Perlu log dulu, hasilnya di-eksponen balik |
Buat model multiplikatif di STL, ubah datanya jadi log dulu. Setelah dekomposisi, hasil aditif di ruang log setara dengan multiplikatif di ruang asli.
harian_log = np.log(harian.replace(0, np.nan)).interpolate()
stl_log = STL(harian_log, period=7, robust=True).fit()
musiman_faktor = np.exp(stl_log.seasonal) # 1,34 artinya 34% di atas normal
Contoh kasus: penjualan harian toko_berkah 14 bulan
Data ngulikdata untuk toko_berkah punya 11.407 transaksi dalam 421 hari operasional. Setelah diagregasi jadi total harian dan didekomposisi pakai STL dengan period 7 dan model multiplikatif, hasilnya begini.
Komponen musiman mingguan:
| Hari | Faktor musiman | Artinya |
|---|---|---|
| Senin | 0,94 | 6% di bawah hari normal |
| Selasa | 0,78 | 22% di bawah, hari paling sepi |
| Rabu | 0,86 | 14% di bawah |
| Kamis | 0,95 | 5% di bawah |
| Jumat | 1,09 | 9% di atas |
| Sabtu | 1,34 | 34% di atas, hari paling ramai |
| Minggu | 1,04 | 4% di atas |
Selisih Sabtu lawan Selasa itu 1,72 kali lipat. Pemilik toko sebelumnya nempatin jumlah pegawai yang sama tiap hari.
Komponen tren: naik dari sekitar Rp 7,1 juta per hari di bulan pertama ke Rp 8,6 juta di bulan ke-14. Pertumbuhan bersih 21 persen dalam 14 bulan, atau sekitar 1,4 persen per bulan.
Angka pertumbuhan itu jauh lebih kecil dari yang dirasain pemiliknya. Dia ngira tokonya tumbuh 40 persen, soalnya dia bandingin bulan Ramadan sama bulan biasa.
Komponen sisa: simpangan bakunya Rp 610.000, atau sekitar 7,6 persen dari rata-rata harian. Ada 9 hari dengan sisa lebih dari 3 kali simpangan baku, dan 7 di antaranya jatuh di sekitar libur nasional.
Sisa inilah yang paling berguna buat pemantauan otomatis. Setelah tren dan musiman dikeluarkan, yang tersisa emang layak ditanyain.
Tiap komponen dipakai buat keputusan apa?
| Komponen | Pertanyaan yang dijawab | Keputusan yang dipengaruhi |
|---|---|---|
| Tren | Bisnisnya tumbuh atau nggak | Target tahunan, keputusan buka cabang |
| Musiman | Kapan ramai dan kapan sepi | Jadwal shift, jadwal restok, waktu promo |
| Sisa | Apa ada yang aneh hari ini | Peringatan otomatis, investigasi |
Buat toko_berkah, komponen musiman langsung jadi tiga perubahan: tambah satu kasir tiap Sabtu, kirim promo tiap Selasa pagi, dan geser jadwal restok dari Sabtu ke Rabu.
Sisa dipakai buat aturan peringatan yang sama polanya kayak deteksi anomali penjualan dengan SQL, cuma di sini baseline-nya udah bersih dari efek hari dalam seminggu.
Kesalahan umum waktu dekomposisi time series
Salah isi period. Ini penyebab nomor satu hasil yang kelihatan acak. Data harian dengan pola mingguan pakai 7, bukan 30 atau 365.
Tanggal bolong dibiarkan. Kalau deretnya gak rapat, posisi hari dalam seminggu jadi geser dan pola musimannya ancur. Selalu jalanin asfreq dulu.
Pakai aditif buat data yang tumbuh cepat. Kalau penjualan naik dua kali lipat dalam setahun, efek Sabtu dalam rupiah juga ikut membesar. Model aditif bakal ninggalin pola sisa yang berbentuk gelombang.
Nafsirin sisa sebagai kesalahan. Sisa bukan berarti data salah. Dia nampung semuanya yang gak dijelasin tren dan musiman, termasuk promo, cuaca, dan kejadian sekali seumur hidup.
Cuma pasang satu periode musiman. Penjualan ritel Indonesia punya pola mingguan sekaligus pola tahunan Ramadan. Satu dekomposisi cuma nangkep satu periode, jadi jalanin dua kali atau pakai MSTL kalau butuh dua-duanya.
Bandingin bulan Ramadan sama bulan biasa. Kesalahan paling mahal dan paling sering. Bandingin Ramadan tahun ini sama Ramadan tahun lalu, atau pakai komponen tren yang udah bersih dari efek musiman.
FAQ
Kapan pakai model aditif dan kapan multiplikatif?
Lihat bentuk grafiknya. Kalau selisih puncak dan lembah musiman kelihatan sama lebarnya dari awal sampai akhir, pakai aditif. Kalau ayunannya makin lebar seiring penjualan naik, pakai multiplikatif. Data penjualan yang tumbuh biasanya multiplikatif. Cara cepat lainnya: coba dua-duanya, lalu pilih yang sisanya paling gak berpola.
Berapa nilai period yang harus aku isi?
Period itu jumlah titik data dalam satu putaran musiman. Data harian dengan pola mingguan pakai 7. Data bulanan dengan pola tahunan pakai 12. Data harian dengan pola tahunan pakai 365, tapi itu butuh minimal dua tahun data. Kalau kamu isi angka yang salah, komponen musimannya bakal kelihatan acak dan gak berulang rapi.
Kenapa awal dan akhir garis tren hasil dekomposisi kosong?
Karena tren dihitung pakai rata-rata bergerak terpusat sepanjang satu periode. Buat period 7, dia butuh 3 titik sebelum dan 3 titik sesudah, jadi 3 titik pertama dan terakhir gak punya nilai. Ini normal dan bukan error. Kalau kamu butuh tren sampai ujung, pakai STL yang ngisi bagian tepinya.
Apa bedanya seasonal_decompose dan STL?
seasonal_decompose pakai rata-rata bergerak sederhana dan nganggap pola musimannya tetap sama sepanjang waktu. STL pakai penghalusan bertahap, jadi pola musimannya boleh berubah pelan-pelan dan dia lebih tahan terhadap satu dua nilai ekstrem. Buat data penjualan Indonesia yang polanya bergeser tiap Ramadan, STL biasanya kasih hasil yang lebih masuk akal.
Dekomposisi bisa dipakai buat forecasting langsung?
Gak langsung, soalnya dia cuma misahin data yang udah ada dan gak bikin ramalan ke depan. Tapi hasilnya jadi bahan bagus buat forecasting. Kamu bisa ramal komponen trennya pakai regresi sederhana, lalu tempelin lagi pola musiman yang udah ketahuan. Model kayak Holt-Winters ngerjain dua langkah itu sekaligus.
Penutup
Tiga hal yang perlu kamu bawa.
Rapatkan deret waktunya dulu sebelum apa pun. Tanggal bolong itu penyebab paling sering hasil dekomposisi kelihatan acak.
Pilih model dari bentuk ayunan musimannya. Data penjualan yang tumbuh hampir selalu multiplikatif.
Tiap komponen punya pemakainya sendiri. Tren buat target, musiman buat jadwal, sisa buat peringatan.
Coba jalanin dekomposisi di data penjualan 6 bulan terakhirmu, lalu lihat faktor musiman per hari dalam seminggu. Selisih hari paling ramai dan paling sepi biasanya lebih besar dari dugaan.
Kalau kamu mau segmentasi pelanggan setelah pola waktunya ketahuan, lanjut ke cara menentukan jumlah cluster. Istilah outlier dan metric yang dipakai di sini juga ada penjelasan singkatnya di glossary.
Mau praktek langsung? Mulai latihan SQL gratis
Latihan interaktif, langsung di browser.
Artikel terkait
Pandas groupby: Agregasi Data ala Pivot di Python (2026)
groupby ngelompokin baris berdasarkan kolom, lalu ngitung ringkasan per grup kayak total atau rata-rata. Ini pivot-nya pandas.
Pandas to_excel: Ekspor DataFrame ke Excel (2026)
to_excel nyimpen DataFrame pandas jadi file Excel .xlsx. Ini cara pakainya, dari satu sheet sampai banyak sheet, plus setelan yang bikin hasilnya rapi.
Pandas read_excel: Baca File Excel di Python
pandas read_excel baca file Excel jadi DataFrame, bisa pilih sheet tertentu dan lompatin baris judul yang berantakan. Ini cara pakai plus parameter penting dengan contoh data toko.