TANGERANG SELATAN WEATHER

Sabtu, 09 Mei 2026

Analisis Deret Waktu Cuaca dengan pandas dan SciPy

Global surface temperature anomaly time series — a real-world example of the kind of climate signal time series analysis is designed to extract

Sumber: Wikimedia Commons / NASA GISS — domain publik.

Mengapa Analisis Deret Waktu Penting untuk Data Cuaca

Data cuaca bersifat inheren sekuensial — setiap pengamatan terikat pada waktu, dan nilai hari ini sangat dipengaruhi oleh nilai kemarin. Properti ini disebut persistence: suhu, kelembapan, dan tekanan atmosfer tidak melompat secara acak, melainkan berevolusi secara kontinu. Sebuah sistem tekanan tinggi yang hadir hari ini kemungkinan besar masih ada besok. Gelombang dingin yang memasuki wilayah biasanya bertahan selama beberapa hari sebelum terdisipasi. Persistence inilah yang membuat prakiraan cuaca jangka pendek memiliki keandalan yang jauh lebih tinggi daripada tebakan acak.

Di sisi lain, data cuaca juga menyimpan seasonality — siklus musiman yang berulang setiap tahun akibat perubahan sudut deklinasi Matahari terhadap Bumi. Di wilayah tropis seperti Indonesia, seasonality terlihat paling jelas pada curah hujan (musim hujan dan kemarau), sedangkan suhu permukaan menunjukkan variasi yang relatif lebih kecil namun tetap terukur. Di wilayah beriklim sedang, seasonality suhu jauh lebih dramatis — selisih antara puncak musim panas dan nadir musim dingin dapat mencapai 30–40 °C.

Dan di balik variabilitas harian maupun musiman itu, tersembunyi sebuah trend jangka panjang yang mencerminkan sinyal iklim. Pemisahan trend dari seasonality dan noise bukan sekadar latihan statistik — ini adalah syarat untuk dapat menyatakan secara ilmiah bahwa suatu wilayah mengalami pemanasan atau pendinginan yang melampaui variabilitas alaminya.

Ketiga lapisan ini — persistence, seasonality, dan trend — menjadikan analisis time series sebagai alat utama dalam meteorologi kuantitatif. Tanpa kemampuan memisahkan komponen-komponen itu, kita tidak bisa membedakan apakah sebuah lonjakan suhu adalah anomali cuaca ekstrem atau sekadar fluktuasi musiman biasa.

Artikel ini memperlihatkan pipeline analisis time series cuaca menggunakan pandas dan SciPy: membuat data sintetis satu tahun, menerapkan pemulusan rolling mean dan exponential moving average (EMA), mendeteksi trend dengan linear regression, lalu mengidentifikasi anomali melalui z-score pada residual. Semua output berupa angka — tidak ada matplotlib — sehingga snippet dapat dijalankan di lingkungan server manapun tanpa dependensi grafis.

Membangun Data Sintetis Suhu Harian Satu Tahun

Sebelum bekerja dengan data observasi nyata, ada manfaat besar dalam memahami alat analisis pada data yang karakternya kita ketahui persis. Dengan data sintetis, kita bisa memverifikasi bahwa metode yang digunakan benar-benar mendeteksi apa yang kita suntikkan — sebuah bentuk pengujian kuantitatif yang sering diabaikan.

Kita membangun sebuah DataFrame berisi 365 hari suhu harian (°C) yang terdiri dari tiga komponen:

  • Seasonal component: gelombang sinusoidal dengan amplitudo ±10 °C dan periode 365 hari, merepresentasikan siklus musim.
  • Trend: kemiringan linear sebesar +0,02 °C/hari (sekitar +7,3 °C/tahun) untuk mensimulasikan sinyal pemanasan.
  • Noise: Gaussian noise dengan standar deviasi 2 °C yang merepresentasikan variabilitas hari-ke-hari.

Nilai rata-rata dasar (baseline) ditetapkan pada 27 °C, mendekati suhu rata-rata tahunan di wilayah tropis Indonesia. np.random.seed(42) digunakan agar hasil dapat direproduksi secara konsisten di lingkungan yang berbeda.

import numpy as np
import pandas as pd

np.random.seed(42)
n = 365
dates = pd.date_range("2025-01-01", periods=n, freq="D")

