Chapter-chapter sebelumnya di section ini memberi kita potongan: pipeline empat tahap (Otomasi Pemrosesan Data), ketahanan galat per berkas (Pemrosesan Banyak File), laporan otomatis, dan checkpoint (Batch Processing). Sekarang kita merakit semuanya menjadi satu sistem analisis end-to-end yang bisa dipercaya: data mentah masuk di satu ujung, laporan dan figur keluar di ujung lain. Pembedanya bukan kecanggihan satu tahap, melainkan disiplin arsitektur — modularisasi, konfigurasi, logging, idempotensi, dan pengujian. Cakupan chapter ini:

  • alur penuh: baca → validasi → bersihkan → analisis → visualisasi → laporan
  • modularisasi: tiap tahap fungsi berkas .m sendiri
  • konfigurasi global: struct cfg dengan nilai default
  • idempotensi: dua kali dijalankan, hasil isequal
  • pengujian: unit test per tahap + uji end-to-end

Merancang Alur End-to-End #

Aturannya: gambar dulu alurnya sebelum menulis kode — tiap kotak menjadi satu tanggung jawab yang bisa diuji sendiri. Mekanisme di balik layar: alur berbentuk rantai lurus dengan validasi di gerbang masuk dan dua keluaran (laporan teks + figur):

TahapBerkas fungsiKeluaran
1-2. Baca + validasijalankanPipeline.m (badan)matriks data + assert
3. BersihkanbersihkanData.mdata valid saja
4. AnalisisanalisisData.mstruct statistik
5. VisualisasijalankanPipeline.m (badan)PNG + laporan
6. LaporanjalankanPipeline.m (subfunction)laporan teks
Ujiuji_pipeline.mPASS/FAIL

Modularisasi: Satu Tahap Satu Berkas #

Aturan modularisasi: tahap yang punya logika nontrivial hidup di berkas fungsinya sendiri — bukan sebagai subfunction raksasa. Mekanismenya: dua tahap inti dipisah sehingga bisa diuji unit tanpa menjalankan seluruh pipeline. bersihkanData.m menyeleksi baris sehat dengan satu mask logis:

matlab
function dataBersih = bersihkanData(data, cfg)
valid = all(isfinite(data), 2) & ...
    data(:, 2) >= cfg.suhuMin & data(:, 2) <= cfg.suhuMaks;
dataBersih = data(valid, :);
end

analisisData.m menghitung statistik ringkas dan tren linear suhu lewat polyfit:

matlab
t       = data(:, 1);
suhu    = data(:, 2);
tekanan = data(:, 3);

p = polyfit(t, suhu, 1);      % tren suhu: slope derajat/jam-waktu

r = struct( ...
    'nBaris',      numel(t), ...
    'suhuMean',    mean(suhu), ...
    'suhuStd',     std(suhu), ...
    'suhuMin',     min(suhu), ...
    'suhuMaks',    max(suhu), ...
    'tekananMean', mean(tekanan), ...
    'trenSuhu',    p(1));

Struktur folder proyeknya ringkas — seluruh pipeline hidup dalam satu chapter bundle:

text
membuat-pipeline-analisis/
    index.md              -> materi ini
    jalankanPipeline.m    -> orkestrator + konfigurasi default
    bersihkanData.m       -> tahap pembersihan
    analisisData.m        -> tahap analisis
    uji_pipeline.m        -> unit test + end-to-end + idempotensi

Konfigurasi Global dan Orkestrasi #

Fungsi jalankanPipeline(cfg) adalah orkestrator: menggabungkan konfigurasi pengguna dengan default, lalu memanggil tahap-tahap secara berurutan. Mekanisme merge: fieldnames(user) di-loop dan tiap field pengguna menimpa default — pengguna cukup menyebut field yang ingin diubah:

matlab
cfgDefault = struct( ...
    'berkasData',    'data_sensor.csv', ...
    'berkasLaporan', 'laporan_analisis.txt', ...
    'berkasFigur',   'pipeline_figur.png', ...
    'suhuMin',       20, ...
    'suhuMaks',      30);
cfg = mergeConfig(cfgDefault, cfg);

fprintf('TAHAP baca        : %s\n', cfg.berkasData);
data = csvread(cfg.berkasData, 1, 0);
assert(isequal(size(data, 2), 3), 'kolom harus 3 (waktu, suhu, tekanan)');
assert(size(data, 1) >= 5, 'data terlalu sedikit untuk dianalisis');

fprintf('TAHAP bersihkan   : buang NaN dan suhu di luar [%g, %g]\n', ...
    cfg.suhuMin, cfg.suhuMaks);
dataBersih = bersihkanData(data, cfg);
assert(size(dataBersih, 1) >= 5, 'data bersih terlalu sedikit');

ringkasan = analisisData(dataBersih);
ringkasan.nBarisMentah = size(data, 1);
ringkasan.nBarisBersih = size(dataBersih, 1);
fprintf('TAHAP analisis    : %d baris bersih\n', ringkasan.nBarisBersih);

fprintf('TAHAP visualisasi : %s\n', cfg.berkasFigur);
figure('visible', 'off');
plot(dataBersih(:, 1), dataBersih(:, 2), '-o');
title('Suhu Sensor terhadap Waktu');
xlabel('waktu'); ylabel('suhu (deg C)');
saveas(gcf, cfg.berkasFigur);
delete(gcf);

fprintf('TAHAP laporan     : %s\n', cfg.berkasLaporan);
tulisLaporan(cfg, ringkasan);

