28  Notebook Bab 10 - Deret Waktu & Data Sensor

Open In ColabNotebook Bab 10 ini punya dua bagian. Bagian Demo tinggal Anda jalankan lalu amati keluarannya; bagian Mini Project berisi soal dan data yang Anda kerjakan sendiri.Pada deret waktu, fitur hanya boleh memakai informasi masa lalu dan validasinya harus kronologis. Melanggar keduanya menimbulkan look-ahead leakage dan skor palsu.

28.1 Persiapan

import warnings
warnings.filterwarnings('ignore')
import numpy as np
import pandas as pd
from sklearn.linear_model import Ridge
from sklearn.model_selection import cross_val_score, TimeSeriesSplit, KFold

RANDOM_STATE = 42
rng = np.random.default_rng(RANDOM_STATE)
print('Setup selesai.')
Setup selesai.

29 Section 1 - Demo: Fitur Lag dan Validasi Temporal

29.1 Data: dua sensor dan target autoregresif

Target y mengikuti proses autoregresif (bergantung pada nilainya sendiri di masa lalu) plus pengaruh sensor. Fitur yang kita bangun hanya lag sensor, jadi sengaja tidak menangkap seluruh memori y.

T = 1500
t = np.arange(T)
s1 = np.sin(2 * np.pi * t / 180) + rng.normal(0, 0.3, T)
s2 = np.cos(2 * np.pi * t / 90) + rng.normal(0, 0.3, T)
y = np.zeros(T)
for i in range(1, T):
    y[i] = 0.8 * y[i - 1] + 0.5 * s1[i - 1] + 0.3 * s2[i - 1] + rng.normal(0, 0.5)
ts = pd.DataFrame({'s1': s1, 's2': s2, 'y': y})

def buat_fitur(d):
    return pd.DataFrame({
        's1_lag1': d['s1'].shift(1), 's1_lag2': d['s1'].shift(2),
        's2_lag1': d['s2'].shift(1), 's2_lag2': d['s2'].shift(2),
        's1_roll5': d['s1'].shift(1).rolling(5).mean(),
    })

F = buat_fitur(ts).assign(y=ts['y']).dropna().reset_index(drop=True)
X = F.drop(columns='y').to_numpy()
yv = F['y'].to_numpy()
print('Frame supervised:', X.shape)
Frame supervised: (1495, 5)

29.2 Validasi temporal vs random

tscv = TimeSeriesSplit(n_splits=5)
kf = KFold(n_splits=5, shuffle=True, random_state=RANDOM_STATE)
r_temporal = cross_val_score(Ridge(), X, yv, cv=tscv, scoring='r2').mean()
r_random = cross_val_score(Ridge(), X, yv, cv=kf, scoring='r2').mean()
print(f'Temporal split (benar) R2 = {r_temporal:.3f}')
print(f'Random split (keliru)  R2 = {r_random:.3f}')
Temporal split (benar) R2 = 0.805
Random split (keliru)  R2 = 0.817

29.3 Look-ahead leakage: fitur yang menyentuh masa depan

bocor = buat_fitur(ts)
bocor['y_masa_depan'] = ts['y'].shift(-1)   # LOOK-AHEAD: memakai target masa depan
B = bocor.assign(y=ts['y']).dropna().reset_index(drop=True)
Xb = B.drop(columns='y').to_numpy()
yb = B['y'].to_numpy()

r_kausal = cross_val_score(Ridge(), X, yv, cv=tscv, scoring='r2').mean()
r_leak = cross_val_score(Ridge(), Xb, yb, cv=tscv, scoring='r2').mean()
print(f'Fitur kausal (hanya lag sensor)   R2 = {r_kausal:.3f}')
print(f'Dengan target masa depan (bocor)  R2 = {r_leak:.3f}')
Fitur kausal (hanya lag sensor)   R2 = 0.805
Dengan target masa depan (bocor)  R2 = 0.934

🔎 Amati. Fitur kausal (hanya lag sensor) menangkap sebagian sinyal. Menambahkan target masa depan y.shift(-1) melonjakkan R2 secara semu karena deret ini berkorelasi kuat antar-waktu: itulah look-ahead leakage, dan fitur seperti itu tidak tersedia saat prediksi nyata. Random split juga sedikit lebih optimistis daripada temporal split karena menaruh titik bertetangga waktu di latih dan uji sekaligus. Aturannya: fitur hanya dari masa lalu, validasi kronologis.

30 Section 2 - Mini Project

30.1 Soal

Anda diberi deret waktu multivariat (tiga sensor) dengan satu target y.

Tugas:

  1. Bangun fitur lag dan rolling yang kausal (hanya masa lalu, pakai shift).
  2. Evaluasi model dengan TimeSeriesSplit (tambahkan gap bila perlu).
  3. Bandingkan dengan KFold(shuffle=True) dan jelaskan mengapa hasilnya bisa menyesatkan.

Luaran: kode fitur + kedua skema validasi, plus 2-3 kalimat kesimpulan.

Kriteria penilaian: (a) semua fitur kausal; (b) validasi temporal benar; (c) analisis perbedaan temporal vs random.

# DATA AWAL (jangan diubah) - tiga sensor, target autoregresif.
T = 1800
t = np.arange(T)
a = np.sin(2 * np.pi * t / 120) + rng.normal(0, 0.3, T)
b = np.cos(2 * np.pi * t / 200) + rng.normal(0, 0.3, T)
c = rng.normal(0, 1, T).cumsum() * 0.02
y = np.zeros(T)
for i in range(2, T):
    y[i] = 0.7 * y[i - 1] + 0.4 * a[i - 1] + 0.3 * b[i - 2] + 0.2 * c[i - 1] + rng.normal(0, 0.4)
sensor = pd.DataFrame({'a': a, 'b': b, 'c': c, 'y': y})
print('Data:', sensor.shape)
sensor.head()
Data: (1800, 4)
a b c y
0 -0.150064 0.831895 0.035560 0.000000
1 -0.394081 1.470670 0.060504 0.000000
2 0.430211 0.782777 0.057327 -0.115630
3 -0.235866 0.924158 0.033161 0.014069
4 0.446995 0.668242 0.059909 0.257067
# Kerjakan di sini.
# Petunjuk: sensor['a'].shift(1), .rolling(k).mean() setelah shift; TimeSeriesSplit(n_splits=5, gap=...).