User-defined functions, pandas/Arrow UDFs, the Pandas API on Spark, and why to prefer built-ins over UDFs.
⏱ 4 min readTopics chapter readerLevel · Structured APIs
01 · Overview
Start here
⏱ 4 min · Topic 1 of 5
A UDF is a user-defined function: your own Python function, used on a column. You write one when no built-in function does the job.
The built-in functions in F run inside Spark, in the JVM, the Java program that runs Spark. A Python UDF runs in a separate Python program on each executor.
So every row must travel from the JVM to Python and back. And Spark cannot see inside your function, so it cannot improve it.
Use a UDF only when F has no function for the job. This chapter shows when, and how to make it less slow.
02 · Plain UDF
Plain Python UDFs: powerful but slow
⏱ 6 min · Topic 2 of 5
The @udf decorator turns a Python function into a column function. You must give the type it returns:
The output is the same six labels. Prefer this form.
explain() on the UDF shows a step called ArrowEvalPython. In Spark 4.2, rows travel to Python in batches, in the Arrow format, by default. But your function still runs once for each row.
The declared type matters. In our run, a UDF declared as IntegerType that returned text stopped the job:
pyarrow.lib.ArrowInvalid: Failed to parse string: '80.0' as a scalar of type int32
IntegerType comes from pyspark.sql.types, like StringType.
Watch out
Older Spark versions, without Arrow, often returned NULL for a wrong type, with no error. If a UDF column is suddenly full of NULL, check the declared type first.
03 · pandas UDF
Pandas UDFs: faster Python
⏱ 6 min · Topic 3 of 5
pandas is a Python library for tables. A pandas Series is one column of values. A pandas UDF gets a whole batch of a column as a Series, not one value at a time.
So Python runs once for each batch, and pandas does the work on the whole column:
The type hints pd.Series -> pd.Series tell Spark the shape. A missing amount stays missing, so 105 gives NULL.
pandas UDFs need the pandas and pyarrow libraries on every executor.
Choose in this order:
A built-in function in F, or SQL. It runs inside Spark, and the optimizer can see it.
A pandas UDF, when you need Python. It works on batches.
A plain Python UDF, only for logic that pandas cannot do on a whole column.
Interview question
“Why is a Python UDF slow?” Data must travel between the JVM and Python, and Spark cannot optimize inside the function. Say what you use first: a built-in function, then a pandas UDF.
04 · pandas API
The pandas API on Spark
⏱ 4 min · Topic 4 of 5
The pandas API on Spark lets you write pandas-style code that runs on Spark. It lives in pyspark.pandas. pandas_api() turns a Spark DataFrame into one:
The same totals as chapter 10. The work runs on the executors, not in one pandas program.
It helps when a team already knows pandas, or when pandas code must grow past one machine.
Some pandas habits are slow here. Row order is not kept unless you sort, and some operations move all data to one place. For new pipelines, the DataFrame API is the clearer choice.
05 · Next Chapter
Next Chapter
You can now choose between a built-in function, a pandas UDF and a plain UDF.
⏱ 3 min · Topic 5 of 5
Next chapter
How Spark Runs on a Cluster
You can now choose between a built-in function, a pandas UDF and a plain UDF.
Next, you follow one job from your code to tasks on many machines.