Download code/scripts/jsonl_to_parquet.py from maxpicy/mamba-cot-tts: direct link, hf CLI and curl.
- Browser
- Download file 5.52 kB
-
https://huggingface.co/maxpicy/mamba-cot-tts/resolve/main/code/scripts/jsonl_to_parquet.py
- Command line
-
hf download hf://maxpicy/mamba-cot-tts/code/scripts/jsonl_to_parquet.py
-
curl -L -o jsonl_to_parquet.py https://huggingface.co/maxpicy/mamba-cot-tts/resolve/main/code/scripts/jsonl_to_parquet.py
5.52 kB
| # jsonl_to_parquet.py: Lossless corpus JSONL <-> parquet (to_row() rebuilds the exact training row). | |
| import argparse | |
| import hashlib | |
| import json | |
| import os | |
| import sys | |
| from multiprocessing import Pool | |
| STR_COLS = ["prompt", "reasoning", "emotion_label", "pace", "pitch", "cot_form", "source", "speaker", "id", "file", | |
| "caption_source"] | |
| CHUNK = 20000 | |
| def _schema(): | |
| import pyarrow as pa | |
| return pa.schema([(c, pa.string()) for c in STR_COLS] + [ | |
| ("speaker_id", pa.int32()), | |
| ("speech_tokens", pa.list_(pa.int16())), | |
| ("residual_codes", pa.list_(pa.list_(pa.int16()))), | |
| ("plan", pa.string()), | |
| ("meta", pa.string()), | |
| ]) | |
| def _is_int(x): | |
| return isinstance(x, int) and not isinstance(x, bool) | |
| def from_row(r): | |
| out = {c: None for c in STR_COLS + ["speaker_id", "speech_tokens", "residual_codes", "plan", "meta"]} | |
| meta = {} | |
| for k, v in r.items(): | |
| if k in STR_COLS and isinstance(v, str): | |
| out[k] = v | |
| elif k == "speaker_id" and _is_int(v) and -2 ** 31 <= v < 2 ** 31: | |
| out[k] = v | |
| elif k == "speech_tokens" and isinstance(v, list) and all(_is_int(x) and -32768 <= x < 32768 for x in v): | |
| out[k] = v | |
| elif (k == "residual_codes" and isinstance(v, list) and all(isinstance(lv, list) for lv in v) | |
| and all(_is_int(x) and -32768 <= x < 32768 for lv in v for x in lv)): | |
| out[k] = v | |
| elif k == "plan" and isinstance(v, dict): | |
| out[k] = json.dumps(v, sort_keys=True, separators=(",", ":")) | |
| else: | |
| meta[k] = v | |
| out["meta"] = json.dumps(meta, sort_keys=True, separators=(",", ":")) if meta else None | |
| return out | |
| def to_row(c): | |
| r = json.loads(c["meta"]) if c.get("meta") else {} | |
| for k in STR_COLS + ["speaker_id", "speech_tokens", "residual_codes"]: | |
| if c.get(k) is not None: | |
| r[k] = c[k] | |
| if c.get("plan") is not None: | |
| r["plan"] = json.loads(c["plan"]) | |
| return r | |
| def row_hash(r): | |
| return hashlib.sha1(json.dumps(r, sort_keys=True, separators=(",", ":")).encode()).digest() | |
| def _shard(task): | |
| import pyarrow as pa | |
| import pyarrow.parquet as pq | |
| src, offset, n_rows, path = task | |
| schema = _schema() | |
| hashes, buf = [], [] | |
| tmp = path + ".tmp" | |
| w = pq.ParquetWriter(tmp, schema, compression="zstd") | |
| def flush(): | |
| cols = {name: [] for name in schema.names} | |
| for r in buf: | |
| for k, v in from_row(r).items(): | |
| cols[k].append(v) | |
| w.write_table(pa.table(cols, schema=schema)) | |
| buf.clear() | |
| with open(src, "rb") as f: | |
| f.seek(offset) | |
| for _ in range(n_rows): | |
| line = f.readline() | |
| if not line: | |
| raise SystemExit(f"{src}: unexpected EOF in shard {path}") | |
| r = json.loads(line) | |
| hashes.append(row_hash(r)) | |
| buf.append(r) | |
| if len(buf) >= CHUNK: | |
| flush() | |
| if buf: | |
| flush() | |
| w.close() | |
| i = 0 | |
| for b in pq.ParquetFile(tmp).iter_batches(batch_size=CHUNK): | |
| for c in b.to_pylist(): | |
| if i >= len(hashes) or row_hash(to_row(c)) != hashes[i]: | |
| raise SystemExit(f"{path}: row {i} does not round-trip") | |
| i += 1 | |
| if i != len(hashes): | |
| raise SystemExit(f"{path}: {i} rows read back, {len(hashes)} written") | |
| os.replace(tmp, path) | |
| h = hashlib.sha256() | |
| with open(path, "rb") as f: | |
| for blk in iter(lambda: f.read(1 << 24), b""): | |
| h.update(blk) | |
| return {"file": os.path.basename(path), "rows": n_rows, "bytes": os.path.getsize(path), "sha256": h.hexdigest()} | |
| def main(): | |
| ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) | |
| ap.add_argument("--src", required=True) | |
| ap.add_argument("--out", required=True) | |
| ap.add_argument("--split", default="train") | |
| ap.add_argument("--shard_rows", type=int, default=100000) | |
| ap.add_argument("--workers", type=int, default=8) | |
| args = ap.parse_args() | |
| os.makedirs(args.out, exist_ok=True) | |
| offsets, n = [], 0 | |
| with open(args.src, "rb") as f: | |
| pos = 0 | |
| for line in f: | |
| if n % args.shard_rows == 0: | |
| offsets.append(pos) | |
| pos += len(line) | |
| n += 1 | |
| k = len(offsets) | |
| tasks = [(args.src, off, min(args.shard_rows, n - i * args.shard_rows), | |
| os.path.join(args.out, f"{args.split}-{i:05d}-of-{k:05d}.parquet")) for i, off in enumerate(offsets)] | |
| print(f"[parquet] {args.src}: {n} rows -> {k} shards of <= {args.shard_rows}", flush=True) | |
| with Pool(args.workers) as pool: | |
| res = [] | |
| for r in pool.imap(_shard, tasks): | |
| print(f"[parquet] ok {r['file']}: {r['rows']} rows, {r['bytes'] / 1e9:.2f} GB, round-trip verified", | |
| flush=True) | |
| res.append(r) | |
| assert sum(r["rows"] for r in res) == n | |
| man = {"source": os.path.basename(args.src), "rows": n, "shards": res, | |
| "source_bytes": os.path.getsize(args.src), "parquet_bytes": sum(r["bytes"] for r in res), | |
| "lossless_round_trip_verified": True} | |
| with open(os.path.join(args.out, f"{args.split}_manifest.json"), "w") as f: | |
| json.dump(man, f, indent=1) | |
| print(f"[parquet] DONE {args.src}: {n} rows, {man['source_bytes'] / 1e9:.2f} GB JSONL -> " | |
| f"{man['parquet_bytes'] / 1e9:.2f} GB parquet", flush=True) | |
| if __name__ == "__main__": | |
| sys.exit(main()) | |