Chapter-chapter sebelumnya membuat setiap langkah kita teruji; chapter penutup ini membuat seluruh rangkaian bisa diulang dengan hasil yang sama. Reproducibilitas adalah syarat dasar debugging yang sehat: jika hasil analisis berubah setiap kali dijalankan, test dan gerbang validasi tidak ada gunanya karena rujukannya sendiri bergerak. Tiga pilar menopangnya — seed untuk angka acak, catatan versi pustaka, dan tanggal serta data uji yang beku. Script pada chapter ini menyatukan ketiganya dalam satu pipeline kecil yang terbukti menghasilkan output identik pada setiap eksekusi.

Menyematkan Seed pada Sumber Acak #

Angka acak muncul di mana-mana pada pekerjaan data: sampling baris, simulasi data uji, inisialisasi parameter. Angka-angka itu tidak perlu dihindari — cukup dibuat terulang. Generator modern NumPy, np.random.default_rng(seed), menghasilkan deret nilai yang sama selama seed-nya sama, dan instansinya diteruskan ke pemanggilan berikutnya sehingga urutannya tetap terjaga. Method sample() milik pandas memiliki padanannya sendiri, yaitu parameter random_state, sehingga baris yang terpilih juga tetap. Kebiasaannya: satu konstanta SEED di bagian atas skrip, dipakai oleh semua sumber acak di bawahnya.

Pipeline Kecil yang Terbukti Dapat Diulang #

Skrip berikut merangkai semuanya: konstanta konfigurasi di atas, generator data transaksi dengan seed, fungsi agregasi, pembuktian dua eksekusi menghasilkan hasil yang equals, sampel dengan random_state, dan uji simpan-baca kembali ke CSV dalam folder sementara yang otomatis dihapus.

python
import numpy as np
import pandas as pd
from datetime import date
from tempfile import TemporaryDirectory

SEED = 42
TANGGAL_LAPORAN = date(2026, 1, 15)
KOTA = np.array(["Jakarta", "Bandung", "Surabaya", "Medan"])
PRODUK = np.array(["Gula", "Minyak", "Beras", "Kopi"])


def bangun_transaksi(n: int = 8) -> pd.DataFrame:
    """Bangun data transaksi dengan rng berseed — sama di setiap eksekusi."""
    rng = np.random.default_rng(SEED)
    return pd.DataFrame(
        {
            "kode_transaksi": [f"TRX-{i:03d}" for i in range(1, n + 1)],
            "cabang": rng.choice(KOTA, size=n),
            "produk": rng.choice(PRODUK, size=n),
            "jumlah": rng.integers(1, 10, size=n),
            "harga": rng.choice([15_000, 20_000, 30_000], size=n),
        }
    )


def rangkum(df: pd.DataFrame) -> pd.DataFrame:
    """Agregasi: total jumlah per cabang, diurutkan menurut nama cabang."""
    return (
        df.groupby("cabang", as_index=False)["jumlah"]
        .sum()
        .sort_values("cabang")
        .reset_index(drop=True)
    )


print("--- konfigurasi yang dicatat ---")
print("pandas :", pd.__version__)
print("numpy  :", np.__version__)
print("seed   :", SEED)
print("tanggal:", TANGGAL_LAPORAN)
print()
print("--- data transaksi dari rng berseed ---")
print(bangun_transaksi().to_string(index=False))
print()
print("--- pipeline dijalankan dua kali ---")
hasil_1 = rangkum(bangun_transaksi())
hasil_2 = rangkum(bangun_transaksi())
print("hasil_1.equals(hasil_2):", hasil_1.equals(hasil_2))
print(hasil_1.to_string(index=False))
print()
print("--- sampel dengan random_state ---")
sampel = bangun_transaksi().sample(3, random_state=SEED)
print("baris terpilih:", sampel["kode_transaksi"].tolist())
print()
print("--- simpan dan baca kembali ---")
with TemporaryDirectory() as folder:
    jalur = f"{folder}/transaksi.csv"
    bangun_transaksi().to_csv(jalur, index=False)
    baca = pd.read_csv(jalur)
    print("roundtrip equals:", bangun_transaksi().equals(baca))

Output yang Dihasilkan:

