Semua chapter sebelumnya mengajari kita satu keterampilan sekaligus: membaca berkas, membersihkan data, menghitung agregasi, menulis hasil. Di dunia nyata, pekerjaan tersebut bukan kegiatan sekali-jalan — data penjualan datang tiap minggu, dan kita tidak ingin mengetik ulang dua puluh baris kode setiap kali data baru tiba. Otomasi pemrosesan data menjawab kebutuhan itu: rantai tahap dirangkai menjadi satu fungsi pipeline, parameter dipisahkan ke sebuah struct konfigurasi, dan tiap perpindahan tahap dijaga oleh validasi. Cakupan chapter ini:

  • merancang pipeline baca → bersihkan → agregasi → tulis sebagai fungsi per tahap
  • memisahkan konfigurasi dari kode lewat struct config
  • validasi antar tahap dengan assert bentuk data
  • logging sederhana yang deterministik (label tahap, bukan jam)

Merancang Pipeline sebagai Rantai Fungsi #

Aturan mainnya: satu script panjang yang melakukan semuanya sekaligus sulit diuji dan sulit dipercaya. Mekanisme di balik layar: kita memecah pekerjaan menjadi empat fungsi kecil — bacaData, bersihkanData, agregasiData, tulisRingkasan — lalu fungsi prosesData menjahitnya berurutan. Kalau hasil akhir mencurigakan, kita bisa menguji tiap tahap secara terpisah dan langsung menemukan tahap mana yang bocor.

TahapFungsiPertanggungjawaban
1. BacabacaDataCSV → matriks, lewati header
2. BersihkanbersihkanDatabuang baris NaN dan di bawah ambang
3. AgregasiagregasiDataaccumarray jumlah per region
4. TulistulisRingkasanmatriks hasil → berkas CSV

Memisahkan Konfigurasi dari Kode #

Aturannya: nama berkas, jumlah region, dan ambang nilai adalah parameter, bukan konstanta yang dikubur di dalam logika. Mekanismenya: seluruh parameter dikumpulkan dalam satu struct, lalu struct tersebut menjadi satu-satunya argumen prosesData. Mengganti sumber data tinggal mengganti isi struct — kode pipeline tidak disentuh sama sekali:

matlab
config = struct( ...
    'berkasMasukan',  'transaksi.csv', ...
    'berkasKeluaran', 'ringkasan.csv', ...
    'nRegion',        3, ...
    'nilaiMin',       0);

Mari kita bedah baris-baris kode di atas:

  1. Baris 1 (config = struct(...)): struct('field', nilai, ...) membangun struct satu tembakan; tiga titik (...) hanya melanjutkan baris, tidak menambah instruksi.
  2. Baris 2-3 (berkasMasukan, berkasKeluaran): path input dan output menjadi data, bukan teks tertanam di dalam fungsi.
  3. Baris 4 (nRegion): jumlah grup agregasi — pipeline akan menolak data yang region-nya di luar 1..3.
  4. Baris 5 (nilaiMin): ambang pembersihan; nilai di bawahnya dianggap salah input.

Badan Pipeline dan Validasi Antar Tahap #

Fungsi utama prosesData berisi tubuh pipeline: empat pemanggilan berurutan, diselingi assert yang menjaga kontrak antar tahap. Berikut inti berkas prosesData.m:

matlab
function ringkasan = prosesData(config)
fprintf('[TAHAP-1] bacaData       : mulai\n');
data = bacaData(config.berkasMasukan);
assert(isequal(size(data, 2), 3), 'kolom data harus 3 (region, toko, nilai)');
assert(size(data, 1) >= 1, 'data kosong');

fprintf('[TAHAP-2] bersihkanData  : mulai\n');
[dataBersih, nDibuang] = bersihkanData(data, config.nilaiMin);
region = dataBersih(:, 1);
nilai  = dataBersih(:, 3);
assert(all(region >= 1 & region <= config.nRegion), 'region di luar 1..nRegion');
assert(all(isfinite(nilai)), 'masih ada nilai non-finit setelah pembersihan');

fprintf('[TAHAP-3] agregasiData   : mulai\n');
total = agregasiData(region, nilai, config.nRegion);
n     = agregasiData(region, ones(size(nilai)), config.nRegion);
assert(isequal(size(total, 1), config.nRegion), 'jumlah baris agregasi salah');

fprintf('[TAHAP-4] tulisRingkasan : mulai\n');
tulisRingkasan(config.berkasKeluaran, total, n);
end

