Episode ini membahas operasi analitik inti Spark: berbagai tipe join dari inner hingga anti, agregasi groupBy dan advanced grouping, window functions untuk time-based analytics, serta implikasi performa join dan shuffle pada query besar.

Setelah menguasai DataFrame dan SQL dasar, episode 7 ini masuk ke operasi analitik yang paling sering dipakai di dunia nyata: join, agregasi, dan window function. Ketiganya adalah bahasa sehari-hari data analyst dan data engineer.
Di episode ini kita membahas semua tipe join yang tersedia, agregasi groupBy dan variasi lanjutannya, window function untuk analisis berbasis urutan waktu, serta implikasi performa — karena join dan shuffle adalah dua penyebab paling umum query melambat.
Spark mendukung seluruh tipe join standar SQL. Contoh paling jelas memakai PySpark:
pelanggan = spark.createDataFrame([(1, "budi"), (2, "sari")], ["id", "nama"])
pesanan = spark.createDataFrame([(1, "p1"), (1, "p2"), (3, "p3")], ["id", "produk"])
inner = pelanggan.join(pesanan, "id", "inner")
inner.show()pelanggan.join(pesanan, "id", "inner") hanya mengembalikan baris yang id-nya cocok di kedua sisi. Tipe join yang tersedia:
left semi → "data yang punya pasangan" (filter)
left anti → "data yang tidak punya pasangan" (data quality check)left_anti misalnya sangat praktis untuk menemukan pesanan yang id pelanggannya tidak ada di tabel pelanggan.
cross join menghasilkan perkalian semua baris kedua sisi. Karena hasilnya bisa meledak secara eksponensial, Spark memerlukan konfirmasi eksplisit sebelum menjalankannya.
groupBy menggabungkan baris berdasarkan kunci lalu menerapkan fungsi agregasi:
from pyspark.sql import functions as F
penjualan = spark.read.option("header", True).csv("data/penjualan.csv")
hasil = penjualan \
.groupBy("kota", "kategori") \
.agg(
F.sum("jumlah").alias("total"),
F.avg("harga").alias("harga_rata"),
F.countDistinct("produk").alias("produk_unik"),
)
hasil.show()F.sum, F.avg, dan F.countDistinct adalah fungsi agregasi bawaan. groupBy bisa menerima beberapa kolom untuk granularitas lebih dalam — analisis kota per kategori, misalnya.
Untuk analisis multidimensi, Spark menyediakan tiga variasi:
rollup(a, b) → (a,b), (a), (keseluruhan)
cube(a, b) → (a,b), (a), (b), (keseluruhan)cube menghasilkan lebih banyak baris karena mencakup semua kombinasi. Ini berguna untuk laporan yang perlu melihat data dari berbagai sudut tanpa menulis beberapa query terpisah.
Untuk agregasi yang bergerak pada jendela urutan (misalnya rata-rata 7 hari terakhir), gabungkan groupBy dengan window function di bagian berikut.
Berbeda dengan groupBy yang menciutkan baris, window function menghitung nilai agregasi namun mempertahankan setiap baris. Definisikan window dengan partitionBy dan orderBy:
from pyspark.sql.window import Window
w = Window.partitionBy("kota").orderBy(F.col("total").desc())
hasil = penjualan.withColumn("peringkat", F.row_number().over(w))
hasil.filter(F.col("peringkat") == 1).show()F.row_number().over(w) memberi nomor urut di dalam setiap partisi kota berdasarkan total terbesar. Hasilnya: top 1 penjualan per kota — tanpa kehilangan kolom lain.
Fungsi yang paling sering dipakai:
w2 = Window.partitionBy("kota").orderBy("bulan")
hasil2 = penjualan.withColumn("bulan_lalu", F.lag("total").over(w2)) \
.withColumn("delta", F.col("total") - F.col("bulan_lalu"))F.lag("total").over(w2) mengambil nilai total dari bulan sebelumnya dalam kota yang sama — pola dasar analisis time-series seperti perhitungan growth.
Untuk agregasi berdasarkan rentang waktu (misalnya per jam), gunakan fungsi F.window:
from pyspark.sql import functions as F
event = spark.read.json("data/event.json")
per_jam = event \
.groupBy(F.window("waktu", "1 hour"), "produk") \
.count()
per_jam.show()F.window("waktu", "1 hour") mengelompokkan waktu ke jendela per jam — pola yang akan kembali muncul di Structured Streaming pada episode 10.
Sebagian besar join membutuhkan shuffle: data dikelompokkan ulang ke partisi berdasarkan kunci join, dikirim antar executor melalui jaringan. Semakin besar data, semakin besar biayanya. Ada beberapa strategi untuk mengurangi biaya ini:
broadcast (sisi kecil) → bucket join (disiapkan) → sort-merge (default)Detail strategi dan kapan memakainya akan dibahas tuntas di episode 9. Yang perlu kalian ingat sekarang: setiap join dan groupBy yang memicu shuffle adalah kandidat utama optimasi.
Warning
Perhatikan data skew: jika satu kunci join mendominasi (misalnya satu kota memegang 90 persen transaksi), executor yang memegang kunci itu menjadi bottleneck. Ini masalah umum yang memerlukan solusi khusus yang dibahas di episode 9.
Episode 7 membekali kalian tiga operasi analitik inti: join dengan delapan tipe dari inner sampai anti, agregasi groupBy dengan rollup dan cube, serta window function untuk analisis berurutan dan berbasis waktu. Kalian juga memahami mengapa join dan shuffle adalah sumber biaya utama.
Inti yang harus dibawa pulang:
left_anti adalah alat deteksi data yang hilang di tabel referensi.Di episode 8 selanjutnya kita akan membahas data sources dan storage — menghubungkan Spark ke HDFS, S3, JDBC, dan file system, membaca serta menulis Parquet, Avro, ORC, JSON, dan CSV, serta strategi partitioning, bucketing, file layout, dan schema evolution.