Tim pemantauan pasar di kantor perdagangan meminta kita menganalisis pergerakan harga tiga komoditas pokok sepanjang 2025: beras, cabai, dan minyak goreng. Data harga asli bersifat internal, maka kita membangun deret sintetis yang meniru perilakunya — tren pelan, musim yang mengangkat harga di pertengahan tahun, dan guncangan harian. Deret harga dibangun sebagai random walk: harga hari ini adalah harga awal yang dikalikan seluruh faktor harian (1 + drift + guncangan) secara beruntun, dan np.cumprod dari Chapter Cumulative Sum dan Product melakukannya tanpa satu pun loop. Karena seluruh keacakan keluar dari np.random.default_rng(42), deret yang sama muncul persis di komputer mana pun (Chapter Reproducibility dan Random Seed).
Bagian ini menyiapkan dua bahan yang dipakai tiga bagian berikutnya: matriks harga 360 × 3 yang tersimpan di data/harga_komoditas.csv, dan deret tanggal datetime64 yang sejajar baris demi baris dengan harganya. Pada proyek sungguhan, langkah pembangunan ini kita kemas dalam src/persiapan_harga.py.
Membangun Deret Harga Sintetis #
Tiga komoditas diberi karakter berbeda: beras bergerak pelan, cabai sangat liar, minyak goreng di tengah-tengah. Perbedaan itu lahir dari dua vektor parameter — drift (arah tren harian) dan sigma (simpangan baku guncangan harian):
import numpy as np
# Pembangun deret harga 2025 — dipakai di seluruh proyek.
rng = np.random.default_rng(42)
awal = np.array([15000.0, 45000.0, 18000.0])
drift = np.array([0.0003, 0.0012, 0.0004])
sigma = np.array([0.006, 0.030, 0.010])
musim = 1 - np.array([0.01, 0.15, 0.03]) * np.cos(2 * np.pi * np.arange(360) / 360)[:, None]
guncangan = drift + rng.normal(0.0, sigma, (360, 3))
harga = awal * np.cumprod(1 + guncangan, axis=0) * musim
print("shape:", harga.shape, "| dtype:", harga.dtype)
print("harga hari pertama :", np.round(harga[0], 2))
print("harga hari terakhir:", np.round(harga[-1], 2))Output yang Dihasilkan:
shape: (360, 3) | dtype: float64
harga hari pertama : [14881.61 37102.52 17598.01]
harga hari terakhir: [16825.21 33284.1 16810.88]Mari kita bedah pembangun deret ini:
- Baris 4-6 (
awal,drift,sigma): tiga array sepanjang 3 menyimpan harga pembukaan (rupiah), tren harian, dan volatilitas per komoditas; urutan kolomnya tetap: beras, cabai, minyak goreng. - Baris 7 (
musim = ...): faktor musiman bentuk 360 × 3;np.cosmenyelesaikan satu siklus penuh sepanjang 360 hari, amplitudo per komoditas (0.01,0.15,0.03) membuat cabai paling terpengaruh, dan tanda minus menaruh lembah musiman di awal tahun. - Baris 8 (
guncangan = ...):rng.normal(0.0, sigma, (360, 3))menggambar derau standar lalu menskalakannya per kolom, ditambahdrift— satu matriks berisi seluruh perubahan harian. - Baris 9 (
np.cumprod(1 + guncangan, axis=0)): perkalian beruntun 360 faktor menghasilkan lintasan harga; mengalikannya denganawalmenempatkan lintasan pada level rupiah, dan* musimmenempelkan gelombang musiman di atasnya. - Baris 11-13: harga hari pertama lebih rendah dari harga pembukaan karena faktor musiman awal tahun berada di lembah; cabai ditutup tahun itu di 33 284 — guncangan harian yang besar bisa melampaui arah tren dalam jangka setahun.
Menulis CSV dan Menyiapkan Tanggal #
Deret harga disimpan ke CSV agar serupa dengan data yang keluar dari sistem kasir, sementara tanggalnya tidak ikut disimpan sebagai angka — kita membuatnya terpisah dari datetime64:
import numpy as np
# Pembangun deret harga 2025 (identik dengan blok sebelumnya).
rng = np.random.default_rng(42)
awal = np.array([15000.0, 45000.0, 18000.0])
drift = np.array([0.0003, 0.0012, 0.0004])
sigma = np.array([0.006, 0.030, 0.010])
musim = 1 - np.array([0.01, 0.15, 0.03]) * np.cos(2 * np.pi * np.arange(360) / 360)[:, None]
guncangan = drift + rng.normal(0.0, sigma, (360, 3))
harga = awal * np.cumprod(1 + guncangan, axis=0) * musim
np.savetxt("harga_komoditas.csv", harga, delimiter=",", fmt="%.2f",
header="beras,cabai,minyak_goreng", comments="")
harga_csv = np.loadtxt("harga_komoditas.csv", delimiter=",", skiprows=1)
print("identik persis:", np.array_equal(harga, harga_csv))
print("allclose atol=0.01:", np.allclose(harga, harga_csv, atol=0.01))
tanggal = np.arange(np.datetime64("2025-01-01"), np.datetime64("2025-12-27"))
print("tanggal:", tanggal[0], "s.d.", tanggal[-1], "| jumlah:", tanggal.size)
with open("harga_komoditas.csv") as f:
for i, baris in enumerate(f):
if i >= 2:
break
print(baris.rstrip())Output yang Dihasilkan:
identik persis: False
allclose atol=0.01: True
tanggal: 2025-01-01 s.d. 2025-12-26 | jumlah: 360
beras,cabai,minyak_goreng
14881.61,37102.52,17598.01Rincian yang penting:
savetxtdenganfmt="%.2f": harga dibulatkan dua desimal;headermenuliskan nama kolom dancomments=""meniadakan tanda#di depannya (Chapter Membaca dan Menulis CSV).array_equalbernilaiFalse,allclosebernilaiTrue: pembulatan dua desimal menggeser nilai paling jauh setengah sen, sehingga kedua array tidak identik bit demi bit tetapi setara untuk analisis harga.np.arange(datetime64, datetime64): 360 tanggal harian dari 1 Januari sampai 26 Desember 2025; deret ini disebut kolom tanggal pendamping karena sejajar persis dengan barishargatanpa ikut masuk berkas CSV (pola datetime64 dibahas pada Chapter datetime64 dan timedelta64).
Pemeriksaan Struktur Awal #
Terakhir, kita pastikan deret layak dianalisis: tiga kolom, nol nan, tidak ada harga nol atau negatif, dan rentang nilai yang masuk akal untuk pasar:
import numpy as np
# Pembangun deret harga 2025 (identik dengan blok sebelumnya).
rng = np.random.default_rng(42)
awal = np.array([15000.0, 45000.0, 18000.0])
drift = np.array([0.0003, 0.0012, 0.0004])
sigma = np.array([0.006, 0.030, 0.010])
musim = 1 - np.array([0.01, 0.15, 0.03]) * np.cos(2 * np.pi * np.arange(360) / 360)[:, None]
guncangan = drift + rng.normal(0.0, sigma, (360, 3))
harga = awal * np.cumprod(1 + guncangan, axis=0) * musim
print("min per komoditas:", np.round(harga.min(axis=0), 2))
print("max per komoditas:", np.round(harga.max(axis=0), 2))
print("semua harga > 0 :", bool(np.all(harga > 0)))
print("ada nan :", bool(np.isnan(harga).any()))Output yang Dihasilkan:
min per komoditas: [14764.39 30267.24 16042.13]
max per komoditas: [16825.21 52830.47 21901.49]
semua harga > 0 : True
ada nan : FalsePemeriksaan ini memakai pola agregasi axis=0 (Chapter Operasi Berdasarkan Axis):
harga.min(axis=0)danharga.max(axis=0): satu nilai per komoditas; cabai menyentuh 52 830 di puncak musim dan turun sampai 30 267 — rentang yang jauh lebih lebar daripada beras.np.all(harga > 0): model perkalian menjamin harga positif, dan pemeriksaan ini membuktikannya pada data nyata — kebiasaan yang wajib dipertahankan ketika model diganti data lapangan.np.isnan(harga).any(): tidak ada sel kosong;bool(...)mengubah hasilnumpy.bool_menjadiTrue/FalsePython murni agar outputnya rapi.
Ringkasan dataset yang dipakai sepanjang proyek:
| Kolom | Komoditas | Harga pembukaan (Rp) | Rentang teramati (Rp) |
|---|---|---|---|
| 0 | beras | 15 000 | 14 764 - 16 825 |
| 1 | cabai | 45 000 | 30 267 - 52 830 |
| 2 | minyak goreng | 18 000 | 16 042 - 21 901 |
Data siap di data/harga_komoditas.csv, tanggal siap di memori. Bagian 2: Menghitung Perubahan Harga mulai mengolah deret ini — dan di sanalah informasi paling berharga dari harga justru muncul: bukan pada levelnya, melainkan pada perubahannya.