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
assertbentuk 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.
| Tahap | Fungsi | Pertanggungjawaban |
|---|---|---|
| 1. Baca | bacaData | CSV → matriks, lewati header |
| 2. Bersihkan | bersihkanData | buang baris NaN dan di bawah ambang |
| 3. Agregasi | agregasiData | accumarray jumlah per region |
| 4. Tulis | tulisRingkasan | matriks 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:
config = struct( ...
'berkasMasukan', 'transaksi.csv', ...
'berkasKeluaran', 'ringkasan.csv', ...
'nRegion', 3, ...
'nilaiMin', 0);Mari kita bedah baris-baris kode di atas:
- Baris 1 (
config = struct(...)):struct('field', nilai, ...)membangun struct satu tembakan; tiga titik (...) hanya melanjutkan baris, tidak menambah instruksi. - Baris 2-3 (
berkasMasukan,berkasKeluaran): path input dan output menjadi data, bukan teks tertanam di dalam fungsi. - Baris 4 (
nRegion): jumlah grup agregasi — pipeline akan menolak data yang region-nya di luar1..3. - 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:
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);
endMari kita bedah baris-baris kode di atas:
- Baris 2 (
data = bacaData(...)): tahap baca menghasilkan matriksn×3;assertbaris 3 menghentikan pipeline lebih awal bila bentuk kolom salah — gagal cepat lebih murah daripada galat membingungkan di tahap agregasi. - Baris 8 (
bersihkanData(...)): mengembalikan data bersih plus jumlah baris yang dibuang; dua output memudahkan pelaporan. - Baris 11-12 (
assert(all(region ...): kontrak untuk tahap agregasi —accumarrayakan error bila kunci grup di luar jangkauan, jadi pipeline memeriksa lebih dulu dengan pesan yang kita tulis sendiri. - Baris 17-18 (
total,n): dua agregasi dari kunci yang sama — jumlahnilaidan jumlah baris (ones) per region. - Baris 21 (
tulisRingkasan(...)): hasil ditulis ke berkas keluaran sebelum structringkasandikembalikan ke pemanggil.
Fungsi bersihkanData sendiri murni logical indexing — satu baris mask menyeleksi baris valid:
function [dataBersih, nDibuang] = bersihkanData(data, nilaiMin)
valid = isfinite(data(:, 3)) & data(:, 3) >= nilaiMin;
dataBersih = data(valid, :);
nDibuang = sum(~valid);
endBaris 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:
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:
[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 PASSSkrip 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.