elrobot-training / scripts /merge_datasets.py
venayc's picture
Upload 31 files
59653ee verified
Raw
History Blame Contribute Delete
2.51 kB
"""Merge several norma-core parquet files into one.
Streams batches via pyarrow.ParquetWriter — never loads all files into RAM,
so it handles the big dataset-5 without issue. Schema check up front.
Run:
uv run python scripts/merge_datasets.py \\
--inputs ../datasets/dataset-*.parquet \\
--output ../datasets/dataset-merged.parquet
"""
from __future__ import annotations
import argparse
import glob
from pathlib import Path
import pyarrow.parquet as pq
def parse_args() -> argparse.Namespace:
p = argparse.ArgumentParser()
p.add_argument("--inputs", nargs="+", required=True,
help="Input parquet paths (shell globs OK).")
p.add_argument("--output", type=Path, required=True)
p.add_argument("--compression", default="snappy",
choices=["snappy", "zstd", "gzip", "none"])
return p.parse_args()
def expand_globs(patterns: list[str]) -> list[Path]:
files: list[Path] = []
for pat in patterns:
matches = sorted(glob.glob(pat))
if not matches:
raise SystemExit(f"no files match: {pat}")
files.extend(Path(m) for m in matches)
return files
def main() -> None:
args = parse_args()
inputs = expand_globs(args.inputs)
print(f"merging {len(inputs)} files:")
for p in inputs:
print(f" {p}")
# Validate schemas match up front.
schemas = [pq.read_schema(p) for p in inputs]
base = schemas[0]
for path, sch in zip(inputs[1:], schemas[1:]):
if sch != base:
raise SystemExit(f"schema mismatch between {inputs[0]} and {path}")
print(f"schema: {base.names}")
total_rows = sum(pq.read_metadata(p).num_rows for p in inputs)
print(f"total rows: {total_rows}")
compression = None if args.compression == "none" else args.compression
args.output.parent.mkdir(parents=True, exist_ok=True)
written = 0
with pq.ParquetWriter(args.output, base, compression=compression) as writer:
for p in inputs:
pf = pq.ParquetFile(p)
for batch in pf.iter_batches(batch_size=1024):
writer.write_batch(batch)
written += batch.num_rows
if written % 10000 < 1024:
print(f" {written}/{total_rows} rows ...")
print(f"wrote {written} rows to {args.output} ({args.output.stat().st_size / 1e6:.1f} MB)")
if __name__ == "__main__":
main()