Dengan data bersih dari Bagian 2: Membersihkan Data, saatnya merangkum 365 hari pengukuran menjadi angka-angka yang bisa dilaporkan: rata-rata suhu per bulan, hari terpanas dan terdingin, serta rekap curah hujan. Semua perhitungan di bagian ini memakai fungsi agregasi NumPy yang telah dipelajari pada Chapter Operasi Berdasarkan Axis — kita hanya mengganti konteksnya menjadi data cuaca.

Satu keputusan teknis perlu disepakati di awal. Kalender 2025 tidak bisa dibagi rata menjadi bulan-bulan 30 hari, maka kita menyederhanakan analisis bulanan menjadi 12 blok × 30 hari = 360 hari pertama; lima hari terakhir Desember tetap terhitung dalam statistik tahunan tetapi tidak masuk tabel bulanan. Penyederhanaan seperti ini lazim dalam analisis eksploratif, selama disebutkan terbuka — dan seluruh proyek memakai aturan yang konsisten.

Statistik Bulanan dengan reshape #

Menghitung rata-rata dua belas bulan tidak membutuhkan loop. Kita mengiris 360 hari pertama, melipatnya menjadi matriks 12 × 30 dengan reshape (Chapter reshape), lalu mengagregasi sepanjang axis=1 — axis hari — sehingga tersisa satu nilai per bulan:

python
import numpy as np

# Pembangun data cuaca 2025 (data bersih, tanpa sentinel).
rng = np.random.default_rng(42)
t = np.arange(365)
noise = rng.normal(0.0, 1.0, (365, 3)) * np.array([0.8, 0.6, 0.8])
musim = -np.cos(2 * np.pi * t / 365)
suhu_rata = 27.0 + 2.5 * musim + 0.001 * t + noise[:, 0]
suhu_min = suhu_rata - 3.5 + noise[:, 1]
suhu_max = suhu_rata + 4.0 + noise[:, 2]
curah_hujan = np.clip(rng.normal(5.0, 7.0, 365), 0.0, None)
curah_hujan *= 1 + 0.7 * np.cos(2 * np.pi * t / 365)
data = np.column_stack([suhu_rata, suhu_min, suhu_max, curah_hujan])

suhu_bulanan = suhu_rata[:360].reshape(12, 30)
rata_bulanan = suhu_bulanan.mean(axis=1)
bulan = np.array(["Januari", "Februari", "Maret", "April", "Mei", "Juni",
                  "Juli", "Agustus", "September", "Oktober", "November", "Desember"])
for b, r in zip(bulan, rata_bulanan):
    print(f"{b:<10}: {r:5.2f} °C")
print("terpanas :", bulan[np.argmax(rata_bulanan)], f"{rata_bulanan.max():.2f} °C")
print("terdingin:", bulan[np.argmin(rata_bulanan)], f"{rata_bulanan.min():.2f} °C")

Output yang Dihasilkan:

text
Januari   : 24.63 °C
Februari  : 25.22 °C
Maret     : 26.31 °C
April     : 27.83 °C
Mei       : 28.74 °C
Juni      : 29.50 °C
Juli      : 29.59 °C
Agustus   : 28.88 °C
September : 28.07 °C
Oktober   : 26.77 °C
November  : 25.86 °C
Desember  : 25.26 °C
terpanas : Juli 29.59 °C
terdingin: Januari 24.63 °C

Yang terjadi di balik blok singkat ini:

  1. suhu_rata[:360].reshape(12, 30): iris 360 elemen dilipat menjadi 12 baris bulan; karena array aslinya 1-D dan kontigu, reshape hanya mengubah cara membaca memori tanpa menyalin data.
  2. .mean(axis=1): agregasi berjalan horizontal — rata-rata 30 hari di dalam satu baris — menghasilkan 12 nilai bulanan; axis=0 justru akan memberi rata-rata per posisi hari.
  3. zip(bulan, rata_bulanan): memasangkan label dengan angka agar output siap dibaca manusia.
  4. bulan[np.argmax(rata_bulanan)]: argmax memberi posisi nilai tertinggi, bukan nilainya, sehingga kita bisa memetakan posisi itu ke nama bulan (lihat Chapter min, max, argmin, dan argmax).

Pola yang muncul sesuai desain dataset: suhu merambat naik dari Januari (24.63 °C) menuju puncak Juli (29.59 °C), lalu turun kembali — rentang musiman hampir 5 °C.

Hari Terpanas dan Terdingin #

Rata-rata bulanan menyembunyikan hari-hari ekstrem. Untuk menemukannya, kita memakai argmax dan argmin pada deret harian, lalu memetakan indeks hari ke tanggal sesungguhnya memakai datetime64 dari Chapter datetime64 dan timedelta64:

python
import numpy as np

# Pembangun data cuaca 2025 (data bersih, tanpa sentinel).
rng = np.random.default_rng(42)
t = np.arange(365)
noise = rng.normal(0.0, 1.0, (365, 3)) * np.array([0.8, 0.6, 0.8])
musim = -np.cos(2 * np.pi * t / 365)
suhu_rata = 27.0 + 2.5 * musim + 0.001 * t + noise[:, 0]
suhu_min = suhu_rata - 3.5 + noise[:, 1]
suhu_max = suhu_rata + 4.0 + noise[:, 2]
curah_hujan = np.clip(rng.normal(5.0, 7.0, 365), 0.0, None)
curah_hujan *= 1 + 0.7 * np.cos(2 * np.pi * t / 365)
data = np.column_stack([suhu_rata, suhu_min, suhu_max, curah_hujan])

