np.savez_compressed是最直接的压缩方案,它默认用zlib(level=3)压缩多个数组为.npz文件,对含零值、重复结构或低熵数据效果明显,但随机浮点数组压缩比通常不足1.2x。

用 np.savez_compressed 是最直接的方案
大数组直接 np.save 生成的是未压缩的 .npy,体积几乎不减;而 np.savez_compressed 默认使用 zlib 压缩(level=3),对含大量重复值、零值或浮点精度冗余的数组效果明显。它把多个数组打包进一个 .npz 文件,内部每个数组单独压缩。
实操建议:
- 单数组也用
np.savez_compressed('data.npz', arr=a),别用np.save—— 后者不压缩,且无法存 dict-like 结构 - 传入关键字参数命名数组(如
features=x, labels=y),读取时用np.load('data.npz')['features'] - 压缩级别可调:
np.savez_compressed(..., compress=True)(默认)或显式指定compression='zlib'+compresslevel=9(更慢但更小) - 注意:压缩过程会加载整个数组进内存两次(原始 + 压缩缓冲),超大数组需预留足够 RAM
内存受限时改用 numpy.memmap 分块处理
当数组大到无法一次性装入内存(比如 >50% 物理内存),np.savez_compressed 会触发 MemoryError。这时不能硬压,得绕开全量加载。
可行路径:
立即学习“Python免费学习笔记(深入)”;
- 先用
np.memmap创建磁盘映射数组,只在需要时读某块(如arr[1000:2000]) - 手动分块:对每块调用
np.savez_compressed(f'chunk_{i}.npz', chunk=chunk_data) - 或汇总为稀疏结构:若数组本身稀疏,转成
scipy.sparse.csr_matrix再保存(np.savez_compressed能存 sparse 对象,但需确保 scipy 版本 ≥1.8) - 避免陷阱:不要对 memmap 对象直接传给
np.savez_compressed—— 它会尝试完整读取,照样爆内存
npz 加载慢?优先用 mmap_mode='r'
压缩后的 .npz 文件解压时默认全部载入内存,大文件加载延迟高。实际只需访问其中一两个键时,浪费严重。
提速关键:
- 加载时加
mmap_mode='r':np.load('data.npz', mmap_mode='r'),让 NumPy 直接从文件映射读取,不占额外内存 - 仅适用于单数组存档(即
np.savez_compressed('x.npz', x=a)),多数组时各键仍共享一个压缩流,mmap_mode无效 - 如果必须多数组+快速随机访问,考虑拆成多个独立
.npz文件,或换用zarr(支持真正的分块压缩与并行 IO) - 验证是否生效:用
psutil.Process().memory_info().rss对比加载前后内存增量
替代方案:zarr 更适合 TB 级数组
当数组超过几 GB,npz 的局限就明显了:单文件、无并发写、压缩粒度粗(整个数组一个压缩块)、不支持云存储。
这时候该换工具:
-
zarr把数组切分成固定大小的 chunk(如chunks=(1024, 1024)),每个 chunk 单独压缩,读写都可并行 - 保存只需两行:
import zarr; zarr.save_array('data.zarr', a, compressor=zarr.Blosc(cname='zstd', clevel=3)) - 兼容性注意:旧项目若依赖
np.load接口,需加适配层;且zarr文件是目录,不是单个文件,备份/传输要打包 - 真正的大数据场景(比如遥感影像、训练集缓存),
npz只是过渡方案,zarr或parquet(配合pyarrow)才是稳态选择
压缩不是万能的——浮点数组用 np.float32 替代 np.float64、整数数组用最小够用 dtype(如 np.int8),这些预处理带来的体积下降,往往比压缩率更重要。别等存完再压,先看 dtype 和数据分布。


















