UTS Pengantar Big Data
Studi Kasus Aplikasi Dompet Digital BayarYuk
November 05, 2026
1 Identitas dan Judul Dokumen
| Keterangan | Isi |
|---|---|
| Mata kuliah | Pengantar Big Data |
| Nama | Alya Maura Raditha |
| NIM | 52240003 |
| Program studi | Sains Data, Institut Teknologi Sains Bandung (ITSB) |
| Topik | Studi kasus aplikasi dompet digital BayarYuk |
| Teknologi | Python, PySpark (Apache Spark), MongoDB, R Markdown |
2 Pendahuluan
Big Data tidak hanya berarti data yang besar. Istilah ini merujuk pada data yang volumenya, kecepatan datangnya, dan ragam bentuknya sudah melampaui kemampuan satu mesin atau satu basis data relasional. Karena itu satu analisis sering harus menggabungkan beberapa teknologi, dan dokumen ini memperlihatkan hal tersebut dalam satu alur kerja:
- MongoDB menyimpan data merchant yang strukturnya fleksibel (schema-flexible).
- Apache Spark (RDD) mengolah log transaksi berukuran 400.000 baris yang sebagian barisnya rusak.
- Spark DataFrame dan Spark SQL mengintegrasikan data merchant dengan transaksi dan menghasilkan agregasi.
- Spark Structured Streaming meng-ingest transaksi baru dari landing zone secara bertahap dan menyimpannya ke format Parquet.
- Exploratory Data Analysis (EDA) pada data skala besar menghasilkan insight untuk keputusan bisnis.
Dokumen ini disusun dalam urutan pengerjaan: konsep dasar, lalu implementasi C1 sampai C5. Setiap bagian implementasi memuat tujuan, implementasi, serta hasil dan interpretasi. Seluruh angka pada dokumen ini keluar dari eksekusi kode saat dokumen di-knit, bukan diketik manual.
3 Deskripsi Studi Kasus BayarYuk
BayarYuk adalah aplikasi dompet digital dengan beragam aktivitas transaksi. Tim data BayarYuk diminta menganalisis tiga sumber data yang karakteristiknya berbeda, yaitu data merchant, log transaksi, dan aliran transaksi baru (streaming). Tujuannya:
- mengolah data dari berbagai sumber;
- menangani masalah kualitas data (nilai sistem error, nilai ekstrem, dan baris rusak);
- mengagregasi transaksi dan mengintegrasikannya dengan data merchant;
- membangun proses ingestion yang tahan gagal (fault tolerant);
- menghasilkan insight yang mendukung keputusan bisnis.
4 Deskripsi Sumber Data dan Karakteristik Dataset
| Sumber | Wadah | Karakteristik | Dipakai pada |
|---|---|---|---|
| A. Merchant | MongoDB, db["merchant"] |
200 dokumen; _id, nama, kategori (5 jenis), kota (5 kota), rating, promo (array 0–2 elemen); sekitar 30% memiliki jam_buka |
C1, C3 |
| B. Log transaksi | File teks, variabel jalur_log |
400.000 baris, pemisah \|: waktu\|id_trx\|id_user\|id_merchant\|nominal\|status; ada nominal -1, nominal Rp250.000.000, dan baris rusak pada setiap baris ke-5.000 |
C2, C3, C5 |
| C. Landing zone | Folder berisi 3 file JSON | 3 file × 500 event = 1.500 event; id_trx, id_merchant, nominal, waktu; sekitar 3% nominal bernilai null |
C4 |
4.1 Persiapan lingkungan dan pembangkitan dataset
Pada tugas ini tidak ada file kode awal, sehingga seluruh dataset dibangkitkan sendiri oleh kode di bawah ini sesuai spesifikasi di atas. Semua proses acak memakai seed tetap (SEED = 42), sehingga hasilnya sama setiap kali dokumen dijalankan ulang. Dataset ini bersifat simulasi: pola di dalamnya (misalnya jam ramai atau merchant yang sering gagal) dirancang oleh pembangkit data, sehingga kesimpulan analisis berlaku untuk data simulasi ini saja.
4.1.1 Konfigurasi yang perlu disesuaikan
| Konfigurasi | Nilai bawaan | Cara menyesuaikan |
|---|---|---|
| Python | python3 pada PATH |
set BAYARYUK_PYTHON (lihat chunk setup) |
| Alamat MongoDB | mongodb://localhost:27017 |
set variabel lingkungan MONGO_URI (contoh MongoDB Atlas: mongodb+srv://user:pass@cluster/...) |
| Folder data | data_bayaryuk/ di samping file .Rmd |
set BAYARYUK_DATA_DIR |
| Java | JDK 17 atau 21 (JAVA_HOME terpasang) | wajib untuk Spark; cek dengan java -version |
| Versi Spark | mengikuti paket pyspark terpasang |
pip install pyspark pymongo numpy pandas matplotlib |
| Windows | perlu winutils.exe dan HADOOP_HOME untuk menulis Parquet |
atau jalankan lewat WSL2 |
4.1.2 Dataset A: merchant (MongoDB)
## {'ok': 1.0}
## Terhubung ke MongoDB 9.0.4 | dokumen merchant: 200
4.2 Verifikasi dataset hasil pembangkitan
Sebelum analisis, setiap karakteristik pada spesifikasi diperiksa ulang secara independen: log dan JSON dibaca dengan Python murni (bukan Spark) dan MongoDB dihitung dengan query-nya sendiri.
## == A. MERCHANT (MongoDB) ==
## Jumlah dokumen : 200
## Kategori distinct : 5 ['Elektronik', 'Fashion', 'Kesehatan', 'Kuliner', 'Transportasi']
## Kota distinct : 5 ['Bandung', 'Jakarta', 'Medan', 'Surabaya', 'Yogyakarta']
## Memiliki jam_buka : 60 (30%)
## Panjang array promo : {0: 55, 1: 68, 2: 77}
## Contoh dokumen : {'_id': 'M003', 'nama': 'Klinik Sentosa 003', 'kategori': 'Kesehatan', 'kota': 'Jakarta', 'rating': 3.6, 'promo': [], 'jam_buka': {'buka': '08:00', 'tutup': '22:00'}}
##
## == B. LOG TRANSAKSI ==
## Jumlah baris : 400000
## 3 baris pertama :
## 2026-10-01 00:00:14|TRX0000001|U17587|M130|53500|PENDING
## 2026-10-01 00:00:35|TRX0000002|U00910|M126|76000|SUKSES
## 2026-10-01 00:01:10|TRX0000003|U19809|M012|72500|SUKSES
## Baris rusak : 80 | semuanya kelipatan 5.000: True
## Contoh baris rusak : ['ERR|TRX0005000', '2026-10-01 19:21:29|TRX0010000|U11817', '2026-10-02 07:33:39|TRX0015000|U00842|M146|abc|SUKSES', '###CORRUPTED###']
## Nominal = -1 : 1200
## Nominal = 250.000.000 : 150
## Distribusi status : {'SUKSES': 360388, 'GAGAL': 23843, 'PENDING': 15689}
##
## == C. LANDING ZONE ==
## transaksi_stream_1.json: 500 event, nominal null = 6
## transaksi_stream_2.json: 500 event, nominal null = 15
## transaksi_stream_3.json: 500 event, nominal null = 24
## Total event: 1500 | nominal null: 45 (3.0%)
5 Jawaban Pertanyaan Konseptual
5.1 Bagian A – Pilihan Ganda
A1. Jawaban: b. Velocity. Alasan: Dua juta pengemudi yang mengirim posisi setiap 3 detik menghasilkan sekitar 667 ribu event per detik, sehingga ciri yang paling menonjol adalah laju kedatangan data, bukan ragam format (seluruhnya data GPS seragam), keandalan, atau nilai. Konsep: 5V terdiri dari Volume (besarnya data), Velocity (kecepatan data dibuat dan harus diproses), Variety (ragam format), Veracity (kualitas dan keandalan), dan Value (manfaat bagi bisnis). Velocity yang tinggi menuntut pemrosesan aliran (stream processing) seperti Kafka dan Spark Structured Streaming, bukan pemrosesan batch berkala.
A2. Jawaban: c. Redis. Alasan: Sesi login adalah pasangan kunci–nilai berumur pendek yang sering dibaca dan ditulis dengan latensi sangat rendah, dan Redis menyediakan kedaluwarsa otomatis (TTL) bawaan pada setiap kunci. Konsep: Redis adalah penyimpanan key-value di memori. MongoDB juga memiliki TTL index, tetapi penghapusan dokumennya dijalankan proses latar belakang secara berkala dan datanya berbasis disk, sehingga kurang pas untuk sesi. Neo4j adalah basis data graf untuk relasi, dan HDFS adalah sistem berkas terdistribusi, bukan basis data dengan TTL.
A3. Jawaban: c. $unwind.
Alasan: $unwind memecah field array sehingga setiap elemen menjadi dokumen tersendiri, sedangkan $match menyaring, $group mengelompokkan, dan $project memilih atau membentuk field.
Konsep: Pada data merchant di atas, {"$unwind": "$promo"} mengubah merchant berpromo [cashback, diskon] menjadi dua dokumen, sehingga $group berikutnya dapat menghitung jumlah merchant per jenis promo. Secara bawaan merchant dengan array kosong ikut terbuang, kecuali opsi preserveNullAndEmptyArrays: true dipakai.
A4. Jawaban: b. 6 block, 2.100 MB. Alasan: 700 MB ÷ 128 MB = 5,47 sehingga dibutuhkan 6 block (lima block penuh 640 MB dan satu block terakhir 60 MB). Dengan replikasi 3, total data tersimpan adalah 700 × 3 = 2.100 MB, dan jumlah replika block menjadi 6 × 3 = 18. Konsep: Ukuran block HDFS adalah batas atas, bukan ukuran tetap. Block terakhir hanya memakan ruang sebesar isinya (60 MB), tidak dicadangkan penuh 128 MB. Pilihan c (2.304 MB = 6 × 128 × 3) keliru karena mengasumsikan block terakhir terisi penuh, dan pilihan d keliru dengan alasan yang sama, sedangkan 18 adalah jumlah replika, bukan jumlah block logis.
A5. Jawaban: b. NameNode. Alasan: NameNode menyimpan metadata sistem berkas (struktur direktori, izin, dan pemetaan berkas ke block) serta mengetahui DataNode mana yang memegang tiap block dari laporan block yang dikirim DataNode. Konsep: DataNode menyimpan isi block, bukan metadata. Secondary NameNode bukan cadangan siaga, melainkan pembantu yang menggabungkan fsimage dan edit log secara berkala. ResourceManager adalah komponen YARN untuk mengelola sumber daya komputasi, bukan komponen penyimpanan HDFS.
A6. Jawaban: d. count().
Alasan: count() memaksa Spark menjalankan seluruh rencana komputasi dan mengembalikan hasil ke driver, sedangkan map(), filter(), dan reduceByKey() hanya mendefinisikan transformasi baru dan tidak langsung dieksekusi.
Konsep: Spark bersifat lazy. Transformation (termasuk reduceByKey yang memicu shuffle) hanya menambah rencana, sedangkan action (count, collect, take, saveAsTextFile, write) memicu eksekusi.
A7. Jawaban: b. reduceByKey().
Alasan: reduceByKey menjumlahkan nilai di tiap partisi lebih dulu sebelum shuffle (map-side combine), sehingga data yang dikirim antar node jauh lebih sedikit dibanding groupByKey yang mengirim semua nilai.
Konsep: collect() membawa seluruh data ke memori driver dan berisiko out of memory, sedangkan sortByKey() mengurutkan dan tidak menjumlahkan. Pendekatan inilah yang dipakai pada C2.
A8. Jawaban: b.
Alasan: Explicit schema menghindari pemindaian tambahan yang dibutuhkan inferSchema untuk menebak tipe, mencegah salah tebak (misalnya ID 007 terbaca sebagai angka, atau satu nilai kotor mengubah seluruh kolom menjadi string), dan berfungsi sebagai kontrak yang terdokumentasi.
Konsep: Pada pipeline produksi, skema yang berubah diam-diam antar eksekusi dapat merusak proses hilir, sedangkan skema eksplisit membuat kesalahan terdeteksi sejak awal. Pilihan a keliru karena skema tidak memengaruhi kompresi. Pilihan c keliru karena CSV tetap dapat dibaca tanpa skema. Pilihan d keliru karena Parquet menyimpan skemanya sendiri, sehingga tidak wajib.
A9. Jawaban: b. 4. Alasan: Dalam satu consumer group, setiap partisi hanya boleh dibaca oleh satu consumer, sehingga jumlah consumer aktif paling banyak sama dengan jumlah partisi, yaitu 4. Dua consumer sisanya menganggur (siaga bila ada consumer lain gagal). Konsep: Partisi adalah satuan paralelisme Kafka. Untuk menambah throughput konsumsi, jumlah partisi yang perlu ditambah, bukan hanya jumlah consumer.
A10. Jawaban: c.
Alasan: Dua miliar baris tidak muat di memori driver, sehingga toPandas() dan collect() akan gagal, sedangkan take(10) hanya memberi sampel kecil yang tidak mewakili distribusi. Pendekatan yang benar adalah membagi nilai ke dalam interval (bin) dan menghitungnya di Spark, lalu memvisualisasikan hasil agregasi yang kecil.
Konsep: Prinsip “agregasi dulu di klaster, visualisasi kemudian di driver”. Contohnya groupBy(floor(jarak_km / lebar_bin)).count() menghasilkan puluhan baris saja. Prinsip yang sama dipakai pada grafik per jam di C5.
5.2 Bagian B – Esai
5.2.1 B1. Scale-up vs scale-out dan mahalnya shuffle
Perbedaan. Scale-up (vertikal) menambah kekuatan satu mesin, misalnya CPU, RAM, dan disk yang lebih besar. Scale-out (horizontal) menambah jumlah mesin biasa yang bekerja bersama dalam satu klaster.
| Aspek | Scale-up | Scale-out |
|---|---|---|
| Batas pertumbuhan | Dibatasi spesifikasi tertinggi satu mesin | Praktis dapat terus ditambah node |
| Biaya | Naik tajam (tidak linear) pada spesifikasi tinggi | Cenderung linear dengan perangkat standar |
| Ketahanan | Satu mesin = satu titik kegagalan (single point of failure) | Kegagalan satu node dapat diatasi node lain |
| Kompleksitas perangkat lunak | Rendah, aplikasi biasa tetap berjalan | Tinggi: data harus dipartisi dan kegagalan harus ditangani |
| Contoh | Meningkatkan server PostgreSQL tunggal | Klaster HDFS dan Spark, MongoDB sharded |
Big Data cenderung memilih scale-out karena volume data melampaui batas satu mesin, biaya mesin sangat besar tidak masuk akal, dan perlu toleransi kegagalan. Harga yang dibayar adalah kompleksitas, yang justru diselesaikan oleh kerangka seperti HDFS (penyimpanan terdistribusi dan replikasi) serta MapReduce dan Spark (komputasi terdistribusi).
Mengapa shuffle mahal. Shuffle adalah pengelompokan ulang data antar partisi dan antar node berdasarkan key, yang diperlukan oleh operasi seperti groupByKey, reduceByKey, join, dan repartition. Biayanya berlapis:
- Jaringan. Setiap tugas map dapat mengirim data ke banyak tugas reduce (pola semua-ke-semua), dan jaringan jauh lebih lambat daripada memori.
- Disk. Keluaran map ditulis ke disk lokal (pada MapReduce dan juga pada berkas shuffle Spark) agar dapat diambil reducer dan aman bila terjadi kegagalan.
- Serialisasi dan pengurutan/penggabungan. Data harus diubah menjadi byte dan dikembalikan, lalu diurutkan atau digabung per key, yang memakan CPU dan memori (dapat spill ke disk).
- Sinkronisasi. Tahap berikutnya baru dapat mulai setelah seluruh tahap sebelumnya selesai, sehingga satu tugas lambat (misalnya karena data skew: satu key sangat dominan) menahan seluruh pekerjaan.
Karena itu praktik yang baik adalah mengurangi atau menghindari shuffle: memakai reduceByKey (penggabungan lokal dulu), broadcast join untuk tabel kecil, menyaring kolom dan baris sebelum shuffle, dan menyetel jumlah partisi. Seluruh praktik ini diterapkan pada C2 dan C3.
5.2.2 B2. Pilihan database NoSQL untuk marketplace
| Kebutuhan | Jenis NoSQL | Contoh |
|---|---|---|
| 1. Katalog produk dengan atribut berbeda per kategori | Document store | MongoDB |
| 2. Keranjang belanja sementara | Key-value store | Redis |
| 3. Rekomendasi “friends also bought” | Graph database | Neo4j |
- Katalog produk: document store. Laptop memiliki atribut seperti RAM dan prosesor, kaos memiliki ukuran dan warna, dan buku memiliki penulis dan ISBN. Dokumen JSON fleksibel menyimpan atribut berbeda dalam koleksi yang sama tanpa kolom
NULLatau tabel yang terus diubah. Array dan objek bersarang (misalnya daftar varian) dapat disimpan dalam satu dokumen, dan field dapat diindeks untuk pencarian. Fleksibilitas yang sama terlihat pada fieldjam_bukamerchant BayarYuk. - Keranjang belanja: key-value store. Akses selalu berdasarkan satu kunci (ID pengguna atau sesi), isinya sementara, dan harus cepat dibaca serta ditulis. Redis menyimpan data di memori dengan latensi sangat rendah dan TTL otomatis untuk menghapus keranjang yang ditinggalkan. Jika keranjang tidak boleh hilang saat restart, opsi persistensi (RDB atau AOF) dapat diaktifkan.
- Rekomendasi “friends also bought”: graph database. Kueri ini menelusuri relasi berantai: pengguna → teman → produk yang dibeli teman. Pada graph database, penelusuran relasi adalah operasi utama dan relasi disimpan langsung sebagai penghubung antar simpul, sehingga biaya penelusuran tidak membengkak seiring kedalaman. Pada basis data relasional, kueri yang sama membutuhkan banyak
JOINbertingkat yang makin lambat pada data besar.
Memakai beberapa jenis database sesuai kebutuhan seperti ini disebut polyglot persistence.
5.2.3 B3. Lazy evaluation dan lineage di Apache Spark
Lazy evaluation. Transformation seperti map, filter, dan join tidak langsung dijalankan. Spark hanya mencatatnya sebagai rencana (DAG), dan baru mengeksekusinya ketika ada action seperti count, collect, atau write. Pada C2, baris rdd_mentah.flatMap(parse_baris) belum membaca file sama sekali sampai count() dipanggil.
Lineage. Setiap RDD atau DataFrame mengingat dari mana ia berasal: RDD induk dan transformation yang membentuknya. Rantai riwayat ini disebut lineage, dan merupakan bentuk konkret dari DAG yang dibangun oleh lazy evaluation.
Manfaat dalam pengolahan terdistribusi:
- Optimasi. Karena seluruh rencana diketahui sebelum dijalankan, Spark dapat menggabungkan transformation sempit (
map,filter) ke dalam satu tahap tanpa menyimpan hasil antara, menentukan batas shuffle, dan (pada DataFrame lewat Catalyst) menerapkan predicate pushdown dan column pruning. - Menghemat komputasi dan memori. Pekerjaan yang tidak dibutuhkan tidak dikerjakan; misalnya
take(5)hanya menghitung partisi yang diperlukan. - Toleransi kegagalan tanpa replikasi data. Bila satu node gagal dan sebuah partisi hilang, Spark menghitung ulang hanya partisi itu dari sumber menurut lineage, tanpa menyimpan salinan setiap hasil antara. Ini jauh lebih murah daripada replikasi data perantara. Untuk lineage yang sangat panjang atau hasil yang dipakai berulang,
cache()atau checkpoint dipakai agar pemulihan tidak mengulang dari awal.
Keduanya saling melengkapi: lazy evaluation membangun rencana dan lineage memungkinkan rencana itu dijalankan ulang secara selektif saat terjadi kegagalan.
5.2.4 B4. At-most-once vs at-least-once dan peran checkpoint
| Aspek | At-most-once | At-least-once |
|---|---|---|
| Jaminan | Pesan diproses 0 atau 1 kali | Pesan diproses 1 kali atau lebih |
| Risiko | Data hilang, tanpa duplikat | Duplikat, tanpa kehilangan |
| Mekanisme | Tanpa percobaan ulang; offset dicatat sebelum diproses | Dicoba ulang sampai berhasil; offset dicatat setelah diproses |
| Contoh kegagalan | Crash setelah offset dicatat tetapi sebelum diproses: pesan tidak pernah diproses | Crash setelah diproses tetapi sebelum offset dicatat: pesan diproses lagi |
Peran checkpoint. Checkpoint pada Structured Streaming menyimpan offset dan status kemajuan di lokasi yang tahan gagal. Sebelum sebuah micro-batch diproses, rentang offset-nya dicatat (write-ahead log), dan setelah batch selesai dicatat sebagai commit.
- Mencegah kehilangan data. Bila sistem mati di tengah batch (offset tercatat tetapi belum di-commit), setelah restart Spark memproses ulang persis rentang offset yang sama, sehingga tidak ada data yang terlewat. Ini memerlukan sumber yang dapat dibaca ulang (file atau Kafka).
- Mencegah pemrosesan berulang. Bila batch sudah di-commit, setelah restart Spark melanjutkan dari offset berikutnya dan tidak membaca ulang data lama. Hal ini terbukti pada C4: menjalankan ulang query dengan checkpoint yang sama menghasilkan nol batch baru dan jumlah baris Parquet tidak berubah.
Namun, pemrosesan ulang batch yang gagal berarti checkpoint saja setara at-least-once pada sisi keluaran. Untuk mendapatkan efek exactly-once (hasil akhir sama seperti diproses tepat satu kali), keluaran harus idempoten. Pada C4 hal ini dicapai dengan menulis setiap batch ke subfolder batch_id=<n> dengan mode overwrite, sehingga pemutaran ulang batch yang sama menimpa hasil lama dan tidak menggandakannya.
6 C1 – Kueri dan Agregasi NoSQL
6.1 Tujuan
Memanfaatkan fleksibilitas dokumen MongoDB untuk (a) menyaring merchant dengan beberapa syarat sekaligus, termasuk syarat pada field array (promo), (b) menghitung dokumen yang memiliki field opsional (jam_buka), dan (c) merangkum data per kategori dengan aggregation pipeline.
6.2 Implementasi
## C1(a) Jumlah merchant yang memenuhi seluruh kondisi: 2
## nama rating promo
## Dapur Mandiri 193 4.2 [gratis_ongkir, cashback]
## Warung Makmur 099 4.2 [cashback, gratis_ongkir]
##
## C1(b) Merchant yang memiliki field jam_buka: 60 dari 200
##
## C1(c) Agregasi per kategori:
## kategori jumlah_merchant rata_rata_rating simpangan_baku
## Transportasi 43 4.044 0.566
## Elektronik 37 4.032 0.552
## Kesehatan 38 3.987 0.637
## Kuliner 37 3.959 0.557
## Fashion 45 3.853 0.591
Fungsi query dan pipeline yang dipakai
find(filter, proyeksi)menyaring dokumen. Beberapa pasangan field–nilai dalam satu filter digabung dengan logika AND. Syarat"promo": "cashback"pada field array berarti “array promo mengandung elemencashback”, tanpa perlu$inatau$elemMatch. Operator$gtedipakai untuk rating minimal 4,0. Proyeksi membatasi field yang dikirim ke klien.count_documents({"jam_buka": {"$exists": True}})menghitung dokumen yang memiliki field tersebut. Ini bekerja karena MongoDB berskema fleksibel: dokumen tanpajam_bukatidak dianggap salah, hanya tidak memiliki field itu.- Pipeline
$grouplalu$sortmemproses dokumen secara bertahap.$groupmengelompokkan menurut$kategorilalu menghitung$sum: 1(jumlah dokumen) dan$avg(rata-rata rating);$stdDevSampditambahkan sebagai pelengkap untuk menilai arti selisih rata-rata.$sortdengan nilai-1mengurutkan menurun menurut rata-rata rating.
6.3 Hasil dan interpretasi
C1(a). Hanya 2 dari 200 merchant yang memenuhi keempat syarat sekaligus: Dapur Mandiri 193 dan Warung Makmur 099, keduanya berating 4,2. Hasil yang sedikit ini wajar: 200 merchant tersebar pada 25 kombinasi kota–kategori (rata-rata 8 merchant per kombinasi), lalu masih disaring oleh syarat promo dan rating. Bagi BayarYuk, daftar ini adalah kandidat paling kuat untuk kampanye cashback kuliner di Bandung karena sudah berating baik dan sudah memiliki promo tersebut.
C1(b). Sebanyak 60 dari 200 merchant (30%) memiliki field jam_buka. Di basis data relasional, field yang hanya terisi sebagian akan menjadi kolom berisi NULL untuk 70% baris. Di MongoDB, dokumen yang tidak memiliki field itu cukup tidak menyimpannya. Konsekuensinya bagi aplikasi: jam operasional hanya diketahui untuk sebagian merchant, sehingga fitur seperti “sedang buka” harus menangani ketiadaan field tersebut.
C1(c). Rata-rata rating tertinggi dimiliki kategori Transportasi (4,044) dan terendah Fashion (3,853). Selisih keduanya hanya 0,19 poin, sedangkan simpangan baku rating di dalam satu kategori berkisar 0,55 sampai 0,64. Dengan hanya 37–45 merchant per kategori, urutan ini bersifat deskriptif dan belum cukup untuk menyimpulkan bahwa satu kategori lebih berkualitas daripada kategori lain. Kategori Fashion memiliki merchant terbanyak (45) sekaligus rata-rata terendah, sehingga layak dipantau.
7 C2 – Pemrosesan Log dengan Spark RDD
7.1 Tujuan
Mengolah 400.000 baris log teks secara terdistribusi: membaca, mem-parsing menjadi tuple bertipe, membuang baris rusak sambil menghitungnya dengan accumulator, lalu menghitung agregasi (jumlah per status dan tingkat kegagalan per merchant) dengan reduceByKey tanpa menarik data mentah ke driver.
7.2 Implementasi
## ============================================================
## HASIL PEMROSESAN LOG TRANSAKSI
## ============================================================
## Baris mentah (textFile) : 400,000
## Baris valid hasil parsing : 399,920
## Baris rusak (accumulator) : 80
## Persentase baris rusak : 0.02%
##
## Contoh tuple hasil parsing:
## (datetime.datetime(2026, 10, 1, 0, 0, 14, tzinfo=datetime.timezone.utc), 'TRX0000001', 'U17587', 'M130', 53500, 'PENDING')
##
## ============================================================
## DISTRIBUSI STATUS TRANSAKSI
## ============================================================
## SUKSES : 360,388 (90.12%)
## GAGAL : 23,843 (5.96%)
## PENDING : 15,689 (3.92%)
##
## Tingkat kegagalan keseluruhan: 5.96%
##
## ============================================================
## 5 MERCHANT DENGAN TINGKAT KEGAGALAN TERTINGGI
## ============================================================
## id_merchant nama total_transaksi jumlah_gagal tingkat_gagal_%
## M186 Gerai Makmur 186 2135 605 28.34
## M141 Klinik Berkah 141 1607 425 26.45
## M146 Apotek Jaya 146 1682 435 25.86
## M151 Gadget Berkah 151 1954 497 25.44
## M010 Ojek Prima 010 2301 522 22.69
##
## ============================================================
## KONTRIBUSI TOP 5 MERCHANT
## ============================================================
## Total transaksi top 5 : 9,679
## Total transaksi gagal top 5 : 2,484
## Porsi transaksi top 5 : 2.42%
## Porsi kegagalan top 5 : 10.42%
##
## ============================================================
## RINGKASAN AKHIR C2
## ============================================================
## Jumlah transaksi mentah : 400,000
## Jumlah transaksi valid : 399,920
## Jumlah transaksi rusak : 80
## Jumlah transaksi sukses : 360,388
## Jumlah transaksi gagal : 23,843
## Jumlah transaksi pending : 15,689
## Tingkat kegagalan global : 5.96%
## Jumlah seluruh merchant : 200
## Merchant dengan >= 1.500 trx : 168
##
## C2 selesai dijalankan.
Catatan teknis. flatMap dipilih karena satu baris dapat menghasilkan nol (baris rusak) atau satu tuple, sedangkan map selalu menghasilkan tepat satu. Accumulator hanya dapat dipercaya jika dibaca di driver setelah action selesai. Pembaruan di dalam transformation dapat terhitung ganda bila tahap tersebut dihitung ulang, sehingga RDD di-cache dan hanya satu action pertama (count) yang menjalankan parsing. Agregasi memakai reduceByKey karena penjumlahan dilakukan di tiap partisi sebelum shuffle (map-side combine), sedangkan groupByKey mengirim seluruh nilai lintas jaringan. Satu-satunya data yang dibawa ke driver adalah tiga baris status, lima baris top-5, dan dua hitungan.
7.3 Hasil dan interpretasi
Kualitas parsing. Dari 400.000 baris mentah, 80 baris dibuang oleh accumulator (0,020%), sehingga 399.920 baris valid tersisa. Angka 80 sama persis dengan 400.000 ÷ 5.000, sesuai ketentuan bahwa setiap baris ke-5.000 rusak, jadi tidak ada baris rusak yang lolos dan tidak ada baris sehat yang terbuang. Kehilangan data sekecil ini tidak mengubah hasil analisis secara berarti.
Status transaksi. SUKSES 360.388 (90,12%), GAGAL 23.843 (5,96%), dan PENDING 15.689 (3,92%). Satu dari kira-kira 17 transaksi berakhir gagal. Catatan: status GAGAL di sini mencakup juga transaksi bernominal -1 (kesalahan sistem), sehingga angka ini belum memisahkan kegagalan bisnis dari kegagalan sistem.
Tingkat kegagalan per merchant. Dari 200 merchant, 168 memenuhi syarat minimal 1.500 transaksi. Syarat ini penting karena tingkat kegagalan merchant bertransaksi sedikit mudah menyesatkan: satu kegagalan pada 10 transaksi sudah berarti 10%. Lima merchant tertinggi memiliki tingkat kegagalan 22,69% sampai 28,34%, yaitu 3,8 sampai 4,8 kali tingkat kegagalan keseluruhan. Kelimanya hanya menyumbang 2,4% dari seluruh transaksi, tetapi 10,4% dari seluruh transaksi gagal. Artinya kegagalan terkonsentrasi pada segelintir merchant, bukan tersebar merata. Bagi operasional BayarYuk, kelima merchant (Gerai Makmur 186, Klinik Berkah 141, Apotek Jaya 146, Gadget Berkah 151, Ojek Prima 010) adalah titik awal investigasi, misalnya integrasi pembayaran atau stabilitas koneksi di sisi merchant. Analisis ini menunjukkan siapa yang bermasalah, belum mengapa.
8 C3 – Spark DataFrame dan SQL
8.1 Tujuan
Mengubah RDD hasil C2 menjadi DataFrame bertipe jelas, menggabungkannya dengan dimensi merchant dari MongoDB, lalu menjawab pertanyaan bisnis: kategori mana yang memberi nominal terbesar di tiap kota dan seberapa dominan kategori tersebut.
8.2 Implementasi
##
## Skema DataFrame transaksi:
## root
## |-- waktu: timestamp (nullable = true)
## |-- id_trx: string (nullable = true)
## |-- id_user: string (nullable = true)
## |-- id_merchant: string (nullable = true)
## |-- nominal: long (nullable = true)
## |-- status: string (nullable = true)
## Jumlah baris trx_df: 399,920
##
## Skema DataFrame merchant:
## root
## |-- _id: string (nullable = false)
## |-- kategori: string (nullable = true)
## |-- kota: string (nullable = true)
## Jumlah baris merchant_df: 200
##
## =================================================================
## JUMLAH TRANSAKSI SETELAH FILTER
## =================================================================
## 360,252
##
## =================================================================
## OPTIMASI PROSES JOIN
## =================================================================
## Metode join : BroadcastHashJoin
## Kolom penghubung : id_merchant = _id
## Jenis join : Inner Join
## Data yang di-broadcast: Data merchant
## Status optimasi : Berhasil digunakan
##
## Keterangan:
## Spark menggunakan strategi broadcast join untuk menggabungkan data transaksi dengan data merchant. Data merchant yang relatif kecil dikirimkan ke partisi yang membutuhkan proses join.
##
## =================================================================
## TOTAL NOMINAL TRANSAKSI PER KOTA DAN KATEGORI
## =================================================================
## Jumlah kombinasi kota-kategori: 25
## Jumlah transaksi yang dianalisis: 360,252
##
## Rata-rata nominal per transaksi antar kombinasi kota-kategori:
## Rp 96,630 s.d. Rp 102,681
##
## Tabel total nominal (Rp):
## kota kategori total_nominal jumlah_trx
## Bandung Kuliner 2,338,306,500 23,836
## Bandung Elektronik 1,965,813,500 19,943
## Bandung Kesehatan 1,349,101,500 13,583
## Bandung Fashion 1,238,715,500 12,330
## Bandung Transportasi 1,090,238,000 11,010
## Jakarta Kuliner 1,878,737,500 19,232
## Jakarta Transportasi 1,732,040,500 17,837
## Jakarta Elektronik 1,711,576,000 17,324
## Jakarta Kesehatan 1,685,516,500 16,931
## Jakarta Fashion 814,589,500 8,430
## Medan Fashion 2,098,380,500 20,989
## Medan Kesehatan 1,445,246,000 14,558
## Medan Elektronik 1,399,371,500 13,878
## Medan Transportasi 831,361,000 8,582
## Medan Kuliner 564,868,000 5,755
## Surabaya Fashion 2,522,657,500 25,356
## Surabaya Transportasi 2,232,461,000 22,682
## Surabaya Kesehatan 1,251,791,000 12,455
## Surabaya Kuliner 1,119,553,000 11,391
## Surabaya Elektronik 671,565,500 6,822
## Yogyakarta Transportasi 1,836,916,500 18,534
## Yogyakarta Fashion 1,455,950,500 14,946
## Yogyakarta Kesehatan 1,235,670,500 12,537
## Yogyakarta Kuliner 654,858,000 6,688
## Yogyakarta Elektronik 474,696,000 4,623
##
## =================================================================
## KATEGORI DENGAN TOTAL NOMINAL TERTINGGI PER KOTA
## =================================================================
## kota kategori total_nominal total_kota kontribusi_pct
## Bandung Kuliner 2,338,306,500 7,982,175,000 29.29
## Jakarta Kuliner 1,878,737,500 7,822,460,000 24.02
## Medan Fashion 2,098,380,500 6,339,227,000 33.10
## Surabaya Fashion 2,522,657,500 7,798,028,000 32.35
## Yogyakarta Transportasi 1,836,916,500 5,658,091,500 32.47
##
## =================================================================
## VERIFIKASI HASIL DATAFRAME DAN SPARK SQL
## =================================================================
## Hasil Spark SQL identik dengan window function DataFrame: True
##
## =================================================================
## RINGKASAN AKHIR C3
## =================================================================
## Jumlah transaksi awal : 399,920
## Transaksi setelah filter : 360,252
## Jumlah merchant : 200
## Jumlah kombinasi kota-kategori : 25
## Kontribusi kategori teratas : 24.02% - 33.10%
## BroadcastHashJoin terpakai : True
## Hasil SQL dan DataFrame sama : True
##
## C3 selesai dijalankan.
Alasan teknis
- Skema eksplisit. Spark tidak perlu memindai data untuk menebak tipe, tipe yang salah gagal sejak awal (bukan diam-diam menjadi string), dan kontrak data terdokumentasi.
waktubertipe timestamp memungkinkan fungsi waktu sepertihour(), sedangkannominalbertipelongmencegah pembulatan dan menjamin penjumlahan bilangan bulat yang tepat. - Broadcast join.
merchant_dfhanya 200 baris, sedangkan transaksi ratusan ribu baris. Denganbroadcast(), tabel kecil disalin ke setiap executor sehingga tabel besar tidak perlu di-shuffle berdasarkanid_merchant. Rencana eksekusi di atas memastikan Spark benar-benar memakaiBroadcastHashJoin. - Window function / SQL. Kita membutuhkan peringkat kategori di dalam tiap kota sekaligus total kota pada baris yang sama.
GROUP BYbiasa menghilangkan baris detail, sedangkanSUM() OVER (PARTITION BY kota)danROW_NUMBER()mempertahankan baris kategori dan menambahkan konteks kota. Persentase kontribusi dihitung sekali jalan tanpa self-join.row_numberdipilih (bukanrank) agar tepat satu kategori terpilih per kota meskipun terjadi nilai seri.
8.3 Hasil dan interpretasi
Struktur dan join. trx_df memiliki enam kolom dengan tipe sesuai skema (waktu timestamp, nominal long), dan rencana eksekusi memastikan join memakai BroadcastHashJoin. Dari 399.920 transaksi valid, 360.252 (90,1%) lolos filter analitik. Sisanya dikeluarkan karena berstatus GAGAL atau PENDING, bernominal -1, atau bernominal Rp250 juta. Kombinasi kota–kategori terisi lengkap (25 dari 25 kelompok).
Kategori teratas per kota. Kategori pemimpin berbeda antar kota: Kuliner di Bandung dan Jakarta, Fashion di Medan dan Surabaya, serta Transportasi di Yogyakarta. Kontribusinya terhadap total kota berkisar 24,02% (Jakarta) sampai 33,10% (Medan). Jakarta paling merata: empat dari lima kategorinya berada pada kisaran nominal yang rapat (sekitar Rp1,7 sampai Rp1,9 miliar), sehingga ketergantungan pada satu kategori paling rendah. Di Medan, Surabaya, dan Yogyakarta, kategori teratas menyumbang sekitar sepertiga nominal kota, sehingga perubahan pada satu kategori itu berdampak besar bagi kota tersebut.
Apa yang menggerakkan peringkat. Rata-rata nominal per transaksi hampir seragam antar kombinasi (RpInf sampai Rp-Inf), sehingga peringkat total nominal terutama mengikuti jumlah transaksi, bukan besarnya nilai per transaksi. Dengan kata lain, kategori dominan adalah kategori yang paling sering dipakai pengguna. Hasil Spark SQL identik dengan hasil window function, sehingga kedua pendekatan dapat dipakai bergantian sesuai preferensi tim. Seluruh temuan berlaku untuk periode 1–30 Oktober 2026 pada data simulasi.
9 C4 – Structured Streaming
9.1 Tujuan
Membangun proses ingestion dari landing zone yang berjalan per berkas, memisahkan event tidak valid (nominal null), menyimpan event valid ke Parquet, mencatat jumlah valid/tidak valid per batch, dan membuktikan bahwa checkpoint mencegah pemrosesan ulang.
9.2 Implementasi
## Catatan pemrosesan setiap batch:
## batch_id event_masuk event_valid event_tidak_valid
## 0 500 494 6
## 1 500 485 15
## 2 500 476 24
##
## Total: masuk = 1500 | valid = 1455 | tidak valid = 45
##
## Jumlah baris tersimpan di Parquet (dibaca ulang dari disk): 1455
## Baris per batch di Parquet: {0: 494, 1: 485, 2: 476}
## Jumlah baris di karantina (tidak valid) : 45
## Pembanding (baca batch langsung dari JSON) : 1455 -> konsisten dengan Parquet: True
##
## Uji checkpoint: batch baru setelah menjalankan ulang = 0 | total Parquet sekarang = 1455
Penjelasan komponen
format("json")denganschema(skema_event)membaca file JSON dengan tipe yang pasti. Streaming file memang mewajibkan skema agar tidak perlu menebak tipe pada data yang belum datang.maxFilesPerTrigger=1membatasi satu file per micro-batch, sehingga tiga file diproses sebagai tiga batch terpisah.trigger(availableNow=True)memproses seluruh data yang ada lalu menghentikan query. Cocok untuk ingestion terjadwal (misalnya tiap jam) yang berperilaku seperti streaming tetapi tidak berjalan terus-menerus.checkpointLocationmenyimpan offset dan daftar file yang sudah diproses. Setelah restart, Spark melanjutkan dari titik terakhir: data tidak hilang (karena lanjut dari offset terakhir) dan tidak diproses ulang.foreachBatchmemberi akses penuh pada API DataFrame untuk satu batch, sehingga pemisahan valid/tidak valid, penulisan Parquet, dan pencatatan dapat dilakukan dalam satu fungsi.
9.3 Hasil dan interpretasi
Per batch. Spark memproses tiga file sebagai tiga micro-batch (batch_id 0, 1, 2) yang masing-masing berisi 500 event. Jumlah event valid per batch adalah 494, 485, 476, sedangkan event tidak valid (nominal null) adalah 6, 15, 24. Total tidak valid 45 dari 1.500 event (3,0%), sesuai karakteristik dataset. Kenaikan jumlah tidak valid dari batch pertama ke ketiga sebaiknya tidak ditafsirkan sebagai tren, karena baru tiga titik pengamatan. Pemantauan lebih panjang diperlukan sebelum menyimpulkan kualitas sumber memburuk.
Verifikasi. Parquet dibaca ulang dari disk dan berisi 1.455 baris, sama dengan penjumlahan catatan batch dan sama dengan hitungan pembanding yang membaca JSON langsung secara batch. Karena 1.455 valid + 45 di karantina = 1.500, tidak ada event yang hilang. Event tidak valid tidak dibuang diam-diam, melainkan disimpan terpisah sehingga dapat ditelusuri (misalnya apakah nominal null berasal dari satu merchant tertentu).
Peran checkpoint. Saat query yang sama dijalankan ulang dengan checkpoint yang sama, tidak ada batch baru yang diproses dan jumlah baris Parquet tetap 1.455. Checkpoint mengingat file yang sudah diproses, sehingga ingestion terjadwal tidak membaca ulang data lama. Perlu dicatat bahwa checkpoint sendiri menjamin Spark tidak mengulang batch yang sudah selesai, sedangkan keamanan terhadap kegagalan di tengah batch datang dari penulisan idempoten per batch_id pada fungsi foreachBatch (lihat jawaban B4).
10 C5 – EDA Skala Besar dan Insight
10.1 Tujuan
Memeriksa kualitas data transaksi, memahami sebaran nominal tanpa menarik data mentah ke driver, dan menemukan pola waktu transaksi sukses. Dari analisis ini ditarik dua insight yang dapat ditindaklanjuti.
10.2 Implementasi
10.2.1 C5(1): Laporan kualitas data (satu operasi select)
## total_baris_valid : 399,920 (100.000%)
## nominal_negatif : 1,200 (0.300%)
## nominal_min_50jt : 150 (0.038%)
## status_pending : 15,689 (3.923%)
10.2.2 C5(2): Distribusi nominal dengan approxQuantile
## Persentil nominal transaksi (relativeError = 0,0001):
## P5 : Rp 11,000 (eksak, relativeError = 0: Rp 11,000)
## P50 : Rp 59,500 (eksak, relativeError = 0: Rp 59,500)
## P95 : Rp 312,000 (eksak, relativeError = 0: Rp 312,000)
## P99 : Rp 615,500 (eksak, relativeError = 0: Rp 616,500)
##
## Rata-rata nominal mentah : Rp 192,220
## Rata-rata nominal bersih : Rp 98,785 (0 < nominal < Rp50 juta)
## Nominal maksimum : Rp 250,000,000
## Total nominal SUKSES mentah: Rp 69,599,981,500
## Total nominal SUKSES bersih: Rp 35,599,981,500
## Selisih akibat nilai ekstrem: Rp 34,000,000,000 (48.9% dari total mentah)
## Transaksi SUKSES bernominal >= Rp50 juta: 136 | nominal negatif berstatus GAGAL: 1200
## Nominal maksimum = 406 kali P99
10.2.3 C5(3): Transaksi SUKSES berdasarkan jam
## jam 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
## jumlah_sukses 4190.00 3251.0 2144.00 2144.00 3316.00 6555.00 10882.00 15246.00 17464.00 16217.0 16318.00 20942.00 25998.00 21535.00 16194.00 15204.00 16252.00 19539.00 24765.00 30457.00 29407.00 21992.0 12872.00 7504.00
## persen 1.16 0.9 0.59 0.59 0.92 1.82 3.02 4.23 4.85 4.5 4.53 5.81 7.21 5.98 4.49 4.22 4.51 5.42 6.87 8.45 8.16 6.1 3.57 2.08
##
## Jam puncak: 19:00 (30,457 transaksi) | jam tersepi: 2:00 (2,144 transaksi)
## Rasio puncak/tersepi: 14.2x
## Tiga jam teratas: 19:00, 20:00, 12:00 -> 23.8% dari seluruh transaksi sukses
10.3 Hasil dan interpretasi
Laporan kualitas data. Dari 399.920 baris valid, terdapat 1.200 nominal negatif (0,300%), 150 nominal di atas atau sama dengan Rp50 juta (0,038%), dan 15.689 transaksi PENDING (3,92%). Masalah kualitas bernominal tergolong sangat sedikit secara persentase, tetapi seperti terlihat di Insight 2, dampaknya terhadap nilai agregat tidak kecil.
Distribusi nominal. Median transaksi sekitar Rp59.500, P95 sekitar Rp312.000, dan P99 sekitar Rp616.500. Selisih P50 terhadap P95 yang besar menunjukkan distribusi menjulur ke kanan: sebagian besar transaksi bernilai kecil dan sebagian kecil bernilai besar. Hasil approxQuantile dengan relativeError = 0,0001 sangat dekat dengan hasil eksak (P99 Rp615.500 berbanding Rp616.500), tetapi tidak selalu identik karena galat dibatasi pada peringkat data. Pada data miliaran baris, selisih sekecil ini jauh lebih murah dibanding pengurutan penuh. Persentil bertahan dari nilai ekstrem, sedangkan rata-rata tidak: rata-rata mentah Rp192.220 hampir dua kali rata-rata bersih Rp98.785.
Transaksi SUKSES per jam. Grafik menunjukkan dua puncak, siang (jam 12) dan malam (jam 19–20), serta lembah di dini hari. Tabel di atas memuat jumlah dan persentase tiap jam.
10.4 Insight dan rekomendasi
10.4.1 Insight 1 – Beban transaksi sangat terkonsentrasi pada jam makan dan malam
- Temuan. Jam tersibuk adalah pukul 19:00 dengan 30.457 transaksi sukses. Tiga jam teratas (19:00, 20:00, 12:00) menyumbang 23,8% dari seluruh transaksi sukses, padahal hanya 12,5% dari 24 jam. Jam tersepi adalah pukul 02:00 dengan 2.144 transaksi (pukul 03:00 sama persis).
- Bukti. Rasio jam puncak terhadap jam tersepi adalah 14,2 kali. Pukul 19:00 dan 20:00 masing-masing menyumbang sekitar 8% dari total, sedangkan pukul 00:00–05:00 masing-masing kurang dari 2% (lihat tabel dan grafik batang).
- Interpretasi. Beban sistem BayarYuk tidak datar sepanjang hari, melainkan melonjak berkali-kali lipat pada jam tertentu. Pola dua puncak ini sejalan dengan jam makan siang dan makan malam, tetapi analisis ini belum memisahkan menurut kategori merchant, sehingga penyebabnya masih dugaan yang perlu diuji. Kapasitas server, antrean pembayaran, dan layanan pelanggan yang dirancang untuk rata-rata harian akan kekurangan saat puncak dan berlebih saat dini hari.
- Rekomendasi. (1) Siapkan kapasitas (misalnya auto-scaling) dan siagakan tim dukungan untuk rentang 11:00–13:00 dan 18:00–21:00. (2) Jadwalkan perawatan sistem dan rilis versi pada rentang 01:00–04:00 saat volume terendah, agar gangguan menyentuh paling sedikit pengguna. (3) Gunakan ambang peringatan (alert) yang sadar jam, karena 100 kegagalan pada pukul 02:00 dan pada pukul 19:00 memiliki arti yang sangat berbeda. (4) Uji sebagai hipotesis, bukan kesimpulan, apakah promo khusus di luar jam sibuk dapat meratakan beban.
10.4.2 Insight 2 – Sebagian kecil nilai ekstrem menggelembungkan hampir separuh nilai transaksi sukses
- Temuan. Terdapat 150 transaksi bernominal Rp250 juta (0,038% dari data), dan 136 di antaranya berstatus SUKSES. Selain itu terdapat 1.200 transaksi bernominal -1, yang seluruhnya (1.200) berstatus GAGAL.
- Bukti. Total nominal SUKSES mentah Rp69.599.981.500 turun menjadi Rp35.599.981.500 setelah nilai ekstrem dikeluarkan, sehingga 48,9% dari total mentah berasal dari hanya 136 transaksi. Nominal maksimum sekitar 406 kali P99 (Rp616.500). Rata-rata nominal mentah Rp192.220 berbanding Rp98.785 setelah dibersihkan.
- Interpretasi. Nilai Rp250 juta berada jauh di luar sebaran transaksi normal, sehingga lebih mungkin kesalahan input atau transaksi yang perlu diverifikasi daripada transaksi wajar. Bila tidak dibersihkan, laporan omzet dan rata-rata nilai transaksi akan menyesatkan manajemen. Nominal -1 adalah kode kesalahan sistem, bukan nilai uang. Karena statusnya digabung dengan GAGAL, tingkat kegagalan pada C2 (5,96%) ikut mencakup 1.200 kegagalan sistem (5,0% dari seluruh transaksi GAGAL) sehingga angka kegagalan bisnis menjadi tidak murni.
- Rekomendasi. (1) Terapkan validasi di titik masuk data (seperti pemisahan ke karantina pada C4): tolak atau tandai nominal ≤ 0 dan nominal di atas batas wajar. (2) Tinjau 136 transaksi SUKSES bernominal Rp250 juta secara manual sebelum masuk laporan keuangan, termasuk kemungkinan penipuan atau kesalahan input. (3) Pisahkan kode kesalahan sistem dari status bisnis, misalnya dengan kolom
kode_errortersendiri, agar tingkat kegagalan merchant tidak tercemar. (4) Laporkan ukuran transaksi tipikal dengan median dan persentil, bukan rata-rata.
Catatan. Dataset pada dokumen ini adalah simulasi yang dibangkitkan dengan seed tetap. Pola seperti jam puncak, merchant dengan kegagalan tinggi, dan nilai ekstrem dirancang pada pembangkit data, sehingga insight di atas menggambarkan data simulasi ini dan bukan perilaku pengguna BayarYuk yang sesungguhnya. Yang divalidasi di sini adalah metode analisisnya.
11 Kesimpulan
Dokumen ini membangun, menjalankan, dan menganalisis alur Big Data BayarYuk dari tiga sumber berbeda dengan teknologi yang sesuai bagi masing-masing sumber. Seluruh angka di bawah berasal dari eksekusi kode pada dokumen ini.
- NoSQL (C1). MongoDB memudahkan penyaringan multi-syarat termasuk pada field array, menangani field opsional (
jam_bukahanya pada 60 dari 200 merchant), dan merangkum data lewat aggregation pipeline. Hanya 2 merchant memenuhi seluruh syarat kampanye cashback kuliner di Bandung. - Spark RDD (C2). Dari 400.000 baris log, 80 baris rusak terdeteksi oleh accumulator dan dibuang. Agregasi dikerjakan dengan
reduceByKeytanpa menarik data mentah ke driver. Tingkat kegagalan keseluruhan 5,96%, dan lima merchant dengan kegagalan tertinggi menyumbang 10,4% dari seluruh transaksi gagal. - DataFrame dan SQL (C3). Skema eksplisit dan broadcast join menghasilkan total nominal per kota dan kategori. Kategori teratas berbeda antar kota dengan kontribusi 24,0% sampai 33,1%, dan hasil window function sama dengan hasil Spark SQL.
- Structured Streaming (C4). Tiga file diproses sebagai tiga batch dengan checkpoint. Sebanyak 1.455 event valid tersimpan di Parquet dan 45 event tidak valid dikarantina, tanpa kehilangan data. Menjalankan ulang query tidak menambah batch baru.
- EDA skala besar (C5). Persentil, laporan kualitas, dan agregasi per jam dihitung di Spark, dan hanya hasil agregasi kecil yang dibawa ke driver. Dua insight utama: beban transaksi sangat terkonsentrasi pada jam tertentu, dan 136 transaksi ekstrem menggelembungkan 48,9% dari nilai transaksi sukses.
Keterbatasan. Dataset adalah simulasi dengan pola yang dirancang pada pembangkitnya, sehingga insight berlaku untuk data ini dan bukan untuk pengguna BayarYuk yang sebenarnya. Analisis juga baru mencakup satu bulan data dan belum memisahkan jam menurut kategori merchant. Penyebab (mengapa) merchant tertentu sering gagal tidak dapat disimpulkan dari data yang tersedia dan perlu diselidiki lebih lanjut.