How to Mock a Parquet partitionBy Sink in Python

Manually write a DataFrame to partitioned Parquet files, mimicking Spark's partitionBy sink behavior without Spark.

Medium Python 3.9+ Aug 9, 2026 Big data & Spark 13 views 0 copies

Requires third-party packages — install first
pip install pyarrow pandas

Python code

47 lines
Python 3.9+
import pyarrow as pa
import pyarrow.parquet as pq
from pathlib import Path
import tempfile
import shutil


def mock_partition_by_sink(data, output_dir, partition_cols):
    table = pa.Table.from_pandas(data)
    schema = table.schema
    unique_combos = table.select(partition_cols).to_pylist()
    seen = set()
    for combo in unique_combos:
        key = tuple(combo[col] for col in partition_cols)
        if key in seen:
            continue
        seen.add(key)
        mask = pa.compute.equal(table[partition_cols[0]], combo[partition_cols[0]])
        for col in partition_cols[1:]:
            mask = pa.compute.and_(mask, pa.compute.equal(table[col], combo[col]))
        subset = table.filter(mask)
        partition_path = Path(output_dir) / "/".join(
            f"{col}={combo[col]}" for col in partition_cols
        )
        partition_path.mkdir(parents=True, exist_ok=True)
        pq.write_table(subset, partition_path / "data.parquet", schema=schema)


if __name__ == "__main__":
    import pandas as pd

    df = pd.DataFrame({
        "year": [2020, 2020, 2021, 2021],
        "month": [1, 2, 1, 2],
        "value": [10, 20, 30, 40],
    })

    tmp_dir = tempfile.mkdtemp()
    try:
        mock_partition_by_sink(df, tmp_dir, ["year", "month"])
        result = sorted(
            str(p.relative_to(tmp_dir))
            for p in Path(tmp_dir).rglob("*.parquet")
        )
        print(result)
    finally:
        shutil.rmtree(tmp_dir)

Output

stdout
['year=2020/month=1/data.parquet', 'year=2020/month=2/data.parquet', 'year=2021/month=1/data.parquet', 'year=2021/month=2/data.parquet']

How it works

The function extracts unique combinations of partition columns from the input table. For each combination, it builds a boolean mask using pa.compute.equal and pa.compute.and_ to filter the rows that belong to that partition. The filtered subset is then written to a directory path constructed as col=value pairs, mimicking Spark's Hive-style partitioning layout. This approach avoids running a full Spark cluster while still producing partitioned Parquet output for local testing or small-scale workflows. The schema is preserved for each partition write to keep column types consistent.

Common mistakes

  • Using `json` or file path strings incorrectly when building partition paths, causing invalid directory structures.
  • Forgetting to convert pandas DataFrame to a PyArrow Table before filtering, which breaks arrow-based operations.
  • Not deduplicating the unique partition combos, causing duplicate writes or redundant file creation.

Variations

  1. Use `table.to_pandas().groupby(partition_cols)` and write each group separately with pandas to_parquet.
  2. Leverage PyArrow's `pq.write_to_dataset` with `partition_cols` parameter to let PyArrow handle partitioning natively.

Real-world use cases

  • Replicating Spark's partitionBy logic in unit tests without spinning up a local Spark instance.
  • Writing small to medium datasets to partitioned Parquet for downstream tools like Athena or DuckDB.
  • Creating partition-on-write example code in a data engineering tutorial for a local dev environment.

Sponsored

Run locally

This sample needs third-party packages, so it cannot run in the browser IDE. Copy the code above, install the packages shown at the top, then run it in your own Python environment.

More from Big data & Spark

Related tutorials and quizzes for this topic.