Partitioning by multiple columns in PySpark with columns in a list

When working with large datasets in PySpark, partitioning is a crucial technique for optimizing performance. This article explores how to partition data by multiple columns using a list, making data processing more efficient and scalable.

What is Partitioning?

Partitioning divides large datasets into smaller, manageable segments called "partitions." Instead of processing one massive file, PySpark can work on multiple smaller files simultaneously, significantly reducing processing time. Think of it like organizing a library ? books are grouped by subject and author, making it faster to find what you need.

Why Partition by Multiple Columns?

Multi-column partitioning organizes data based on multiple criteria simultaneously. For example, partitioning sales data by both "region" and "year" creates a hierarchical structure where data is first grouped by region, then subdivided by year within each region.

Setting Up PySpark and Loading Data

Let's start with a practical example using the Iris dataset ?

from pyspark.sql import SparkSession
import pandas as pd

# Initialize Spark session
spark = SparkSession.builder.appName("MultiColumnPartitioning").getOrCreate()

# Load Iris dataset
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
columns = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species']
iris_df = pd.read_csv(url, names=columns)

# Convert to PySpark DataFrame
spark_df = spark.createDataFrame(iris_df)
spark_df.show(5)
+------------+-----------+------------+-----------+-----------+
|sepal_length|sepal_width|petal_length|petal_width|    species|
+------------+-----------+------------+-----------+-----------+
|         5.1|        3.5|         1.4|        0.2|Iris-setosa|
|         4.9|        3.0|         1.4|        0.2|Iris-setosa|
|         4.7|        3.2|         1.3|        0.2|Iris-setosa|
|         4.6|        3.1|         1.5|        0.2|Iris-setosa|
|         5.0|        3.6|         1.4|        0.2|Iris-setosa|
+------------+-----------+------------+-----------+-----------+

Partitioning by Multiple Columns Using a List

Here's how to partition data by multiple columns stored in a list ?

# Define partition columns as a list
partition_columns = ["species", "sepal_length"]

# Partition and save as Parquet files
output_path = "output/partitioned_iris"
spark_df.write.mode("overwrite").partitionBy(*partition_columns).parquet(output_path)

print(f"Data partitioned by: {partition_columns}")
print("Files saved to:", output_path)
Data partitioned by: ['species', 'sepal_length']
Files saved to: output/partitioned_iris

Reading Partitioned Data

When reading partitioned data, PySpark automatically recognizes the partition structure ?

# Read the partitioned data
partitioned_df = spark.read.parquet(output_path)

# Show partition information
print("Partitioned DataFrame schema:")
partitioned_df.printSchema()

# Filter data efficiently using partition columns
setosa_data = partitioned_df.filter(partitioned_df.species == "Iris-setosa")
print(f"Setosa records: {setosa_data.count()}")
Partitioned DataFrame schema:
root
 |-- sepal_width: double (nullable = true)
 |-- petal_length: double (nullable = true)
 |-- petal_width: double (nullable = true)
 |-- species: string (nullable = true)
 |-- sepal_length: double (nullable = true)

Setosa records: 50

Dynamic Partitioning with Variable Column Lists

You can dynamically change partition columns based on different scenarios ?

# Different partitioning strategies
def partition_data(df, strategy="species_only"):
    if strategy == "species_only":
        partition_cols = ["species"]
    elif strategy == "multi_column":
        partition_cols = ["species", "petal_length"]
    elif strategy == "detailed":
        partition_cols = ["species", "sepal_length", "petal_width"]
    
    # Save with different partition strategy
    output = f"output/iris_{strategy}"
    df.write.mode("overwrite").partitionBy(*partition_cols).parquet(output)
    
    return partition_cols

# Apply different strategies
strategies = ["species_only", "multi_column", "detailed"]
for strategy in strategies:
    cols = partition_data(spark_df, strategy)
    print(f"{strategy}: Partitioned by {cols}")
species_only: Partitioned by ['species']
multi_column: Partitioned by ['species', 'petal_length']
detailed: Partitioned by ['species', 'sepal_length', 'petal_width']

Best Practices

Practice Recommendation Reason
Partition Size 100MB - 1GB per partition Optimal performance balance
Column Selection Low cardinality columns Avoids too many small files
Query Patterns Match common filter conditions Maximizes partition pruning benefits

Common Pitfalls to Avoid

Over-partitioning: Using high-cardinality columns creates too many small files, degrading performance.

# Bad example - high cardinality column
# Don't partition by columns with many unique values
high_cardinality = spark_df.select("sepal_length").distinct().count()
print(f"Unique sepal_length values: {high_cardinality}")

# Good example - check cardinality first
species_cardinality = spark_df.select("species").distinct().count()
print(f"Unique species values: {species_cardinality}")
Unique sepal_length values: 35
Unique species values: 3

Conclusion

Partitioning by multiple columns using lists in PySpark significantly improves query performance and data organization. Choose partition columns wisely based on query patterns and cardinality. Remember that effective partitioning balances the number of partitions with partition size for optimal performance.

Updated on: 2026-03-27T16:45:30+05:30

360 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements