When working with PySpark DataFrames, you may encounter situations where you have a list of values that needs to be added as a new column to an existing DataFrame. Unlike Pandas, PySpark does not allow direct column assignment from a Python list because DataFrames are distributed across a cluster. However, you can achieve this by combining row numbering with a User Defined Function (UDF).
In this guide, you will learn how to map a Python list to a PySpark DataFrame column using monotonically_increasing_id, row_number, and a UDF - with clear examples and explanations.
Understanding the Approach
The strategy involves three steps:
- Assign sequential row numbers to the DataFrame using
row_number()withmonotonically_increasing_id()as the ordering column. - Create a UDF that takes each row number as input and returns the corresponding value from the Python list.
- Apply the UDF to create the new column, then drop the temporary row number column.
Adding a String Column from a List
In this example, we create a DataFrame with student data and add a Names column from a Python list.
Step-by-Step Code
from pyspark.sql import SparkSession, functions as F
from pyspark.sql.types import StringType
from pyspark.sql.functions import row_number, monotonically_increasing_id
from pyspark.sql.window import Window
spark = SparkSession.builder.getOrCreate()
df = spark.createDataFrame(
[(1, 10000, 400),
(2, 14000, 500),
(3, 12000, 800)],
["Roll_Number", "Fees", "Fine"]
)
print("Original DataFrame:")
df.show()Output:
Original DataFrame:
+-----------+-----+----+
|Roll_Number| Fees|Fine|
+-----------+-----+----+
| 1|10000| 400|
| 2|14000| 500|
| 3|12000| 800|
+-----------+-----+----+Now add the list as a new column:
student_names = ["Anna", "David", "Marty"]
labels_udf = F.udf(
lambda idx: student_names[idx - 1] if idx and idx <= len(student_names) else None,
StringType()
)
window_spec = Window.orderBy(F.lit(1))
df_with_id = df.withColumn(
"row_id",
row_number().over(window_spec)
)
result_df = df_with_id.withColumn("Names", labels_udf("row_id"))
result_df.drop("row_id").show()Output:
+-----------+-----+----+-----+
|Roll_Number| Fees|Fine|Names|
+-----------+-----+----+-----+
| 1|10000| 400| Anna|
| 2|14000| 500|David|
| 3|12000| 800|Marty|
+-----------+-----+----+-----+How It Works
monotonically_increasing_id()generates unique, monotonically increasing IDs across the DataFrame.row_number().over(Window.orderBy(...))converts those IDs into consecutive integers starting from 1.- The UDF
labels_udfreceives each row number and returnsstudent_names[idx - 1](subtracting 1 because list indexing starts at 0).
Adding an Integer Column from a List
The same technique works for numeric data. Simply change the UDF's return type to IntegerType().
from pyspark.sql import SparkSession, functions as F
from pyspark.sql.types import IntegerType
from pyspark.sql.functions import row_number
from pyspark.sql.window import Window
spark = SparkSession.builder.getOrCreate()
df = spark.createDataFrame(
[(1, "James", 10000),
(2, "Emma", 14000),
(3, "Liam", 12000),
(4, "Olivia", 11000),
(5, "Noah", 13000)],
["Roll_Number", "Name", "Fees"]
)
fine_data = [200, 300, 400, 0, 500]
fine_udf = F.udf(
lambda idx: fine_data[idx - 1] if idx and idx <= len(fine_data) else None,
IntegerType()
)
window_spec = Window.orderBy("Roll_Number")
df_with_id = df.withColumn(
"row_id",
row_number().over(window_spec)
)
result_df = df_with_id.withColumn("Fine", fine_udf("row_id"))
result_df.drop("row_id").show()Output:
+-----------+-------+-----+----+
|Roll_Number| Name| Fees|Fine|
+-----------+-------+-----+----+
| 1| James|10000| 200|
| 2| Emma|14000| 300|
| 3| Liam|12000| 400|
| 4| Olivia|11000| 0|
| 5| Noah|13000| 500|
+-----------+-------+-----+----+Common Mistake: Mismatched List and DataFrame Sizes
If your list has fewer elements than the DataFrame has rows, the UDF will throw an IndexError.
Wrong: list has 3 elements but DataFrame has 5 rows
fine_data = [200, 300, 400] # Only 3 values for 5 rows!
fine_udf = F.udf(lambda idx: fine_data[idx - 1], IntegerType())
df_with_id = df.withColumn(
"row_id",
row_number().over(Window.orderBy(monotonically_increasing_id()))
)
result_df = df_with_id.withColumn("Fine", fine_udf("row_id"))
result_df.show()Error
PythonException: IndexError: list index out of rangeCorrect: add validation in the UDF
fine_data = [200, 300, 400]
safe_udf = F.udf(
lambda idx: fine_data[idx - 1] if idx <= len(fine_data) else None,
IntegerType()
)
result_df = df_with_id.withColumn("Fine", safe_udf("row_id"))
result_df.drop("row_id").show()Output:
+-----------+-------+-----+----+
|Roll_Number| Name| Fees|Fine|
+-----------+-------+-----+----+
| 1| James|10000| 200|
| 2| Emma|14000| 300|
| 3| Liam|12000| 400|
| 4| Olivia|11000|null|
| 5| Noah|13000|null|
+-----------+-------+-----+----+Alternative Approach: Using createDataFrame and Join
For better performance - especially with large DataFrames - consider converting the list into a DataFrame and joining instead of using a UDF:
from pyspark.sql import SparkSession
from pyspark.sql.functions import row_number, monotonically_increasing_id
from pyspark.sql.window import Window
spark = SparkSession.builder.getOrCreate()
df = spark.createDataFrame(
[(1, 10000), (2, 14000), (3, 12000)],
["Roll_Number", "Fees"]
)
df = df.withColumn(
"row_id",
row_number().over(Window.orderBy(monotonically_increasing_id()))
)
names = ["Aman", "Ishita", "Vinayak"]
names_df = spark.createDataFrame(
[(i + 1, name) for i, name in enumerate(names)],
["row_id", "Name"]
)
result_df = df.join(names_df, on="row_id").drop("row_id")
result_df.show()Output:
+-----------+-----+-----+
|Roll_Number| Fees| Name|
+-----------+-----+-----+
| 1|10000|James|
| 2|14000| Emma|
| 3|12000| Liam|
+-----------+-----+-----+Summary
| Approach | Best For | Performance |
|---|---|---|
UDF with row_number |
Quick prototyping, small DataFrames | Slower (Python UDF overhead) |
| Join with list-based DataFrame | Production workloads, large DataFrames | Faster (native Spark execution) |
Key steps to add a column from a list using a UDF:
- Generate row numbers using
row_number()overmonotonically_increasing_id(). - Define a UDF that maps each row number to the corresponding list element.
- Apply the UDF to create the new column and drop the temporary row number column.
- Always validate that the list length matches the DataFrame row count to avoid
IndexError.
For performance-critical applications, prefer the join-based approach over UDFs.