Partitioned file output lets Apache Spark store rows in folders named from column values, such as region=apac/order_date=2026-07-07. This layout is useful when later Spark SQL or Hive-style readers commonly filter by those same columns, because Spark can discover partition values from the path and avoid scanning unrelated folders.
DataFrameWriter.partitionBy() belongs to the write path, not the in-memory partition count. It creates a Hive-style directory layout for file-based sinks such as Parquet, JSON, CSV, and ORC while the output still remains one dataset rooted at the directory passed to the writer.
Choose partition columns with values that group the data at a useful query level. A high-cardinality column can create many small directories or files, and the local check uses coalesce(1) only to keep the sample output compact.
Steps to write partitioned data with PySpark:
- Create the PySpark partitioned-write job.
- partitioned_write_check.py
from pathlib import Path import shutil from pyspark.sql import SparkSession from pyspark.sql.functions import col from pyspark.sql.types import DoubleType, StringType, StructField, StructType spark = ( SparkSession.builder .appName("sg-dataframe-write-partitioned") .master("local[2]") .config("spark.ui.enabled", "false") .config("spark.ui.showConsoleProgress", "false") .getOrCreate() ) spark.sparkContext.setLogLevel("ERROR") base_path = Path("spark-partitioned-demo") output = base_path / "output" / "orders" shutil.rmtree(base_path, ignore_errors=True) schema = StructType( [ StructField("order_id", StringType(), False), StructField("region", StringType(), False), StructField("order_date", StringType(), False), StructField("status", StringType(), False), StructField("amount", DoubleType(), False), ] ) orders = spark.createDataFrame( [ ("ord-1001", "apac", "2026-07-07", "paid", 212.10), ("ord-1002", "emea", "2026-07-07", "paid", 149.50), ("ord-1003", "emea", "2026-07-08", "cancelled", 42.00), ("ord-1004", "na", "2026-07-08", "paid", 87.25), ], schema, ) ( orders .coalesce(1) .write .mode("overwrite") .partitionBy("region", "order_date") .parquet(str(output)) ) partition_dirs = sorted( str(path.relative_to(output)) for path in output.glob("region=*/order_date=*") if path.is_dir() ) print("Partition directories:") for directory in partition_dirs: print(directory) read_back = spark.read.parquet(str(output)) print("Read-back row count:", read_back.count()) read_back.orderBy("order_id").show(truncate=False) apac_paid = read_back.where( (col("region") == "apac") & (col("order_date") == "2026-07-07") ) print("Filtered row count:", apac_paid.count()) apac_paid.select("order_id", "region", "order_date", "amount").show(truncate=False) spark.stop()
mode(“overwrite”) replaces the sample output directory. Use append only when the destination layout, schema, and duplicate-handling rules are already controlled by the job.
- Run the job in local Spark.
$ spark-submit --master 'local[2]' partitioned_write_check.py ##### snipped ##### Partition directories: region=apac/order_date=2026-07-07 region=emea/order_date=2026-07-07 region=emea/order_date=2026-07-08 region=na/order_date=2026-07-08 Read-back row count: 4 +--------+---------+------+------+----------+ |order_id|status |amount|region|order_date| +--------+---------+------+------+----------+ |ord-1001|paid |212.1 |apac |2026-07-07| |ord-1002|paid |149.5 |emea |2026-07-07| |ord-1003|cancelled|42.0 |emea |2026-07-08| |ord-1004|paid |87.25 |na |2026-07-08| +--------+---------+------+------+----------+ Filtered row count: 1 +--------+------+----------+------+ |order_id|region|order_date|amount| +--------+------+----------+------+ |ord-1001|apac |2026-07-07|212.1 | +--------+------+----------+------+
The read-back DataFrame gets region and order_date from the partition folders when it reads the dataset root.
- Inspect the partitioned output directory.
$ ls -R spark-partitioned-demo/output/orders spark-partitioned-demo/output/orders: _SUCCESS region=apac region=emea region=na spark-partitioned-demo/output/orders/region=apac: order_date=2026-07-07 spark-partitioned-demo/output/orders/region=apac/order_date=2026-07-07: part-00000-6b8185b6-c0cc-44d1-ba41-79ba885e4614.c000.snappy.parquet spark-partitioned-demo/output/orders/region=emea: order_date=2026-07-07 order_date=2026-07-08 spark-partitioned-demo/output/orders/region=emea/order_date=2026-07-07: part-00000-6b8185b6-c0cc-44d1-ba41-79ba885e4614.c000.snappy.parquet spark-partitioned-demo/output/orders/region=emea/order_date=2026-07-08: part-00000-6b8185b6-c0cc-44d1-ba41-79ba885e4614.c000.snappy.parquet spark-partitioned-demo/output/orders/region=na: order_date=2026-07-08 spark-partitioned-demo/output/orders/region=na/order_date=2026-07-08: part-00000-6b8185b6-c0cc-44d1-ba41-79ba885e4614.c000.snappy.parquet
_SUCCESS marks the completed write. The nested region=value/order_date=value folders are the partition layout created by partitionBy(“region”, “order_date”).
- Remove the local proof files.
$ rm -r partitioned_write_check.py spark-partitioned-demo
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.