Kursus
User-Defined Functions (UDF) di PySpark menawarkan cara yang kuat bagi pengembang Python untuk menangani tugas-tugas unik yang tidak dapat diatasi oleh fungsi bawaan Spark. Jika Anda adalah data engineer, analis, atau ilmuwan data yang mahir dalam Python, memahami konsep UDF dapat memungkinkan Anda menangani tantangan data dunia nyata yang kompleks secara efektif.
Tutorial ini memandu Anda melalui konsep UDF PySpark, implementasi praktis, serta praktik terbaik dalam optimasi, pengujian, debugging, dan pola penggunaan lanjutan. Pada akhirnya, Anda akan mampu menulis, mengoptimalkan, dan menerapkan UDF yang efisien dalam skala besar dengan percaya diri.
Jika Anda baru mengenal PySpark, saya sarankan terlebih dahulu melihat tutorial Memulai dengan PySpark karena yang kita bahas di sini adalah konsep Spark tingkat lanjut.
Apa itu PySpark UDF?
PySpark UDF adalah fungsi Python kustom yang diintegrasikan ke dalam kerangka kerja terdistribusi Spark untuk beroperasi pada data yang disimpan dalam Spark DataFrame. Tidak seperti fungsi bawaan Spark, UDF memungkinkan pengembang menerapkan logika kustom yang kompleks pada tingkat baris atau kolom.
Lembar Contekan PySpark kami mencakup semua yang perlu Anda ketahui tentang Spark DataFrame, sehingga memudahkan Anda memahami Spark UDF.
Kapan sebaiknya Anda menggunakan PySpark UDF?
Gunakan UDF ketika:
- Anda memerlukan logika yang tidak dapat diekspresikan menggunakan fungsi bawaan Spark.
- Transformasi Anda melibatkan operasi kompleks yang khas Python (misalnya, manipulasi regex, logika NLP kustom).
- Anda bersedia menukar performa demi fleksibilitas, terutama saat prototyping atau untuk dataset kecil hingga menengah.
Hindari UDF ketika:
- Fungsionalitas yang setara ada di
pyspark.sql.functions; fungsi native Spark lebih cepat, dioptimalkan, dan dapat diteruskan ke mesin eksekusi. - Anda bekerja dengan dataset besar di mana performa sangat penting. UDF memperkenalkan overhead serialisasi dan memutus kemampuan Spark untuk mengoptimalkan rencana eksekusi.
- Anda dapat mengekspresikan logika menggunakan ekspresi SQL, fungsi bawaan Spark SQL, atau Pandas UDF (untuk operasi vektorisasi).
Aplikasi strategis dalam rekayasa data
Berikut adalah kasus penggunaan utama untuk PySpark UDF:
- Transformasi data kompleks, seperti parsing teks tingkat lanjut, ekstraksi data, atau manipulasi string.
- Integrasi dengan pustaka pihak ketiga berbasis Python, termasuk kerangka kerja machine learning populer seperti TensorFlow dan XGBoost.
- Menjembatani sistem legacy dan mendukung evolusi skema yang mulus saat struktur data berubah.
UDF menyederhanakan tugas rekayasa data dunia nyata yang berantakan, memberdayakan tim untuk menangani berbagai kebutuhan secara fleksibel dan efektif.
Sekarang mari kita lihat bagaimana Anda dapat mengimplementasikan PySpark UDF.
Mengimplementasikan PySpark UDF
Bagian ini menguraikan cara mendefinisikan dan mengimplementasikan PySpark UDF secara praktis.
Metode deklarasi UDF standar
Ada tiga pendekatan umum untuk mendeklarasikan UDF di PySpark:
1. UDF berbasis lambda: Cepat didefinisikan langsung di dalam kueri DataFrame; terbaik untuk operasi sederhana.
UDF berbasis lambda (UDF Python dasar) paling cocok untuk transformasi yang cepat dan sederhana. Hindari untuk pekerjaan skala besar di mana performa penting.
Berikut contohnya:
from pyspark.sql.functions import udf
from pyspark.sql.types import StringType
uppercase = udf(lambda x: x.upper() if x else None, StringType())
df = spark.createDataFrame([("Ada",), (None,)], ["name"])
df.withColumn("upper_name", uppercase("name")).show()
2. Fungsi Python dengan dekorator: Dianotasi secara eksplisit menggunakan @pyspark.sql.functions.udf, mendukung reusabilitas dan keterbacaan.
Contohnya:
from pyspark.sql import functions as F
from pyspark.sql.types import IntegerType
@F.udf(returnType=IntegerType())
def str_length(s):
return len(s) if s else 0
df.withColumn("name_length", str_length("name")).show()
3. UDF terdaftar di SQL: Didaftarkan langsung dalam konteks Spark SQL, memungkinkan penggunaannya di dalam kueri SQL.
from pyspark.sql.types import StringType
def reverse_string(s):
return s[::-1] if s else ""
spark.udf.register("reverse_udf", reverse_string, StringType())
df.createOrReplaceTempView("people")
spark.sql("""SELECT name, reverse_udf(name) AS reversed FROM people""").show()
Setiap metode memiliki trade-off: UDF lambda ringkas namun terbatas, sementara anotasi fungsi mendukung keterbacaan, kemudahan pemeliharaan, dan praktik terbaik.
Pandas UDF memungkinkan operasi vektorisasi pada batch Arrow. Mereka sering lebih cepat daripada UDF biasa dan lebih baik terintegrasi dengan mesin eksekusi Spark.
Pandas UDF Skalar (per elemen, seperti map)
Paling cocok untuk transformasi cepat per baris pada dataset besar. Misalnya:
from pyspark.sql.functions import pandas_udf
from pyspark.sql.types import IntegerType
import pandas as pd
@pandas_udf(IntegerType())
def pandas_strlen(s: pd.Series) -> pd.Series:
return s.str.len()
df.withColumn("name_len", pandas_strlen("name")).show()
Pandas UDF Grouped Map
Paling cocok untuk logika kustom per grup, mirip dengan groupby().apply() di Pandas.
from pyspark.sql import functions as F
from pyspark.sql.types import StructType, StructField, StringType, DoubleType
import pandas as pd
schema = StructType([
StructField("group", StringType()),
StructField("avg_val", DoubleType())
])
@pandas_udf(schema, F.PandasUDFType.GROUPED_MAP)
def group_avg(pdf: pd.DataFrame) -> pd.DataFrame:
return pd.DataFrame({
"group": [pdf["group"].iloc[0]],
"avg_val": [pdf["value"].mean()]
})
df.groupBy("group").apply(group_avg).show()
Pandas Aggregate UDF
Yang ini melakukan agregasi per grup, lebih cepat daripada grouped map. Misalnya:
from pyspark.sql.functions import pandas_udf
from pyspark.sql.types import DoubleType
import pandas as pd
@pandas_udf(DoubleType(), functionType="grouped_agg")
def mean_udf(v: pd.Series) -> float:
return v.mean()
df.groupBy("category").agg(mean_udf("value").alias("mean_value")).show()
Pandas Iterator UDF
Pandas Iterator UDF paling cocok untuk dataset besar yang memerlukan pemrosesan berdaya memori rendah (per batch). Contohnya:
from pyspark.sql.functions import pandas_udf
from pyspark.sql.types import IntegerType
from typing import Iterator
import pandas as pd
@pandas_udf(IntegerType(), functionType="iterator")
def batch_sum(it: Iterator[pd.Series]) -> Iterator[pd.Series]:
for batch in it:
yield batch + 1
df.withColumn("incremented", batch_sum("id")).show()
Penanganan tipe dan keamanan null
Tipe dan nilai null sering menjadi tantangan bagi PySpark UDF. PySpark menerapkan pemeriksaan tipe yang ketat, yang sering menyebabkan konversi tipe implisit atau masalah saat runtime. Selain itu, Spark meneruskan nilai null langsung ke UDF, yang dapat menyebabkan crash jika tidak ditangani secara eksplisit.
Pastikan UDF yang tangguh dengan strategi berikut:
- Tentukan tipe keluaran secara eksplisit.
- Sertakan pemeriksaan null (misalnya, pernyataan kondisional) di dalam fungsi Python Anda.
- Adopsi praktik pengkodean defensif; pemeriksaan null sederhana mencegah pengecualian runtime yang menjengkelkan.
Mengoptimalkan Performa UDF
Performa sering menjadi titik lemah utama untuk UDF standar karena model eksekusinya per baris. Memanfaatkan UDF vektorisasi dan alat optimasi Spark akan sangat meningkatkan waktu eksekusi.
UDF tervektorisasi dengan integrasi Pandas
Pandas UDF memperkenalkan pendekatan vektorisasi pada UDF di PySpark dengan meneruskan batch data sebagai Pandas Series ke fungsi Python. Desain ini secara signifikan meningkatkan performa dengan mengurangi overhead serialisasi dibandingkan UDF berbasis baris standar.
Didukung oleh Apache Arrow untuk transfer data zero-copy antara JVM dan proses Python, Pandas UDF memungkinkan eksekusi operasi yang efisien dalam skala besar. Mereka sangat efektif untuk komputasi intensif dan manipulasi string kompleks pada jutaan rekaman.
Kami membahas lebih detail tentang manipulasi data dengan PySpark dalam kursus Cleaning Data with PySpark kami.
Selain itu, Pandas UDF memungkinkan integrasi yang mulus dengan ekosistem data science Python yang lebih luas, memanfaatkan alat dan alur kerja yang familier.
|
Jenis UDF |
Gaya Eksekusi |
Kecepatan |
Optimasi Spark |
Terbaik Untuk |
Catatan |
|
UDF Standar |
Per baris (Python) |
Lambat |
Tidak dioptimalkan |
Logika sederhana, dataset kecil |
Mudah ditulis, tetapi mahal |
|
Pandas UDF Skalar |
Tervektorisasi (per kolom) |
Cepat |
Didukung Arrow |
Operasi numerik, transformasi string |
Gunakan dibandingkan UDF standar bila memungkinkan |
|
Pandas UDF Grouped Map |
Per grup (Pandas DataFrame) |
Sedang–Cepat |
Didukung Arrow |
Transformasi per grup |
Skema keluaran harus didefinisikan manual |
|
Pandas UDF Agregat |
Per grup (masukan Series → keluaran skalar) |
Cepat |
Dioptimalkan |
Agregasi seperti mean, sum |
Lebih sederhana daripada grouped map |
|
Pandas Iterator UDF |
Iterator batch (streaming) |
Cepat |
Dioptimalkan |
Memproses batch besar dengan aman |
Jejak memori lebih rendah |
Teknik optimasi Arrow
Format memori kolumnar Apache Arrow memungkinkan transfer data yang efisien dan zero-copy antara Spark JVM dan proses Python. Dengan mengaktifkan Arrow (spark.sql.execution.arrow.pyspark.enabled=true) dalam konfigurasi Spark Anda, data berpindah cepat antara lingkungan JVM dan Python, sehingga mempercepat eksekusi UDF secara signifikan.
Optimasi rencana eksekusi
Mengoptimalkan pekerjaan PySpark melibatkan pemahaman cara memengaruhi optimizer Catalyst milik Spark. Strategi lanjutan mencakup teknik seperti predicate pushdown, pemangkasan kolom, dan penggunaan petunjuk broadcast join untuk meningkatkan perencanaan kueri dan efisiensi eksekusi.
Untuk memaksimalkan performa, penting untuk meminimalkan cakupan eksekusi UDF dan lebih memilih fungsi bawaan Spark SQL kapan pun memungkinkan. Penggunaan caching yang strategis dan perancangan rencana yang cermat dapat semakin meningkatkan kecepatan eksekusi pekerjaan dan pemanfaatan sumber daya.
Optimasi performa adalah salah satu pertanyaan utama yang mungkin Anda temui dalam wawancara PySpark. Temukan cara menjawab ini dan pertanyaan Spark lainnya dalam 36 Pertanyaan dan Jawaban Wawancara PySpark Teratas untuk 2025 di blog kami.
Pola Lanjutan dan Anti-Pattern
Memahami pola penggunaan yang tepat dan tidak tepat membantu memastikan penerapan UDF yang stabil dan efisien.
Implementasi UDF stateful
UDF stateful dan non-deterministik menghadirkan tantangan unik di PySpark. Fungsi-fungsi ini menghasilkan hasil yang bergantung pada state eksternal atau kondisi yang berubah, seperti variabel lingkungan, waktu sistem, atau konteks sesi.
Meskipun UDF non-deterministik kadang diperlukan—misalnya untuk menghasilkan stempel waktu, melacak sesi pengguna, atau memasukkan unsur acak—UDF dapat mempersulit debugging, reprodusibilitas, dan optimasi.
Mengimplementasikan UDF stateful memerlukan pola desain yang cermat: mendokumentasikan perilaku dengan jelas, mengisolasi efek samping, dan menambahkan logging yang menyeluruh untuk membantu pemecahan masalah serta memastikan konsistensi di setiap run pekerjaan.
Jika digunakan dengan bijak, UDF ini dapat membuka kemampuan yang kuat, namun mempertahankan pipeline data yang andal memerlukan pengelolaan yang disiplin. Kursus Big Data Fundamentals with PySpark kami membahas lebih detail tentang cara menangani big data di PySpark.
Anti-pattern umum
Anti-pattern umum dalam penggunaan UDF dapat sangat menurunkan performa PySpark:
- Pemrosesan per baris alih-alih per batch: Menerapkan UDF pada setiap baris alih-alih menggunakan pendekatan vektorisasi seperti Pandas UDF menyebabkan perlambatan eksekusi yang besar.
- Operasi DataFrame bersarang di dalam UDF: Menyematkan kueri DataFrame di dalam UDF menyebabkan komputasi berlebihan dan menghambat kemampuan Spark untuk mengoptimalkan rencana eksekusi.
- Pendaftaran UDF inline berulang: Mendefinisikan dan mendaftarkan UDF berkali-kali di dalam kueri menambah overhead yang tidak perlu; lebih baik mendeklarasikan UDF sekali dan menggunakannya kembali di berbagai pekerjaan.
- Terlalu sering menggunakan logika Python kustom untuk operasi sederhana: Tugas seperti penyaringan dasar, aritmetika, atau transformasi yang lugas sebaiknya menggunakan fungsi bawaan Spark yang sangat dioptimalkan ketimbang UDF kustom.
Menghindari jebakan ini memastikan performa lebih baik, memudahkan optimasi oleh Catalyst, dan kode PySpark yang lebih mudah dipelihara.
Debugging dan Pengujian PySpark UDF
Pengujian dan debugging UDF memastikan keandalan dan ketangguhan dalam skenario produksi.
Pola penanganan pengecualian
Menerapkan penangkapan error terstruktur di dalam UDF sangat penting untuk membangun pipeline PySpark yang tangguh dan mudah dipelihara. Gunakan blok try-except di dalam UDF untuk menangani kejutan runtime dengan baik, seperti nilai null, ketidakcocokan tipe, atau error pembagian dengan nol.
Penanganan pengecualian yang kuat menstabilkan pipeline terhadap data tak terduga dan menyederhanakan debugging dengan menampilkan pesan error yang jelas dan dapat ditindaklanjuti. Pengecualian yang ditangkap dan dicatat dengan benar membuat perilaku UDF lebih transparan, mempercepat penyelesaian masalah dan meningkatkan keandalan pipeline secara keseluruhan.
Kerangka kerja unit testing
Gunakan kelas dasar pengujian bawaan PySpark, pyspark.testing.utils.ReusedPySparkTestCase, bersama kerangka kerja seperti pytest, untuk menulis unit test yang andal bagi UDF Anda. Menyusun pengujian yang jelas dan fokus memastikan kebenaran, stabilitas, dan kemudahan pemeliharaan logika UDF Anda dari waktu ke waktu.
Praktik terbaik untuk menguji UDF mencakup mencakup kasus umum dan edge case, memvalidasi keluaran terhadap hasil yang diketahui, serta mengisolasi perilaku UDF dari dependensi eksternal. Pengujian yang dirancang dengan baik tidak hanya melindungi dari regresi tetapi juga menyederhanakan pengembangan dan refaktorisasi di masa mendatang.
Evolusi dan Arah Masa Depan
Ekosistem PySpark terus berkembang pesat, menghadirkan kemampuan baru yang semakin meningkatkan UDF.
Integrasi Unity Catalog
Perkembangan terbaru telah mengintegrasikan pendaftaran UDF ke dalam Unity Catalog, menyederhanakan cara UDF dikelola, ditemukan, dan diatur dalam skala besar. Unity Catalog memungkinkan kontrol terpusat atas manajemen siklus hidup UDF, termasuk pendaftaran, versioning, dan kontrol akses, yang semuanya penting untuk lingkungan enterprise.
Integrasi ini meningkatkan tata kelola, menegakkan kebijakan keamanan yang konsisten, dan meningkatkan keterjangkauan lintas tim, sehingga UDF lebih mudah digunakan kembali, diaudit, dan dikelola dalam ekosistem data yang besar dan kompleks.
UDF dengan akselerasi GPU
Kerangka kerja seperti RAPIDS Accelerator memungkinkan offloading GPU untuk tugas UDF yang intensif komputasi di PySpark, menawarkan peningkatan performa yang transformasional. Dengan memindahkan operasi berat, seperti analisis numerik, inferensi deep learning, dan pemodelan data skala besar, ke GPU, RAPIDS dapat mengurangi waktu eksekusi dari jam menjadi menit pada beban kerja yang sesuai.
Akselerasi GPU sangat bermanfaat untuk skenario yang melibatkan dataset masif, komputasi tervektorisasi yang kompleks, dan pipeline machine learning, sehingga secara dramatis memperluas performa dan skalabilitas PySpark untuk tugas rekayasa data modern. Kursus Machine Learning with PySpark kami membahas konsep-konsep ini lebih dalam.
Kesimpulan
PySpark UDF adalah alat yang kuat untuk memperluas kapabilitas Spark, memungkinkan tim menangani tugas pemrosesan data yang kompleks dan kustom yang melampaui fungsi bawaan. Jika diterapkan dengan benar, UDF membuka fleksibilitas dan inovasi dalam pipeline data skala besar.
Namun, mengoptimalkan performa UDF memerlukan perhatian cermat, menghindari jebakan umum seperti operasi per baris, mengelola pengecualian dengan baik, dan memanfaatkan teknik seperti vektorisasi Pandas UDF dengan integrasi Arrow.
Kemajuan yang muncul, seperti akselerasi GPU melalui kerangka kerja seperti RAPIDS, semakin memperluas kemungkinan dengan alur kerja berbasis UDF. Baik Anda mentransformasi data dunia nyata yang berantakan maupun menyematkan analitik lanjutan ke dalam sistem produksi, menguasai praktik terbaik UDF sangat penting untuk membangun pipeline data yang cepat, efisien, dan andal.
Pelajari seluk-beluk yang menyita 70–80% waktu ilmuwan data—data wrangling dan feature engineering—melalui kursus Feature Engineering with PySpark kami.
FAQ PySpark UDF
Kapan saya harus menggunakan PySpark UDF alih-alih fungsi bawaan?
Anda sebaiknya menggunakan PySpark UDF hanya ketika transformasi Anda tidak dapat dicapai menggunakan fungsi bawaan Spark. Fungsi bawaan telah dioptimalkan dan berjalan lebih cepat daripada UDF karena beroperasi secara native di JVM tanpa overhead serialisasi.
Mengapa Pandas UDF lebih cepat daripada UDF Python biasa di PySpark?
Pandas UDF (UDF tervektorisasi) lebih cepat karena menggunakan Apache Arrow untuk serialisasi data yang efisien dan memproses data dalam batch alih-alih per baris, yang mengurangi overhead perpindahan data antara JVM dan interpreter Python.
Apakah saya selalu perlu menentukan tipe keluaran untuk UDF di PySpark?
Ya, PySpark memerlukan tipe data keluaran yang eksplisit saat mendefinisikan UDF. Persyaratan ini memastikan serialisasi yang tepat antara Java dan Python serta mencegah error saat runtime.
Bagaimana cara mengaktifkan Apache Arrow dalam aplikasi PySpark saya?
Anda dapat mengaktifkan Apache Arrow dengan menetapkan konfigurasi berikut sebelum menjalankan UDF apa pun:
spark.conf.set("spark.sql.execution.arrow.pyspark.enabled", "true")
Apa cara terbaik menangani nilai null dalam PySpark UDF?
Selalu sertakan pemeriksaan kondisi untuk nilai None (null) di UDF Anda guna menghindari pengecualian. Misalnya: if product_name is None: return None.

