Apache Parquet files are fundamentally immutable binary structures. Unlike traditional relational databases or text CSV files, you cannot append new rows directly to the bottom of an existing .parquet file without rewriting the entire dataset. In high-volume enterprise environments, rewriting multi-gigabyte data files daily leads to high processing latency and server memory exhaustion. An Incremental (Delta) Append Pipeline solves this issue by organizing datasets into partitioned directory structures using Pandas and PyArrow.
Because Parquet organizes data into compressed column chunks with dictionary encoding and metadata footers, appending data requires a deliberate architectural strategy:
| Append Strategy | Mechanism | Memory Footprint | Best Use Case |
|---|---|---|---|
| File Overwrite | Read existing file → concatenate → rewrite file | High (Full dataset loaded into RAM) | Small datasets (< 100 MB) |
| Directory Append | Write new delta file with unique timestamp in same folder | Low (Only delta payload in RAM) | Medium logs / streaming data |
| Hive Partitioning | Partition into subfolders (e.g. year=2026/month=11/) | Minimal (Targeted partition writes) | Enterprise Data Lakes / Cloud Storage |
The standard industry solution is Hive-style dataset partitioning. By specifying the partition_cols parameter in to_parquet(), Pandas and PyArrow automatically organize files into a structured directory tree:
data_lake/
├── year=2026/
│ ├── month=10/
│ │ └── delta_20261030_120000.parquet
│ └── month=11/
│ └── delta_20261101_090000.parquet
Here is how to write a production pipeline function that appends incoming daily batches to a partitioned Parquet dataset without loading pre-existing data into memory:
import pandas as pd
import pyarrow.parquet as pq
import pyarrow as pa
from datetime import datetime
def append_to_parquet_dataset(df_delta, dataset_root_path, partition_cols=None):
# Convert Pandas DataFrame to PyArrow Table
table = pa.Table.from_pandas(df_delta)
# Write delta table to root dataset folder (appends new files automatically)
pq.write_to_dataset(
table,
root_path=dataset_root_path,
partition_cols=partition_cols,
use_legacy_dataset=False,
compression="snappy"
)
print(f"Successfully appended {len(df_delta)} records to dataset at {dataset_root_path}")
Download sample student data directly from plus2net Student Benchmark Dataset and simulate adding new incoming batches across multiple operational dates:
import pandas as pd
# Download initial sample student dataset inside Google Colab
# !wget https://www.plus2net.com/python/download/student.csv
# 1. Load initial batch (Batch A)
df_batch_a = pd.read_csv("https://www.plus2net.com/python/download/student.csv")
df_batch_a["entry_date"] = "2026-11-01"
# 2. Write Batch A to partitioned data lake
df_batch_a.to_parquet(
"student_data_lake",
partition_cols=["entry_date"],
engine="pyarrow",
index=False
)
# 3. Simulate new incoming batch on the following day (Batch B)
df_batch_b = pd.DataFrame({
"id": [101, 102],
"name": ["Elena", "Frank"],
"class": ["Four", "Five"],
"mark": [88, 95],
"sex": ["female", "male"],
"entry_date": ["2026-11-02", "2026-11-02"]
})
# Append Batch B without loading or overwriting Batch A
df_batch_b.to_parquet(
"student_data_lake",
partition_cols=["entry_date"],
engine="pyarrow",
index=False
)
print("Batch B incrementally appended successfully!")
Pandas automatically discovers directory partitions and re-assembles them as standard column filters when using read_parquet():
# Read entire partitioned dataset seamlessly
df_full_lake = pd.read_parquet("student_data_lake")
print(f"Total records in data lake: {len(df_full_lake)}")
# Selective partition pruning (reads ONLY the 2026-11-02 subfolder from disk)
df_nov2 = pd.read_parquet("student_data_lake", filters=[("entry_date", "==", "2026-11-02")])
print(df_nov2)
SchemaMismatchError: Table schema does not match existing dataset schema: Occurs when the incoming delta DataFrame has different column names or dtypes than existing Parquet files. Fix: Align column schemas and explicit data types prior to writing.ArrowInvalid: Could not open file for writing: Occurs if another process or thread has locked a Parquet file. Fix: Ensure unique file naming or use PyArrow's default UUID naming generator.No. Parquet files are immutable. You must either rewrite the single file or append new binary files into a partitioned directory dataset.
Partition pruning allows Pandas and PyArrow to bypass unneeded subdirectories entirely during reads, dramatically improving speed and lowering memory usage.
Perform the following task:
sales_lake partitioned by region.region='North' records to the lake.region='South' records without overwriting.South region using filters=[('region', '==', 'South')] and assert the row count.import pandas as pd
# 1. Initial Batch (North)
df_north = pd.DataFrame({"order_id": [1, 2], "amount": [150.0, 200.0], "region": ["North", "North"]})
df_north.to_parquet("sales_lake", partition_cols=["region"], index=False)
# 2. Delta Batch (South)
df_south = pd.DataFrame({"order_id": [3, 4, 5], "amount": [310.0, 420.0, 110.0], "region": ["South", "South", "South"]})
df_south.to_parquet("sales_lake", partition_cols=["region"], index=False)
# 3. Partition Pruned Read
df_south_read = pd.read_parquet("sales_lake", filters=[("region", "==", "South")])
assert len(df_south_read) == 3
print("Delta append exercise passed successfully!")
Explore interactive code cells, build a partitioned Parquet data lake, execute delta appends, and run partition pruning queries.
View on GitHubAuthor & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.