Mari kita bedah baris-baris kode di atas:

  1. Baris 2 (data = bacaData(...)): tahap baca menghasilkan matriks n×3; assert baris 3 menghentikan pipeline lebih awal bila bentuk kolom salah — gagal cepat lebih murah daripada galat membingungkan di tahap agregasi.
  2. Baris 8 (bersihkanData(...)): mengembalikan data bersih plus jumlah baris yang dibuang; dua output memudahkan pelaporan.
  3. Baris 11-12 (assert(all(region ...): kontrak untuk tahap agregasi — accumarray akan error bila kunci grup di luar jangkauan, jadi pipeline memeriksa lebih dulu dengan pesan yang kita tulis sendiri.
  4. Baris 17-18 (total, n): dua agregasi dari kunci yang sama — jumlah nilai dan jumlah baris (ones) per region.
  5. Baris 21 (tulisRingkasan(...)): hasil ditulis ke berkas keluaran sebelum struct ringkasan dikembalikan ke pemanggil.

Fungsi bersihkanData sendiri murni logical indexing — satu baris mask menyeleksi baris valid:

matlab
function [dataBersih, nDibuang] = bersihkanData(data, nilaiMin)
valid = isfinite(data(:, 3)) & data(:, 3) >= nilaiMin;
dataBersih = data(valid, :);
nDibuang = sum(~valid);
end

Baris pertama membangun mask isfinite (menangkap NaN sekaligus Inf) DAN ambang minimum; baris kedua menyeleksi baris yang lolos; baris ketiga menghitung korban untuk keperluan logging.

Menjalankan dan Menguji Pipeline #

Berkas uji_prosesData.m membuat data uji delapan baris — satu berisi NaN, satu berisi nilai negatif — lalu menjalankan pipeline dengan konfigurasi di atas dan memverifikasi hasil agregasi dengan assert:

matlab
ringkasan = prosesData(config);

assert(ringkasan.barisValid == 6, 'baris valid salah');
assert(ringkasan.barisDibuang == 2, 'baris dibuang salah');
assert(isequal(ringkasan.total, [520; 395; 120]), 'total per region salah');
assert(isequal(ringkasan.n, [3; 2; 1]), 'jumlah transaksi salah');
fprintf('ASSERT: baris 8 -> 6 valid, 2 dibuang, total 1035\n');

Output yang Dihasilkan:

text
[TAHAP-1] bacaData       : mulai
[TAHAP-2] bersihkanData  : mulai
[TAHAP-3] agregasiData   : mulai
[TAHAP-4] tulisRingkasan : mulai
prosesData selesai: 8 baris masuk, 6 valid, 2 dibuang
ASSERT: baris 8 -> 6 valid, 2 dibuang, total 1035
ASSERT: ringkasan.csv berisi agregasi benar
BERSIH: berkas uji dihapus, uji selesai PASS

Skrip uji juga membaca kembali ringkasan.csv dan mencocokkan isinya ([1 520 3; 2 395 2; 3 120 1]), lalu menghapus semua berkas uji — pipeline teruji tanpa meninggalkan bekas.

Logging Sederhana yang Deterministik #

Aturan logging pipeline: catat tahap yang sedang berjalan, jangan catat jam dinding. Mekanismenya: setiap fprintf('[TAHAP-k] ...') dipasang di badan pipeline, sehingga urutan baris output selalu identik setiap dijalankan. Panggilan seperti fix(clock) memang bisa mencetak waktu — tetapi clock membuat output berubah tiap detik, sehingga uji otomatis dan blok dokumentasi tidak lagi bisa mencocokkan hasil. Label tahap plus jumlah baris (8 baris masuk, 6 valid, 2 dibuang) sudah cukup untuk menjawab dua pertanyaan penting: pipeline sampai tahap mana, dan berapa banyak data yang selamat.

Ringkasan #

Pipeline prosesData(config) merangkai empat fungsi kecil — bacaData, bersihkanData, agregasiData, tulisRingkasan — dengan kontrak assert di tiap sambungan dan logging label tahap yang deterministik. Konfigurasi hidup di satu struct, sehingga sumber data berganti tanpa menyentuh kode. Skrip uji_prosesData.m membuktikan pipeline tersebut benar: 8 baris mentah menjadi 6 baris valid, agregasi [520; 395; 120] cocok dengan hitungan manual, dan berkas uji dibersihkan sampai tuntas.