fprintf('PIPELINE selesai: %d -> %d baris, laporan siap\n', ...
    ringkasan.nBarisMentah, ringkasan.nBarisBersih);

Mari kita bedah baris-baris kode di atas:

  1. Baris 1-8 (cfgDefault + mergeConfig): konfigurasi default terpusat di satu tempat; pengguna yang memanggil jalankanPipeline(struct()) pun mendapat perilaku standar yang masuk akal.
  2. Baris 10-13 (baca + dua assert): validasi bentuk data terjadi SEBELUM tahap berat — pipeline gagal cepat dengan pesan yang jelas.
  3. Baris 19-21 (bersihkanData + assert): tahap modular dipanggil satu baris; assert berikutnya menjaga kontrak antar tahap (minimal 5 baris bersih).
  4. Baris 23-26 (analisisData): hasil analisis diperkaya jumlah baris mentah/bersih untuk keperluan logging dan laporan.
  5. Baris 33-39 (visualisasi): figur dibuat headless dan disimpan lewat saveas, lalu delete(gcf) membebaskan memori.
  6. Baris 41-42 (tulisLaporan): subfunction penulis laporan memakai fopen(..., 'w') — menulis dari awal, kunci idempotensi.

Logging tahap memakai label tetap (TAHAP baca, TAHAP bersihkan, …), bukan stempel waktu — output pipeline identik antar-jalankan sehingga bisa dicocokkan otomatis (prinsip yang sama dengan chapter Otomasi Pemrosesan Data).

Pengujian Pipeline: Unit dan End-to-End #

Aturan pengujian berlapis: unit per tahap menangkap galat logika, end-to-end menangkap galat penyambungan. Mekanismenya: skrip uji_pipeline.m membuat data uji 10 baris (satu NaN, satu outlier 999), menguji tiap tahap dengan input kecil yang hasilnya dihitung tangan, lalu menjalankan pipeline penuh dua kali:

matlab
cfgUji = struct('suhuMin', 20, 'suhuMaks', 28);
M = [1 24; 2 NaN; 3 30; 4 25];
hasilBersih = bersihkanData(M, cfgUji);
assert(isequal(hasilBersih, [1 24; 4 25]), 'unit test bersihkanData gagal');
fprintf('UNIT bersihkanData: [4x2] -> [2x2] OK\n');

r = analisisData([1 24 101; 4 25 102]);
assert(abs(r.suhuMean - 24.5) < 1e-12, 'unit test suhuMean gagal');
assert(abs(r.tekananMean - 101.5) < 1e-12, 'unit test tekananMean gagal');
assert(abs(r.trenSuhu - 1/3) < 1e-9, 'unit test trenSuhu gagal');
fprintf('UNIT analisisData : mean 24.5, tren 0.3333 OK\n');

Uji idempotensi menjalankan pipeline dua kali dengan cfg yang sama, membandingkan seluruh struct hasil dengan isequal, lalu memverifikasi angka kunci end-to-end:

matlab
r1 = jalankanPipeline(cfg);
r2 = jalankanPipeline(cfg);
assert(isequal(r1, r2), 'pipeline tidak idempoten');
fprintf('IDEMPOTEN: dua kali jalankan, hasil isequal\n');

assert(r1.nBarisMentah == 10, 'jumlah baris mentah salah');
assert(r1.nBarisBersih == 8, 'jumlah baris bersih salah');
assert(abs(r1.suhuMean - 25.7625) < 1e-9, 'suhuMean salah');
fprintf('END-TO-END: 10 -> 8 baris, suhu mean 25.7625\n');

Output yang Dihasilkan (keseluruhan uji_pipeline.m):

text
UNIT bersihkanData: [4x2] -> [2x2] OK
UNIT analisisData : mean 24.5, tren 0.3333 OK
TAHAP baca        : data_sensor.csv
TAHAP bersihkan   : buang NaN dan suhu di luar [20, 30]
TAHAP analisis    : 8 baris bersih
TAHAP visualisasi : pipeline_figur.png
TAHAP laporan     : laporan_analisis.txt
PIPELINE selesai: 10 -> 8 baris, laporan siap
TAHAP baca        : data_sensor.csv
TAHAP bersihkan   : buang NaN dan suhu di luar [20, 30]
TAHAP analisis    : 8 baris bersih
TAHAP visualisasi : pipeline_figur.png
TAHAP laporan     : laporan_analisis.txt
PIPELINE selesai: 10 -> 8 baris, laporan siap
IDEMPOTEN: dua kali jalankan, hasil isequal
END-TO-END: 10 -> 8 baris, suhu mean 25.7625
BERSIH: 3 berkas uji dihapus, uji pipeline PASS

Dua blok TAHAP ... yang tercetak berurutan bukan kebetulan — itu bukti visual idempotensi: jalankan ulang menghasilkan jejak yang persis sama, termasuk laporan yang ditimpa bersih (mode 'w') dan figur yang ditulis ulang.

Ringkasan #

Pipeline analisis utuh kita terdiri dari orkestrator jalankanPipeline.m (konfigurasi merge + logging tahap + visualisasi + laporan), dua tahap modular bersihkanData.m dan analisisData.m, serta uji_pipeline.m yang menguji unit, end-to-end, dan idempotensi. Data 10 baris dengan satu NaN dan satu outlier menjadi 8 baris bersih dengan suhuMean 25.7625; dua kali jalankan menghasilkan isequal penuh; tiga berkas uji dihapus di akhir. Pola ini siap dipindahkan: ganti tahap analisisData dengan analisis domain kita, tambah tahap baru sebagai berkas fungsi baru — arsitekturnya menahan pertumbuhan.