Data bersih belum berarti data yang dikenal. Sebelum memikirkan fitur dan model, kita perlu tahu bentuk deret harga ini: ke mana kecenderungannya, apakah ada irama yang berulang, dan di mana kejadian luar biasa berada. Jawaban atas tiga pertanyaan itu menentukan pilihan fitur pada Bagian 6 dan menyiapkan penafsiran hasil evaluasi nanti — model yang menang atau kalah hanya bisa dijelaskan bila kita paham medan yang diprediksi.

Menggambar Deret Harga #

Alat baca pertama untuk deret waktu selalu grafik garis: sumbu horizontal waktu, sumbu vertikal harga, satu titik per minggu. Grafik di bawah memakai data bersih hasil Bagian 4:

python
import matplotlib.pyplot as plt
import pandas as pd

# Resep pembersihan hasil Bagian 4: duplikat dibuang, '-' diinterpolasi.
df = pd.read_csv("../data/harga_mingguan.csv")
bersih = df.drop_duplicates(subset=["minggu"], keep="first").copy()
bersih["harga_ribu"] = pd.to_numeric(bersih["harga_ribu"], errors="coerce")
bersih = bersih.sort_values("tanggal_mulai").reset_index(drop=True)
bersih["harga_ribu"] = bersih["harga_ribu"].interpolate().round(1)

x = range(1, len(bersih) + 1)
y = bersih["harga_ribu"]

fig, ax = plt.subplots(figsize=(9, 4.2))
ax.plot(x, y, marker="o", markersize=3.5, linewidth=1.6, color="#1f77b4", label="harga mingguan")
ax.axhline(y.mean(), color="gray", linestyle="--", linewidth=1, label=f"rata-rata {y.mean():.1f}")
ax.axvspan(19, 21, color="tab:orange", alpha=0.18, label="lonjakan W19-W21")
ax.annotate("puncak W20: 53.1", xy=(20, 53.1), xytext=(24.5, 52.3), arrowprops={"arrowstyle": "->"})
ax.set_xlabel("minggu ke-")
ax.set_ylabel("harga (ribu rupiah per kg)")
ax.set_title("Harga mingguan cabai merah, W1-W36")
ax.set_xticks(range(2, 37, 2))
ax.legend(loc="upper left")
ax.grid(alpha=0.3)
fig.tight_layout()
plt.show()

Bentuk grafiknya dapat dibaca sebagai berikut. Sepanjang 36 minggu, harga berayun di sekitar garis rata-rata 40,6 ribu (garis putus-putus abu-abu), dengan lembah terdalam 35,9 di W6 dan bukit-bukit kecil yang muncul teratur. Pita oranye menandai satu kejadian yang menyimpang dari irama biasa: lonjakan W19–W21 yang menembus hingga 53,1 di W20 — jauh di atas segala puncak lain — sebelum harga jatuh kembali ke 39,9 pada W22. Di ujung kanan, deret menutup dengan tanjakan W31–W34 yang lalu melandai. Tiga pola itulah — kecenderungan naik, irama delapan mingguan, dan satu lonjakan — yang kita ukur satu per satu berikutnya. Ragam jenis grafik dan kapan memakainya dibahas pada katalog tipe plot matplotlib .

Tren, Gelombang, dan Lonjakan dalam Angka #

Mata bisa tertipu oleh skala sumbu; angka tidak. Blok berikut mengukur tiga hal sekaligus: kecenderungan lewat kemiringan garis lurus terbaik (np.polyfit derajat 1), irama lewat jarak antar puncak lokal, dan lonjakan lewat perubahan hariannya:

python
import numpy as np
import pandas as pd

# Resep pembersihan hasil Bagian 4: duplikat dibuang, '-' diinterpolasi.
df = pd.read_csv("../data/harga_mingguan.csv")
bersih = df.drop_duplicates(subset=["minggu"], keep="first").copy()
bersih["harga_ribu"] = pd.to_numeric(bersih["harga_ribu"], errors="coerce")
bersih = bersih.sort_values("tanggal_mulai").reset_index(drop=True)
bersih["harga_ribu"] = bersih["harga_ribu"].interpolate().round(1)

y = bersih["harga_ribu"].to_numpy()
harga = bersih.set_index("minggu")["harga_ribu"]

print(f"Rentang      : {y.min():.1f} (W6) s.d. {y.max():.1f} (W20), rata-rata {y.mean():.2f} ribu")
print(f"Setengah awal vs akhir : {y[:18].mean():.2f} vs {y[18:].mean():.2f} ribu")
print(f"Tren polyfit : {np.polyfit(np.arange(1, 37), y, 1)[0]:+.3f} ribu per minggu")
print(f"Korelasi lag-1: {np.corrcoef(y[:-1], y[1:])[0, 1]:.3f}")
print()
print("Puncak lokal tiap ~8 minggu (siklus panen):")
puncak_lokal = ["W10", "W18", "W26", "W34"]
nomor_puncak = [10, 18, 26, 34]
for i, m in enumerate(puncak_lokal):
    if i == 0:
        print(f"  {m} = {harga[m]:.1f}")
    else:
        print(f"  {m} = {harga[m]:.1f}  (jarak {nomor_puncak[i] - nomor_puncak[i - 1]} minggu dari puncak sebelumnya)")
