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
.msendiri - konfigurasi global: struct
cfgdengan 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):
| Tahap | Berkas fungsi | Keluaran |
|---|---|---|
| 1-2. Baca + validasi | jalankanPipeline.m (badan) | matriks data + assert |
| 3. Bersihkan | bersihkanData.m | data valid saja |
| 4. Analisis | analisisData.m | struct statistik |
| 5. Visualisasi | jalankanPipeline.m (badan) | PNG + laporan |
| 6. Laporan | jalankanPipeline.m (subfunction) | laporan teks |
| Uji | uji_pipeline.m | PASS/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:
function dataBersih = bersihkanData(data, cfg)
valid = all(isfinite(data), 2) & ...
data(:, 2) >= cfg.suhuMin & data(:, 2) <= cfg.suhuMaks;
dataBersih = data(valid, :);
endanalisisData.m menghitung statistik ringkas dan tren linear suhu lewat polyfit:
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:
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 + idempotensiKonfigurasi 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:
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:
- Baris 1-8 (
cfgDefault+mergeConfig): konfigurasi default terpusat di satu tempat; pengguna yang memanggiljalankanPipeline(struct())pun mendapat perilaku standar yang masuk akal. - Baris 10-13 (baca + dua
assert): validasi bentuk data terjadi SEBELUM tahap berat — pipeline gagal cepat dengan pesan yang jelas. - Baris 19-21 (
bersihkanData+assert): tahap modular dipanggil satu baris;assertberikutnya menjaga kontrak antar tahap (minimal 5 baris bersih). - Baris 23-26 (
analisisData): hasil analisis diperkaya jumlah baris mentah/bersih untuk keperluan logging dan laporan. - Baris 33-39 (visualisasi): figur dibuat headless dan disimpan lewat
saveas, laludelete(gcf)membebaskan memori. - Baris 41-42 (
tulisLaporan): subfunction penulis laporan memakaifopen(..., '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:
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:
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):
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 PASSDua 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.