text
--- konfigurasi yang dicatat ---
pandas : 3.0.6
numpy  : 2.1.3
seed   : 42
tanggal: 2026-01-15

--- data transaksi dari rng berseed ---
kode_transaksi   cabang produk  jumlah  harga
       TRX-001  Jakarta   Gula       5  30000
       TRX-002    Medan   Gula       2  20000
       TRX-003 Surabaya  Beras       8  20000
       TRX-004  Bandung   Kopi       5  30000
       TRX-005  Bandung  Beras       5  20000
       TRX-006    Medan   Kopi       4  20000
       TRX-007  Jakarta  Beras       2  20000
       TRX-008 Surabaya   Kopi       9  15000

--- pipeline dijalankan dua kali ---
hasil_1.equals(hasil_2): True
  cabang  jumlah
 Bandung      10
 Jakarta       7
   Medan       6
Surabaya      17

--- sampel dengan random_state ---
baris terpilih: ['TRX-002', 'TRX-006', 'TRX-001']

--- simpan dan baca kembali ---
roundtrip equals: True

Mari kita bedah bagian pentingnya:

  1. Konstanta di atas file: SEED = 42 dan TANGGAL_LAPORAN = date(2026, 1, 15) menjadi satu-satunya tempat yang perlu diubah bila skenario berubah. Tanggal laporan ditulis eksplisit — bukan date.today() — sehingga hasil tidak tergantung hari eksekusi.
  2. np.random.default_rng(SEED): dibuat di dalam bangun_transaksi(), sehingga setiap pemanggilan menghasilkan urutan angka yang sama persis: delapan baris transaksi dengan cabang, produk, jumlah, dan harga yang selalu identik pada output.
  3. hasil_1.equals(hasil_2): pipeline dijalankan dua kali dan perbandingannya True. Angka per cabang — Bandung 10, Jakarta 7, Medan 6, Surabaya 17 — kini menjadi pegangan yang sah: menggeser salah satu artinya ada kode atau data yang berubah.
  4. sample(3, random_state=SEED): baris terpilih selalu ['TRX-002', 'TRX-006', 'TRX-001']. Tanpa random_state, baris yang terpilih berbeda setiap eksekusi dan laporan berbasis sampel sulit diperiksa ulang.
  5. Uji simpan-baca kembali: to_csv() ke folder TemporaryDirectory() lalu read_csv() menghasilkan DataFrame yang equals dengan asalnya, termasuk dtype-nya. Folder sementara itu dihapus otomatis saat blok with berakhir, jadi bundle tidak menyimpan artefak.
  6. pd.__version__ dan np.__version__: versi pustaka dicetak di awal. Ketika seorang kolega mendapatkan angka yang berbeda, perbandingan versi adalah pertanyaan pertama — kombinasi pandas 3.0.6 dengan numpy 2.1.3 adalah lingkungan tempat semua contoh course ini diverifikasi.

Daftar Periksa Reproducibilitas #

PilarYang dilakukanAlat
Acak terulangSatu SEED untuk semua sumber acaknp.random.default_rng(), random_state=
Lingkungan tercatatVersi pandas/numpy dicatat bersama hasilpd.__version__, np.__version__
Waktu bekuTanggal laporan eksplisit, bukan hari inidate(2026, 1, 15)
Data uji tetapInput kecil di dalam skrip, bukan file eksternalDataFrame konstruksi + assert_frame_equal()
Roundtrip terujiSimpan-baca kembali menghasilkan objek yang samato_csv() + read_csv() + equals()

Section Pengujian dan Debugging sampai di sini: kita bisa membaca error, melacak index, merge, dan dtype, menguji DataFrame dan transformasinya, memasang gerbang schema, serta mengunci seluruh rangkaian agar dapat direproduksi. Langkah berikutnya adalah menyusun kemampuan itu menjadi struktur yang utuh — section Praktik Data Engineering membuka dengan chapter Membangun Pipeline Data, yang menyusun tahapan extract, transform, dan load menjadi fungsi-fungsi pandas yang terstruktur.

Referensi resmi untuk chapter ini: random sampling NumPy , pandas.DataFrame.sample , dan whatsnew v3.0.0 .