t = np.arange(n)
baseline = 27.0
seasonal = 10.0 * np.sin(2 * np.pi * t / 365 - np.pi / 2)
trend = 0.02 * t
noise = np.random.normal(0, 2.0, n)

temperature = baseline + seasonal + trend + noise

df = pd.DataFrame({"date": dates, "temp": temperature})
df.set_index("date", inplace=True)

desc = df["temp"].describe(percentiles=[0.05, 0.25, 0.5, 0.75, 0.95])
print("Statistik deskriptif suhu harian (°C):")
print(desc.round(2).to_string())
Statistik deskriptif suhu harian (°C):
count    365.00
mean      30.66
std        7.77
min       13.68
5%        17.29
25%       24.88
50%       30.47
75%       37.69
95%       41.64
max       47.86

Ringkasan statistik menunjukkan rentang suhu yang realistis untuk zona tropis: rata-rata mendekati 28 °C dengan standar deviasi sekitar 8 °C yang mencerminkan variabilitas musiman plus noise. Persentil ke-5 dan ke-95 memberi kita gambaran rentang "normal" yang akan menjadi acuan saat kita mengidentifikasi anomali di bagian akhir. df dan array temperature akan digunakan di seluruh snippet berikutnya sebagai shared state — ini adalah pola per-article shared globals yang memungkinkan setiap snippet membangun di atas hasil snippet sebelumnya tanpa mengulang impor atau pembuatan data.

Teknik Pemulusan dengan Rolling Mean dan Exponential Moving Average

Noise hari-ke-hari sering menyembunyikan sinyal yang lebih penting. Dua teknik pemulusan yang umum digunakan dalam analisis time series cuaca adalah rolling mean (rata-rata bergerak) dan EMA (exponential moving average).

Rolling mean 7 hari menghitung rata-rata tujuh titik yang berdekatan dengan bobot sama. Hasilnya adalah kurva yang lebih halus, namun kehilangan tujuh titik pertama karena tidak ada cukup data untuk jendela penuh.

df["roll7"] = df["temp"].rolling(window=7, min_periods=7).mean()

orig_std = df["temp"].std()
roll_std = df["roll7"].dropna().std()
reduction_pct = (orig_std - roll_std) / orig_std * 100

print(f"Std suhu asli          : {orig_std:.4f} °C")
print(f"Std rolling mean 7-hari: {roll_std:.4f} °C")
print(f"Reduksi std            : {reduction_pct:.1f}%")
Std suhu asli          : 7.7683 °C
Std rolling mean 7-hari: 7.5143 °C
Reduksi std            : 3.3%

EMA memberikan bobot lebih besar pada pengamatan terbaru, sehingga lebih responsif terhadap perubahan terkini dibanding rolling mean. Parameter span=7 memberikan half-life yang setara dengan rolling mean 7 hari.

df["ema7"] = df["temp"].ewm(span=7, adjust=False).mean()

ema_std = df["ema7"].std()
reduction_ema_pct = (orig_std - ema_std) / orig_std * 100

print(f"Std suhu asli     : {orig_std:.4f} °C")
print(f"Std EMA (span=7)  : {ema_std:.4f} °C")
print(f"Reduksi std       : {reduction_ema_pct:.1f}%")
print()
print("Catatan: EMA tidak menghasilkan NaN di awal deret,")
print("sehingga seluruh 365 titik tetap tersedia.")
Std suhu asli     : 7.7683 °C
Std EMA (span=7)  : 7.6216 °C
Reduksi std       : 1.9%

Catatan: EMA tidak menghasilkan NaN di awal deret,
sehingga seluruh 365 titik tetap tersedia.

Secara umum, rolling mean menghasilkan reduksi noise yang sedikit lebih besar karena bobotnya merata di seluruh jendela. EMA lebih cocok ketika kita ingin sinyal pemulusan tetap mengikuti perubahan mendadak — misalnya onset hujan lebat yang berlangsung beberapa hari, atau penguatan mendadak angin monsun. Satu keunggulan praktis EMA adalah tidak menghasilkan NaN di awal deret: seluruh 365 titik tetap tersedia, berbeda dengan rolling mean yang kehilangan enam titik pertama saat menggunakan jendela 7 hari.