tanggal = np.arange(np.datetime64("2025-01-01"), np.datetime64("2026-01-01"))
i_max = np.argmax(suhu_rata)
i_min = np.argmin(suhu_rata)
print(f"terpanas  : {tanggal[i_max]}  {suhu_rata[i_max]:.2f} °C")
print(f"terdingin : {tanggal[i_min]}  {suhu_rata[i_min]:.2f} °C")
j = np.argmax(suhu_max)
print(f"suhu max tertinggi: {tanggal[j]}  {suhu_max[j]:.2f} °C")
k = np.argmin(suhu_min)
print(f"suhu min terendah : {tanggal[k]}  {suhu_min[k]:.2f} °C")

Output yang Dihasilkan:

text
terpanas  : 2025-07-15  31.24 °C
terdingin : 2025-12-09  23.29 °C
suhu max tertinggi: 2025-06-24  35.84 °C
suhu min terendah : 2025-12-29  19.32 °C

Penjelasan singkat:

  1. np.arange(datetime64, datetime64): membuat deret 365 tanggal harian tanpa loop, sehingga tanggal[i] selalu sejajar dengan suhu_rata[i].
  2. argmax pada suhu_rata: hari terpanas jatuh pada 15 Juli (31.24 °C) — bertepatan dengan puncak musiman, tetapi tanggal persisnya ditentukan derau harian.
  3. Hari terdingin 9 Desember: menariknya, bulan terdingin adalah Januari, tetapi hari terdingin justru di Desember; rata-rata dan ekstrem tidak harus jatuh di tempat yang sama, dan laporan yang jujur menyajikan keduanya.

Rekap Curah Hujan #

Statistik hujan yang paling sering diminta adalah jumlah hari hujan, total tahunan, dan peristiwa hujan terbesar. Ketiganya hanya butuh perbandingan, count_nonzero, dan argmax (pola penghitungan serupa dibahas pada Chapter Counting dan Frekuensi):

python
import numpy as np

# Pembangun data cuaca 2025 (data bersih, tanpa sentinel).
rng = np.random.default_rng(42)
t = np.arange(365)
noise = rng.normal(0.0, 1.0, (365, 3)) * np.array([0.8, 0.6, 0.8])
musim = -np.cos(2 * np.pi * t / 365)
suhu_rata = 27.0 + 2.5 * musim + 0.001 * t + noise[:, 0]
suhu_min = suhu_rata - 3.5 + noise[:, 1]
suhu_max = suhu_rata + 4.0 + noise[:, 2]
curah_hujan = np.clip(rng.normal(5.0, 7.0, 365), 0.0, None)
curah_hujan *= 1 + 0.7 * np.cos(2 * np.pi * t / 365)
data = np.column_stack([suhu_rata, suhu_min, suhu_max, curah_hujan])

tanggal = np.arange(np.datetime64("2025-01-01"), np.datetime64("2026-01-01"))
hari_hujan = np.count_nonzero(curah_hujan > 1.0)
print("hari hujan (> 1 mm) :", hari_hujan, f"({hari_hujan / 365:.1%})")
print("total curah hujan   :", f"{curah_hujan.sum():.1f} mm")
print("rata-rata harian    :", f"{curah_hujan.mean():.2f} mm")
i = np.argmax(curah_hujan)
print(f"hujan terbesar: {tanggal[i]}  {curah_hujan[i]:.1f} mm")
nol = np.count_nonzero(curah_hujan == 0.0)
print("hari tanpa hujan (0 mm):", nol)

Output yang Dihasilkan:

text
hari hujan (> 1 mm) : 257 (70.4%)
total curah hujan   : 2338.0 mm
rata-rata harian    : 6.41 mm
hujan terbesar: 2025-12-28  35.8 mm
hari tanpa hujan (0 mm): 85

Uraian singkat:

  1. curah_hujan > 1.0: ambang satu milimeter memisahkan gerimis yang tidak berarti dari hujan sesungguhnya; hasilnya mask boolean sepanjang 365 hari.
  2. np.count_nonzero(...): menghitung sel True — jauh lebih ringkas daripada loop penghitung.
  3. curah_hujan.sum() dan .mean(): agregasi total dan rata-rata sepanjang satu sumbu data 1-D.
  4. argmax pada hujan: peristiwa terbesar (35.8 mm pada 28 Desember) terjadi saat faktor musim hujan sedang tinggi di akhir tahun.

Angka-angkanya konsisten dengan profil kota sangat lembap: 70.4% hari di atas ambang hujan, total 2 338 mm setahun. Sisa 85 hari tanpa hujan terkonsentrasi saat faktor cos menekan curah hujan di pertengahan tahun.

Statistik di bagian ini masih berdiri sendiri-sendiri. Bagian 4: Menganalisis Pola Cuaca akan menyatukannya — menaksir tren, menyaring anomali, dan merangkai laporan akhir proyek.