Buckets:

redmingwei's picture
download
raw
4.3 kB
#!/usr/bin/env python3
"""v4 — 不补齐,按原生长度存。
v3 的 counts.npy 是 (5483479, 192) uint8,把每个片段补零到 192 列。
这暗示了一个不存在的共同维度:第 j 列在不同片段里是不同单元,
而且很多片段本来就只有 40–65 个单元。
从集合论看,数据集是索引族 D = ⊔_i X_i,每个 X_i 有自己的形状
(n_bins_i, n_units_i)。不需要把它们塞进同一个矩阵。
改存:
counts_ragged.npy 1-D uint8,Σ n_bins_i × n_units_i 个元素
counts_index.parquet 加 offset 列(元素偏移),保留 n_bins / n_units
读取:
a = C[off : off + n_bins*n_units].reshape(n_bins, n_units)
这样:
- 没有补零,体积等于原生大小
- 没有"第 j 列"的假象
- 每条的 bin 宽度、单元数都是这条自己的属性(写进 index)
"""
from __future__ import annotations
import json
import os
import time
from pathlib import Path
import numpy as np
import pandas as pd
SRC = Path("/domains/counts-v3")
OUT = Path("/domains/counts-v4")
OUT.mkdir(parents=True, exist_ok=True)
def main():
t0 = time.time()
ci = pd.read_parquet(SRC / "counts_index.parquet")
print(f"segments {len(ci)}", flush=True)
native = int((ci.n_bins_20ms * ci.n_units).sum())
padded = int((ci.n_bins_20ms * 192).sum())
print(f"padded {padded:,} ({padded/1e6:.0f} MB) | "
f"native {native:,} ({native/1e6:.0f} MB) | "
f"waste {100*(padded-native)/padded:.1f}%", flush=True)
W = np.load(SRC / "counts.npy", mmap_mode="r")
print(f"source {W.shape} {W.dtype}", flush=True)
out = OUT / "counts_ragged.npy"
if out.exists():
out.unlink()
R = np.lib.format.open_memmap(out, mode="w+", dtype=np.uint8, shape=(native,))
offs = np.zeros(len(ci), dtype=np.int64)
k = 0
for i, (_, r) in enumerate(ci.iterrows()):
nb, nu = int(r.n_bins_20ms), int(r.n_units)
m = nb * nu
blk = np.asarray(W[r.bin_start:r.bin_end, :nu]).reshape(-1)
R[k:k + m] = blk
offs[i] = k
k += m
if i % 40 == 0:
print(f" [{i+1}/{len(ci)}] {r.file} {nb}x{nu} -> {k:,} "
f"[{time.time()-t0:.0f}s]", flush=True)
assert k == native, (k, native)
R.flush()
del R, W
ci = ci.copy()
ci["offset"] = offs
ci["n_elements"] = ci.n_bins_20ms * ci.n_units
# 每条记录自己的时间分辨率(目前全是 20 ms,但作为列写出来,
# 以后加入不同 bin 宽的成员时不需要改结构)
ci["bin_ms"] = 20.0
ci.to_parquet(OUT / "counts_index.parquet", index=False)
meta = {
"schema_version": "2.0",
"layout": "ragged",
"members": int(len(ci)),
"total_elements": native,
"padded_equivalent": padded,
"waste_avoided_pct": round(100 * (padded - native) / padded, 2),
"shapes": sorted({(int(r.n_bins_20ms), int(r.n_units))
for _, r in ci.iterrows()}, key=lambda t: -t[0])[:5],
"n_distinct_widths": int(ci.n_units.nunique()),
"note": ("每个片段是独立的集合 (n_bins_i, n_units_i)。"
"不存在跨片段的共同单元维度。"),
}
(OUT / "layout.json").write_text(json.dumps(meta, indent=2, ensure_ascii=False),
encoding="utf-8")
print()
print(json.dumps(meta, indent=2, ensure_ascii=False), flush=True)
print(f"\nwrote {out} {out.stat().st_size/1e6:.0f} MB", flush=True)
print(ci.groupby(["source", "region"]).agg(
segs=("file", "size"), bins=("n_bins_20ms", "sum"),
elems=("n_elements", "sum")).to_string(), flush=True)
# 自检:随机抽三段,圆整后与原始一致
rng = np.random.default_rng(0)
Rr = np.load(out, mmap_mode="r")
W2 = np.load(SRC / "counts.npy", mmap_mode="r")
for i in rng.choice(len(ci), 3, replace=False):
r = ci.iloc[i]
a = np.asarray(Rr[r.offset:r.offset + r.n_elements]).reshape(
int(r.n_bins_20ms), int(r.n_units))
b = np.asarray(W2[r.bin_start:r.bin_end, :int(r.n_units)])
print(f" check {r.file}: equal={np.array_equal(a, b)} shape={a.shape}",
flush=True)
if __name__ == "__main__":
main()

Xet Storage Details

Size:
4.3 kB
·
Xet hash:
c0784e902797b29d4789477320b58c82738b04ecbc2ad1e2895767c5bb880e75

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.