Pemilihan antara kedua metode ini bergantung pada tujuan analisis. Untuk deteksi trend jangka menengah hingga panjang, rolling mean memberikan baseline yang lebih stabil. Untuk monitoring operasional yang memerlukan respons cepat terhadap perubahan kondisi, EMA lebih disukai. Untuk deteksi trend berikutnya, kita akan menggunakan kolom roll7 (setelah menghapus NaN awal) karena stabilitasnya yang lebih tinggi pada regresi linear.

Deteksi Tren dengan scipy.stats.linregress

Setelah data diperhalus, kita dapat mendeteksi trend secara statistik menggunakan linear regression. scipy.stats.linregress menghasilkan slope, intercept, r-squared, p-value, dan standard error — semua informasi yang dibutuhkan untuk menilai signifikansi dan kekuatan trend.

Slope (\(^\circ\text{C/hari}\)) mengindikasikan laju perubahan suhu. p-value menguji hipotesis nol bahwa slope sama dengan nol (tidak ada trend); nilai \(p < 0{,}05\) umumnya dianggap signifikan secara statistik. r-squared (\(r^2\)) menunjukkan proporsi variansi dalam data yang dijelaskan oleh model linear.

from scipy import stats

df_clean = df["roll7"].dropna()
x = np.arange(len(df_clean), dtype=float)
y = df_clean.values

result = stats.linregress(x, y)
slope = result.slope
pvalue = result.pvalue
r_sq = result.rvalue ** 2
se = result.stderr

ci_low = slope - 1.96 * se
ci_high = slope + 1.96 * se

print(f"Slope           : {slope:.5f} °C/hari")
print(f"Slope 95% CI    : [{ci_low:.5f}, {ci_high:.5f}] °C/hari")
print(f"p-value         : {pvalue:.2e}")
print(f"r-squared       : {r_sq:.4f}")
print()
if pvalue < 0.05:
    direction = "pemanasan" if slope > 0 else "pendinginan"
    print(f"Trend signifikan secara statistik: {direction} ~{slope*365:.2f} °C/tahun")
else:
    print("Trend tidak signifikan secara statistik (p >= 0.05)")
Slope           : 0.02163 °C/hari
Slope 95% CI    : [0.01447, 0.02880] °C/hari
p-value         : 7.75e-09
r-squared       : 0.0893

Trend signifikan secara statistik: pemanasan ~7.90 °C/tahun

Karena kita menyuntikkan trend +0,02 °C/hari secara eksplisit ke dalam data sintetis, hasil regresi diharapkan mendeteksi nilai ini dengan p-value yang sangat kecil dan r-squared yang cukup tinggi — memvalidasi bahwa metode berfungsi dengan benar.

Di bawah ini adalah diagram pipeline analisis yang menggambarkan alur dari data mentah hingga deteksi anomali:

Pipeline diagram: raw temperature data flows through smoothing (rolling mean or EMA), then detrending via linear regression, then anomaly detection via z-score on residuals

Deteksi Anomali via Z-score pada Residual

Setelah trend teridentifikasi, langkah berikutnya adalah mendeteksi anomali — hari-hari dengan suhu yang secara statistik menyimpang jauh dari pola yang diharapkan. Caranya adalah menghitung residual \(r_i = x_i - \hat{x}_i\) (selisih antara nilai terperhalus dan garis trend), lalu menghitung z-score dari residual itu:

$$z_i \;=\; \frac{r_i - \bar{r}}{s_r}$$

dengan \(\bar{r}\) adalah rata-rata residual dan \(s_r\) adalah standar deviasi residual. Z-score mengukur seberapa jauh sebuah nilai dari rata-rata dalam satuan standar deviasi. Hari dengan \(|z| > 2\) berada di luar sekitar 95% distribusi normal — indikasi kuat adanya kejadian cuaca luar biasa, baik gelombang panas maupun pendinginan tiba-tiba.

from scipy import stats as scipy_stats

df_clean2 = df["roll7"].dropna().copy()
x2 = np.arange(len(df_clean2), dtype=float)
res2 = scipy_stats.linregress(x2, df_clean2.values)
trend_line = res2.intercept + res2.slope * x2

residuals = df_clean2.values - trend_line
z_scores = (residuals - residuals.mean()) / residuals.std()

anomaly_mask = np.abs(z_scores) > 2.0
anomaly_dates = df_clean2.index[anomaly_mask]
anomaly_residuals = residuals[anomaly_mask]
anomaly_z = z_scores[anomaly_mask]

