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 analisisAlat jawabKeluaran
Bagaimana tren penjualan tiap produk selama 36 bulan — naik, turun, atau berulang?line plot tiga seritren_bulanan.png
Bagaimana sebaran penjualan tiap produk — stabil, lebar, atau berpuncak musiman?histogram per produkdistribusi_penjualan.png
Bagaimana perbandingan total tahunan antar produk beserta variabilitasnya?grouped bar + errorbarperbandingan_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):

text
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:

Produkdasartren/bulanamplitudobulan puncakKarakter
kopi4805.411012 (Des)besar, tumbuh cepat, musiman jelas
teh3002.6656 (Jun)kecil, tumbuh pelan, puncak musim panas
susu4206.82512 (Des)sedang, tumbuh tercepat, hampir stabil

Konversi formula menjadi matriks berlangsung di buatDataset.m. Intinya:

matlab
% 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);
end

Mari kita bedah baris-baris kode di atas:

  1. Baris 2-6 (param = [...]): parameter semua produk hidup dalam satu matriks — menambah produk cukup menambah baris, tanpa menyentuh loop.
  2. Baris 8 (bulanKal = mod(bulan - 1, 12) + 1;): memetakan 1..36 ke bulan kalender 1..12 secara berulang; mod(0,12)+1 menghasilkan 1, mod(35,12)+1 menghasilkan 12.
  3. Baris 9 (M = [bulan, zeros(36, 3)];): matriks 36x4 diawali kolom bulan; tiga kolom produk diisi loop berikutnya.
  4. 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.
  5. Baris 12-14 (M(:, k+1) = round(...)): formula lengkap per produk; round membulatkan ke unit karena penjualan tercatat dalam satuan bulat — sekaligus menghilangkan selisih cos tingkat 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):

matlab
% 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:

text
kopi(6) dari formula = 404, di CSV = 404

Masalah Data yang Disuntikkan #

Setelah matriks bersih terbentuk, lima sel dirobek secara sengaja — tiga sebagai nilai kosong, dua sebagai pencilan dengan penyebab yang masuk akal:

matlab
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:

matlab
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:

text
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: 2

Baris 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:

KolomTipeMaknaRentang nilai wajar
bulanintegerbulan ke- sejak Jan 2022 (1–36)1–36
kopiintegerunit kopi terjual pada bulan itu410–780
tehintegerunit teh terjual246–442
susuintegerunit susu terjual430–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.