Analisis yang baik dimulai jauh sebelum grafik pertama digambar: dari pertanyaan yang ingin dijawab dan data yang sanggup menjawabnya. Pada bagian ini kita menetapkan pertanyaan analisis, menurunkan kriteria dataset darinya, lalu merancang dataset penjualan sintetis yang deterministik — lengkap dengan tiga nilai kosong dan dua pencilan yang disuntikkan sengaja — dan mendokumentasikan struktur kolomnya sebagai kontrak untuk tiga bagian berikutnya.
Yang kita kerjakan pada bagian ini:
- pertanyaan analisis yang spesifik dan bisa dijawab grafik,
- kriteria dataset turunan dari pertanyaan tersebut,
- formula penjualan (tren + musiman + derau) beserta parameternya,
- suntikan masalah data dan dokumentasi struktur kolom,
- diagram pipeline proyek secara keseluruhan.
Pertanyaan Analisis #
Pertanyaan menentukan alat. Kita memilih tiga pertanyaan yang masing-masing dijawab satu jenis grafik:
| Pertanyaan analisis | Alat jawab | Keluaran |
|---|---|---|
| Bagaimana tren penjualan tiap produk selama 36 bulan — naik, turun, atau berulang? | line plot tiga seri | tren_bulanan.png |
| Bagaimana sebaran penjualan tiap produk — stabil, lebar, atau berpuncak musiman? | histogram per produk | distribusi_penjualan.png |
| Bagaimana perbandingan total tahunan antar produk beserta variabilitasnya? | grouped bar + errorbar | perbandingan_tahunan.png |
Ketiganya juga menentukan bentuk laporan akhir: statistik per produk menjawab pertanyaan ketiga secara angka, puncak musiman menjawab pola berulang dari pertanyaan pertama. Satu pertanyaan, satu grafik, satu pesan — prinsip yang kita pakai lagi pada Bagian 3.
Kriteria Dataset #
Dari pertanyaan di atas, dataset harus memenuhi empat kriteria:
- Variabel waktu — satu kolom bulan (1–36) sebagai sumbu-x semua figur dan dasar agregasi tahunan.
- Multi-seri — tiga produk sebagai kolom yang sebanding, agar tren dan perbandingan antar produk terlihat.
- Cukup baris — 36 bulan (3 tahun penuh): cukup panjang untuk tren, cukup banyak (108 observasi produk) untuk histogram yang bermakna, dan genap satu siklus tahunan per tahun.
- Sengaja bermasalah — data nyata hampir selalu memuat lubang dan pencilan; dataset latihan pun harus memuatnya agar tahap pembersihan di Bagian 2 benar-benar bekerja.
Merancang Dataset Sintetis #
Kita bisa mengunduh dataset publik, tetapi proyek ini membangun datanya sendiri lewat formula — keputusan yang dibayar lunas oleh tiga keuntungan: hasil identik di setiap eksekusi sehingga laporan dan test bisa membandingkan angka persis, kompatibel lintas MATLAB/Octave karena tidak bergantung pada generator acak yang beda mesin, dan bebas masalah lisensi karena semua angka buatan kita sendiri.
Model tiap produk menggabungkan tiga komponen yang lazim pada data penjualan: kenaikan lambat (tren), lonjakan tahunan (musiman), dan fluktuasi kecil (derau):
penjualan(t) = dasar + tren * t + amplitudo * cos(2*pi*(m - puncak)/12) + derau(t)dengan t = bulan ke-1..36, m = bulan kalender (1..12), dan derau(t) = mod(13*t, 9) - 4 — pola bulat tetap di rentang -4..+4 yang tampak acak tanpa RNG. Parameter tiap produk dipilih agar ketiganya punya karakter berbeda:
| Produk | dasar | tren/bulan | amplitudo | bulan puncak | Karakter |
|---|---|---|---|---|---|
| kopi | 480 | 5.4 | 110 | 12 (Des) | besar, tumbuh cepat, musiman jelas |
| teh | 300 | 2.6 | 65 | 6 (Jun) | kecil, tumbuh pelan, puncak musim panas |
| susu | 420 | 6.8 | 25 | 12 (Des) | sedang, tumbuh tercepat, hampir stabil |
Konversi formula menjadi matriks berlangsung di buatDataset.m. Intinya:
% parameter formula: [dasar, tren/bulan, amplitudo, bulan puncak]
param = [ ...
480, 5.4, 110, 12; % kopi : puncak Desember (hadiah akhir tahun)
300, 2.6, 65, 6; % teh : puncak Juni (minuman dingin)
420, 6.8, 25, 12]; % susu : puncak Desember
bulan = (1:36)';
bulanKal = mod(bulan - 1, 12) + 1; % 1..12 (Januari..Desember)
M = [bulan, zeros(36, 3)];
for k = 1:3
derau = mod(13 * bulan, 9) - 4; % derau tetap -4..+4
M(:, k + 1) = round(param(k, 1) + param(k, 2) * bulan ...
+ param(k, 3) * cos(2 * pi * (bulanKal - param(k, 4)) / 12) + derau);
endMari kita bedah baris-baris kode di atas:
- Baris 2-6 (
param = [...]): parameter semua produk hidup dalam satu matriks — menambah produk cukup menambah baris, tanpa menyentuh loop. - Baris 8 (
bulanKal = mod(bulan - 1, 12) + 1;): memetakan 1..36 ke bulan kalender 1..12 secara berulang;mod(0,12)+1menghasilkan 1,mod(35,12)+1menghasilkan 12. - Baris 9 (
M = [bulan, zeros(36, 3)];): matriks 36x4 diawali kolom bulan; tiga kolom produk diisi loop berikutnya. - Baris 11 (
derau = mod(13 * bulan, 9) - 4;): karena 13 dan 9 relatif prima,mod(13*t, 9)berputar lewat semua sisa 0..8 dengan periode 9 — derau yang terlihat acak, rata-rata nol, dan sepenuhnya deterministik. - Baris 12-14 (
M(:, k+1) = round(...)): formula lengkap per produk;roundmembulatkan ke unit karena penjualan tercatat dalam satuan bulat — sekaligus menghilangkan selisihcostingkat bit terakhir antar mesin.
Formula ini bisa diverifikasi tangan. Cuplikan berikut menyiapkan dataset, lalu menghitung kopi bulan ke-6 langsung dari parameter dan membandingkannya dengan isi CSV (dikurangi 1900, nilai suntikan pencilan):
% siapkan pembanding dari CSV
pathCsv = buatDataset('latihan');
data = muatData(pathCsv);
% hitung kopi bulan ke-6 langsung dari formula (m = 6, puncak = 12)
dasar = 480; tren = 5.4; amp = 110; puncak = 12;
m = 6;
derau = mod(13 * 6, 9) - 4;
nilai = round(dasar + tren * 6 + amp * cos(2 * pi * (m - puncak) / 12) + derau);
fprintf('kopi(6) dari formula = %d, di CSV = %g\n', ...
nilai, data.penjualan(6, 1) - 1900);Output yang Dihasilkan:
kopi(6) dari formula = 404, di CSV = 404Masalah Data yang Disuntikkan #
Setelah matriks bersih terbentuk, lima sel dirobek secara sengaja — tiga sebagai nilai kosong, dua sebagai pencilan dengan penyebab yang masuk akal:
M(9, 2) = NaN; % kopi bulan ke-9 : laporan hilang
M(22, 3) = NaN; % teh bulan ke-22 : laporan hilang
M(30, 4) = NaN; % susu bulan ke-30 : laporan hilang
M(6, 2) = M(6, 2) + 1900; % kopi bulan ke-6 : entri terduplikasi (spike)
M(27, 3) = M(27, 3) - 280; % teh bulan ke-27 : titik desimal salah (dip)Penyebabnya penting, bukan hiasan: laporan hilang adalah NaN yang harus diimputasi, entri terduplikasi menjelaskan nilai 2304 yang jauh di atas rentang wajar, dan titik desimal salah menjelaskan nilai 86 yang jauh di bawahnya. Dua jenis pencilan (ke atas dan ke bawah) kita uji keduanya di Bagian 2.
Jalankan modulnya, lalu intip isi CSV dan pindai masalahnya — muatData dibedah penuh pada Bagian 2, di sini kita cukup memakai laporannya:
pathCsv = buatDataset('latihan');
fprintf('dataset : %s\n', pathCsv);
% lihat tujuh baris pertama berkas
fid = fopen(pathCsv);
for k = 1:7
fprintf('%s\n', fgetl(fid));
end
fclose(fid);
% pindai masalah data
data = muatData(pathCsv);
fprintf('baris : %d\n', data.jumlahBaris);
fprintf('NaN per produk : %d %d %d\n', data.hilangPerKolom);
fprintf('kandidat outlier: %d\n', data.jumlahOutlier);Output yang Dihasilkan:
dataset : latihan/data/penjualan.csv
bulan,kopi,teh,susu
1,581,246,448
2,550,277,450
3,495,307,439
4,450,346,438
5,410,367,430
6,2304,383,438
baris : 36
NaN per produk : 1 1 1
kandidat outlier: 2Baris keenam memperlihatkan spike 2304 tepat di tempatnya; tiga NaN tidak terlihat pada tujuh baris pertama ini karena berada di baris 9, 22, dan 30 — laporan pindai mengonfirmasi keduanya: satu NaN per produk dan dua kandidat pencilan.
Dokumentasi Struktur Kolom #
Struktur kolom ini adalah kontrak yang dipakai muatData, bersihkanData, dan seluruh figur — simpan di kepala bersama laporan:
| Kolom | Tipe | Makna | Rentang nilai wajar |
|---|---|---|---|
bulan | integer | bulan ke- sejak Jan 2022 (1–36) | 1–36 |
kopi | integer | unit kopi terjual pada bulan itu | 410–780 |
teh | integer | unit teh terjual | 246–442 |
susu | integer | unit susu terjual | 430–686 |
Tahun dapat diturunkan tanpa kolom tambahan: tahun = 2022 + floor((bulan-1)/12), dan bulan kalender dari mod(bulan-1,12)+1 — keduanya dipakai hitungStatistik pada Bagian 4.
Diagram Pipeline Proyek #
Seluruh proyek mengalir lewat enam modul yang dirangkai jalankanPipeline:
Data mengalir sebagai struct dari tahap ke tahap: buatDataset mengembalikan path CSV, muatData mengembalikan struct data mentah beserta laporan masalahnya, bersihkanData mengembalikan struct data bersih, dan hitungStatistik mengembalikan struct statistik yang dimakan figur dan laporan. Setiap panah adalah kontrak antarmuka yang diuji oleh tests/uji_pipeline.m.
Dataset dan kontraknya siap. Bagian 2 membaca CSV itu kembali ke memori — dengan guard yang menghubungkan MATLAB modern dan Octave — lalu membersihkan tiga NaN dan dua pencilan dengan metode yang bisa dipertanggungjawabkan.