Data yang direkam urut waktu — suhu bulanan, penjualan harian, harga saham mingguan — menyimpan struktur yang tidak dimiliki data acak: nilai hari ini berkorelasi dengan nilai kemarin. Analisis deret waktu (time series analysis) membedah struktur itu menjadi tiga komponen: trend (arah jangka panjang), musiman (pola berulang tiap periode), dan noise (fluktuasi acak). Chapter ini membangun deret suhu sintetis 48 bulan, lalu mengurai ketiga komponennya dengan moving average, detrending regresi, seasonal index, dan autokorelasi — semuanya dari operasi dasar yang teruji di berkas contoh.m.
Anatomi Deret Waktu #
Model aditif menyatakan y(t) = trend(t) + musiman(t) + noise(t). Kita membangun data sintetis deterministik dari rumus tersebut — tanpa RNG sama sekali, sehingga setiap angka output dapat direproduksi persis:
t = 0:47; % 48 bulan (4 tahun)
noise = [ 0.3 -0.2 0.1 -0.4 0.2 0.3 -0.1 0.2 -0.3 0.1 0.2 -0.1; ...
0.2 -0.3 0.4 -0.1 0.1 -0.2 0.3 -0.2 0.1 0.4 -0.1 0.2; ...
0.1 0.2 -0.1 0.3 -0.2 0.1 0.4 -0.1 0.2 -0.3 0.1 -0.2; ...
0.2 -0.1 0.3 0.2 -0.4 0.1 0.2 -0.3 0.1 0.2 -0.2 0.3];
noise = reshape(noise', 1, []); % 48 nilai noise tetap
suhu = 20 + 0.04*t + 6*sin(2*pi*t/12) + noise; % trend + musiman + noiseTrend linier 0.04 derajat per bulan, musiman tahunan beramplitudo 6, dan noise buatan berkisar ±0.4. Keuntungan data sintetis: kebenaran dasarnya diketahui, jadi setiap estimasi dapat dinilai galatnya — latihan yang jujur sebelum menangani data nyata.
Moving Average: Trailing vs Centered #
Moving average (MA) menghaluskan noise dengan mengganti tiap titik rata-rata tetangganya. MA trailing memakai jendela [t-k+1, t] — aman untuk sistem real-time karena hanya memakai data lampau. MA terpusat (centered) memakai [t-k, t+k] — lebih akurat untuk analisis historis karena tidak tertinggal di titik belok:
ma3 = nan(1, 48);
for i = 3:48
ma3(i) = mean(suhu(i-2:i));
end
fprintf('Suhu asli (bulan 3-5): %.4f %.4f %.4f\n', suhu(3), suhu(4), suhu(5));
fprintf('MA-3 trailing (bulan 3-5): %.4f %.4f %.4f\n', ma3(3), ma3(4), ma3(5));
% MA-3 terpusat (centered): window i-1, i, i+1
ma3c = nan(1, 48);
for i = 2:47
ma3c(i) = mean(suhu(i-1:i+1));
end
fprintf('MA-3 terpusat (bulan 3-5): %.4f %.4f %.4f\n', ma3c(3), ma3c(4), ma3c(5));Output yang Dihasilkan:
Suhu asli (bulan 3-5): 25.3762 25.7200 25.5562
MA-3 trailing (bulan 3-5): 22.8387 24.6454 25.5508
MA-3 terpusat (bulan 3-5): 24.6454 25.5508 24.9254Mari kita bedah baris-baris kode di atas:
- Baris 1-4 (
ma3loop): bulan ke-3 (indeks 3) dirata-rata dari bulan 1-3;nanmenandai dua bulan awal yang jendelanya belum penuh. - Baris 9-12 (
ma3cloop): jendela bergeser simetris di sekitar titik — bandingkan 24.6454 terpusat melawan 22.8387 trailing pada bulan ke-3.
Pada puncak musim naik, MA trailing melaporkan 22.84 padahal data 25.38 — tertinggal hampir 2.5 derajat. MA terpusat memotong jarak itu dengan rata-rata simetris; itulah sebabnya analisis historis selalu memilih jendela terpusat.
Estimasi Trend dan Detrending #
Untuk data bulanan, jendela 12 bulan menghapus musiman: satu jendela selalu memuat Januari sampai Desember lengkap, sehingga komponen musiman saling meniadakan. Karena 12 genap, MA terpusat standar tidak sejajar dengan titik data; solusi klasiknya MA 2×12 — rata-rata dua MA trailing berurutan:
ma12 = conv(suhu, ones(1,12)/12, 'valid'); % MA trailing, posisi t = 5.5..41.5
cma = (ma12(1:36) + ma12(2:37)) / 2; % MA terpusat 2x12, t = 6..41
idxT = 6:41; % posisi t yang punya CMA
det = suhu(idxT+1) - cma; % data dikurangi trend
bulan = mod(idxT, 12); % 0 = Januari
sidx = zeros(1, 12);
for m = 0:11
sidx(m+1) = mean(det(bulan == m)); % rata-rata per bulan
end
fprintf('\nTrend (CMA-12): t=6: %.4f, t=24: %.4f, t=41: %.4f\n', ...
cma(1), cma(19), cma(36));
fprintf('\nSeasonal index per bulan (Jan..Des):');
fprintf(' %.3f', sidx);
fprintf('\n');Output yang Dihasilkan:
Trend (CMA-12): t=6: 20.2608, t=24: 21.0558, t=41: 21.6692
Seasonal index per bulan (Jan..Des): 0.118 2.887 5.352 6.082 4.982 2.953 0.157 -3.076 -5.243 -5.992 -5.188 -3.081Detrending adalah pengurangan biasa: det = suhu - cma menyisakan musiman plus noise karena trend sudah dibuang. Seasonal index mengambil rata-rata detrended untuk tiap bulan kalender (bulan 0 = Januari, dipetakan lewat mod(t,12)) — empat tahun memberi empat sampel per bulan. Indeks mencapai puncak +6.08 pada April dan dasar -5.99 pada Oktober, cermin setia dari amplitudo 6 yang kita tanam.
Regresi pada Data Terkoreksi Musiman #
Setelah seasonal index terbentuk, kita membangun data terkoreksi musiman y_sa = suhu - index(bulan), lalu memfit tren linier pada data bersih itu:
bulanAll = mod(t, 12);
y_sa = suhu - sidx(bulanAll+1); % musiman dihapus
pRaw = polyfit(t, suhu, 1); % regresi pada data mentah
pAdj = polyfit(t, y_sa, 1); % regresi pada data bersih
fprintf('\nSlope mentah = %.6f (bias oleh musiman)\n', pRaw(1));
fprintf('Slope terkoreksi = %.6f (trend sebenarnya 0.04)\n', pAdj(1));Output yang Dihasilkan:
Slope mentah = -0.018187 (bias oleh musiman)
Slope terkoreksi = 0.040620 (trend sebenarnya 0.04)Slope terkoreksi 0.0406 hanya meleset 1.6% dari trend sebenarnya 0.04 — sisa galat berasal dari noise dan ketidaksempurnaan estimasi index. Slope mentah bahkan salah tanda. Inilah urutan dekomposisi klasik: MA estimasi trend, residu menghasilkan seasonal index, koreksi musiman memurnikan data, regresi akhir menutup analisis.
Autokorelasi dan Stasioneritas #
Autokorelasi lag-k mengukur kemiripan deret dengan salinan dirinya yang digeser k periode: r_k = sum((y_t - m)(y_{t+k} - m)) / sum((y_t - m)^2). Deret musiman menunjukkan pola khas — korelasi tinggi pada lag kelipatan periode, negatif pada lag setengah periode:
m0 = mean(suhu); yc = suhu - m0; den = sum(yc.^2);
for lag = [1 6 12 24]
rk = sum(yc(1:48-lag) .* yc(1+lag:48)) / den;
fprintf(' lag %2d: %.4f\n', lag, rk);
end
m1 = mean(y_sa); yc1 = y_sa - m1; den1 = sum(yc1.^2);
rk12 = sum(yc1(1:36) .* yc1(13:48)) / den1;
fprintf(' lag 12 setelah koreksi musiman: %.4f\n', rk12);Output yang Dihasilkan:
lag 1: 0.8608
lag 6: -0.8856
lag 12: 0.7399
lag 24: 0.4854
lag 12 setelah koreksi musiman: 0.2212Lag 1 sebesar 0.86 menandakan bulan berdekatan sangat mirip — nilai bulan ini meramalkan nilai bulan depan. Lag 6 negatif (-0.89) karena bulan yang terpisah setengah tahun berada di sisi berlawanan siklus. Lag 12 (0.74) dan 24 (0.49) positif: pola mengulang tiap tahun. Setelah koreksi musiman, lag 12 runtuh ke 0.22 — bukti komponen musiman memang terhapus.
Deret yang mean dan variansinya konstan sepanjang waktu disebut stasioner; deret suhu kita tidak stasioner karena memuat trend dan musiman. White noise — deret tanpa autokorelasi sama sekali — adalah patokan: bila residu analisis sudah menyerupai white noise, model selesai mengekstraksi struktur.
smoothdata: Pintasan Modern MATLAB #
MATLAB R2017a+ menyediakan smoothdata untuk smoothing sekali panggil:
% versi MATLAB (R2017a+) - smoothdata memilih window otomatis
yHalus = smoothdata(suhu, 'movmean', 12);
% padanan dengan fungsi core MATLAB R2016a+ (juga ada di Octave 10)
yHalus2 = movmean(suhu, 12);Kedua fungsi menggantikan loop manual kita dengan implementasi teroptimasi; movmean(x, 12) menghitung moving average dengan jendela terpusat secara default.
Berkas contoh.m ditutup dengan plot tiga kurva — data asli (abu-abu), trend CMA-12 (biru), dan data terkoreksi musiman (hijau putus-putus) — yang disimpan ke timeseries.png lewat saveas.
Ringkasan #
Dekomposisi deret waktu memecah y(t) menjadi trend, musiman, dan noise: MA-3 terpusat menghaluskan noise tanpa lag, CMA-12 mengestimasi trend, residu terhadap trend menghasilkan seasonal index, dan koreksi musiman memurnikan data sebelum regresi slope. Autokorelasi lag-k memverifikasi struktur — lag 12 tinggi pada data musiman lalu runtuh setelah koreksi. Urutan kerja ini penting karena regresi langsung pada data musiman menghasilkan slope bias (-0.018 padahal trend +0.04). smoothdata dan movmean menyediakan pintasan modern di MATLAB, sementara loop manual tetap menjadi versi yang teruji lintas platform.