print()
print(f"Lonjakan W19-W21: W18 {harga['W18']:.1f} -> W19 {harga['W19']:.1f} "
      f"(+{(harga['W19'] - harga['W18']) / harga['W18'] * 100:.1f}%) -> W20 {harga['W20']:.1f} "
      f"-> W22 {harga['W22']:.1f}")

Output yang Dihasilkan:

text
Rentang      : 35.9 (W6) s.d. 53.1 (W20), rata-rata 40.56 ribu
Setengah awal vs akhir : 38.98 vs 42.15 ribu
Tren polyfit : +0.089 ribu per minggu
Korelasi lag-1: 0.728

Puncak lokal tiap ~8 minggu (siklus panen):
  W10 = 41.4
  W18 = 41.5  (jarak 8 minggu dari puncak sebelumnya)
  W26 = 42.2  (jarak 8 minggu dari puncak sebelumnya)
  W34 = 43.0  (jarak 8 minggu dari puncak sebelumnya)

Lonjakan W19-W21: W18 41.5 -> W19 51.0 (+22.9%) -> W20 53.1 -> W22 39.9

Mari kita bedah angka-angka itu satu per satu:

  1. Tren naik ringan +0,089 ribu per minggu: garis lurus terbaik menanjak sekitar 0,7 ribu setiap delapan minggu. Perbandingan setengah awal (38,98) dan setengah akhir (42,15) mengonfirmasi arah yang sama. Tren sehalus ini tidak terasa dari minggu ke minggu, tetapi terakumulasi menjadi sekitar 3 ribu sepanjang 36 minggu — cukup besar untuk diwakili sebuah fitur.
  2. Irama delapan mingguan: empat puncak lokal — W10, W18, W26, W34 — berjarak tepat delapan minggu dan makin tinggi: 41,4, 41,5, 42,2, 43,0. Inilah siklus panen cabai yang sudah disinggung pada Bagian 2: panen melimpah menekan harga, pasokan menipis mengangkatnya, lalu berulang. Musiman seperti ini adalah bahan bakar utama prediksi — pola yang pernah terjadi berulang kali layak dipelajari model.
  3. Lonjakan W19–W21 sebagai gangguan: harga melompat 22,9% dalam satu minggu (41,5 menjadi 51,0), memuncak di 53,1, lalu turun kembali. Lonjakan ini jatuh pada awal Oktober 2025 — permulaan musim hujan yang mengganggu pasokan — dan bentuknya berbeda dari gelombang panen: lebih tajam masuk, lebih tajam keluar. Pola seperti ini bukan irama yang bisa diantisipasi dari riwayat semata; kelak kita akan melihat bahwa justru di sinilah model paling banyak tersandung.

Korelasi Lag-1: Harga Minggu Ini dan Minggu Depan #

Baris keempat keluaran memberi angka paling penting untuk desain fitur: korelasi lag-1 sebesar 0,728. Angka itu dihitung dengan menjajarkan deret dengan salinannya yang digeser satu minggu — harga W1 dengan W2, W2 dengan W3, dan seterusnya — lalu menghitung korelasi Pearson pasangan-pasangan itu. Nilai 0,728 termasuk kuat: harga minggu ini memang membawa informasi tentang harga minggu depan. Deret yang berayun perlahan seperti ini “menempel pada dirinya sendiri”.

Konsekuensinya langsung: bila harga kemarin memprediksi harga besok sebesar itu, maka harga satu minggu dan dua minggu silam adalah kandidat fitur yang paling masuk akal — dan itulah jalan yang kita ambil pada Bagian 6. Pendekatan mengubah deret waktu menjadi masalah supervised learning dengan fitur geser seperti ini dibahas tuntas pada chapter Forecasting dengan Machine Learning .

Ringkasan Bagian 5 #

Deret harga cabai terbaca sebagai gabungan tiga pola: tren naik ringan +0,089 ribu per minggu, gelombang musiman delapan mingguan dengan puncak W10–W34 yang makin tinggi, dan satu lonjakan W19–W21 (puncak 53,1) akibat gangguan musim hujan. Korelasi lag-1 sebesar 0,728 menegaskan bahwa harga minggu sebelumnya adalah prediktor yang kuat.

Tiga temuan itu kita olah menjadi bahan baku model: tabel fitur lag dan pembagian kronologis, pada Bagian 6: Membuat Fitur Prediksi .