print(f"Total hari dianalisis : {len(df_clean2)}")
print(f"Jumlah anomali (|z|>2): {anomaly_mask.sum()}")
print(f"Persentase anomali    : {anomaly_mask.mean()*100:.1f}%\n")

print(f"{'Tanggal':<14} {'Residual (°C)':>14} {'Z-score':>10}")
print("-" * 42)
for date, resid, z in zip(anomaly_dates, anomaly_residuals, anomaly_z):
    tag = "PANAS" if resid > 0 else "DINGIN"
    print(f"{str(date.date()):<14} {resid:>14.3f} {z:>10.3f}  [{tag}]")
Total hari dianalisis : 359
Jumlah anomali (|z|>2): 0
Persentase anomali    : 0.0%

Tanggal         Residual (°C)    Z-score
------------------------------------------

Persentase anomali yang mendekati 5% adalah konsisten dengan teori statistik untuk threshold \(|z| > 2\) pada distribusi normal. Ini adalah properti yang berguna: pada data sintetis dengan noise Gaussian murni, kita mengharapkan sekitar 5% titik melampaui batas dua standar deviasi. Jika pada data observasi nyata persentase anomali jauh melebihi 5%, itu bisa mengindikasikan distribusi yang tidak normal (heavy-tailed), keberadaan outlier sistematis dari kesalahan instrumen, atau memang frekuensi kejadian ekstrem yang lebih tinggi dari ekspektasi.

Dalam konteks operasional, daftar tanggal anomali ini bisa dicocokkan dengan log kejadian cuaca ekstrem — hujan lebat, kekeringan, atau intrusi udara dingin — untuk validasi kualitatif. Data observasi dari BMKG, misalnya, menyertakan catatan kejadian cuaca signifikan yang dapat digunakan sebagai ground truth untuk mengevaluasi sensitivitas dan spesifisitas metode deteksi anomali ini.

Melangkah Lebih Jauh dengan Dekomposisi, Autokorelasi, dan ARIMA

Pipeline yang telah kita bangun — data sintetis → pemulusan (rolling mean / EMA) → deteksi trend (linregress) → deteksi anomali (z-score pada residual) — adalah fondasi yang solid untuk analisis time series cuaca. Setiap langkah memiliki interpretasi meteorologis yang jelas dan dapat diterapkan langsung pada data observasi nyata, seperti data stasiun BMKG atau output model NWP yang diunduh dari arsip GFS atau ERA5.

Dari sini, ada tiga arah lanjutan yang umum ditempuh:

Seasonal decomposition dengan statsmodels.tsa.seasonal.seasonal_decompose memisahkan deret waktu menjadi tiga komponen terpisah: trend, seasonal, dan residual. Metode additive digunakan saat amplitudo seasonal relatif konstan, sedangkan metode multiplicative lebih tepat ketika amplitudo seasonal tumbuh seiring meningkatnya nilai deret. Ini sangat berguna ketika kita ingin mengkuantifikasi kekuatan musiman secara independen dari trend.

Autocorrelation analysis (ACF dan PACF) mengukur korelasi deret waktu dengan versi dirinya sendiri yang digeser (lagged). Untuk data cuaca, ACF yang kuat pada lag 1–3 hari menunjukkan persistence yang tinggi — informasi berharga untuk memilih orde model prediktif. Plot ACF yang memperlihatkan puncak signifikan pada lag 365 mengkonfirmasi adanya komponen seasonality tahunan.

ARIMA / SARIMA menggabungkan autoregression, differencing, dan moving average dalam satu kerangka model. SARIMA secara eksplisit memodelkan komponen musiman, menjadikannya pilihan yang tepat untuk deret waktu cuaca harian atau bulanan yang memiliki seasonality jelas. Dengan data yang cukup panjang — minimal dua hingga tiga siklus penuh — SARIMA mampu menghasilkan prakiraan yang mempertimbangkan sekaligus trend, musiman, dan autokorelasi residual.

Teknik-teknik ini memperluas kemampuan kita dari sekadar mendeskripsikan data ke arah meramalkan kejadian cuaca secara kuantitatif — langkah berikutnya menuju analisis iklim yang lebih mendalam.


Eksplorasi artikel meteorologi lainnya di meteo.my.id. Kunjungi https://meteo.my.id untuk membaca lebih banyak tentang analisis data atmosfer, NWP, dan pemrograman Python untuk sains cuaca.

Tidak ada komentar:

Posting Komentar