Zarr — 云原生分块压缩数组存储格式
一句话说明
Zarr 把大型 NumPy 数组切成小块,分别压缩存储(本地/S3/GCS均可),让 TB 级数据也能高效并行读写,是云时代的 HDF5 替代方案。
安装与配置
# pip 安装(当前默认安装 v3.x,要求 Python >=3.12)
pip install zarr # 核心库(v3.2+)
# 云存储支持
pip install zarr s3fs # AWS S3 支持
pip install zarr gcsfs # Google Cloud Storage 支持
# 验证
python -c "import zarr; print(zarr.__version__)"
核心用法
创建和写入数组
import zarr
import numpy as np
from zarr.codecs import BloscCodec # v3 压缩编解码器
# 创建根组(直接传路径,Zarr 自动管理存储)
root = zarr.open_group("my_data.zarr", mode="w") # 写模式创建
# 创建数组(指定形状和分块大小)
z = root.create_array(
name = "temperature", # 数据集名称
shape = (1000, 1000), # 总形状:1000x1000
chunks = (100, 100), # 每块 100x100
dtype = "float32", # 数据类型
codecs = [BloscCodec( # 压缩方式(v3 用 codecs 参数)
cname="lz4", # LZ4 压缩器(速度快)
clevel=5, # 压缩级别 1-9
)],
)
# 写入数据
z[:] = np.random.rand(1000, 1000).astype("float32")
print(z.info) # 打印数组信息
读取数组
# 打开已有存储(只读模式)
root = zarr.open_group("my_data.zarr", mode="r")
z = root["temperature"] # 取出数据集
# 切片读取(只读需要的块,不全量加载)
subset = z[100:200, 100:200] # 读取子区域
print(subset.shape) # (100, 100)
print(type(subset)) # numpy.ndarray
分组管理
# 层级结构(类似文件夹)
root = zarr.open_group("experiment.zarr", mode="w")
# 创建子组
raw = root.create_group("raw") # 原始数据组
proc = root.create_group("processed") # 处理后数据组
raw.create_array("reads", shape=(500, 150), dtype="int16")
proc.create_array("counts", shape=(500, 20000), dtype="float32")
# 添加元数据
root.attrs["experiment_id"] = "EXP-001" # 全局属性
root.attrs["date"] = "2025-05-01"
实战案例
单细胞数据大矩阵存储
import zarr
import numpy as np
# 存储 10x Genomics 风格的计数矩阵(细胞 x 基因)
n_cells, n_genes = 50000, 30000 # 5万细胞,3万基因
root = zarr.open_group("scRNA.zarr", mode="w")
# 稀疏矩阵用分块存储
counts = root.create_array(
"X",
shape = (n_cells, n_genes),
chunks = (1000, 1000), # 每块 1000x1000
dtype = "float32",
fill_value = 0, # 空位填 0(稀疏友好)
)
# 批量写入(避免内存溢出)
batch = 1000 # 每次写1000行
for i in range(0, n_cells, batch):
end = min(i + batch, n_cells)
counts[i:end, :] = np.random.poisson(0.5, (end-i, n_genes)).astype("float32")
print(f"数组形状:{counts.shape}")
print(f"分块大小:{counts.chunks}")
常见报错与解决
| 报错 | 原因 | 解决 |
|---|
ImportError: cannot import DirectoryStore | 使用了 Zarr v2 API | 改用 zarr.open_group(path) 或 zarr.storage.LocalStore(path) |
AttributeError: module 'zarr' has no attribute 'Blosc' | v3 移除了 zarr.Blosc | 改用 from zarr.codecs import BloscCodec |
ContainsGroupError | 组已存在 | 用 mode="a"(追加)或 mode="w"(覆盖) |
PermissionError | 文件只读 | 打开时用 mode="a"(追加)或 mode="w"(覆盖) |
| 写入慢 | 块太小频繁 I/O | 增大 chunks 参数,推荐每块 1-10 MB |
速查表
| 操作 | 代码 |
|---|
| 创建/打开组 | zarr.open_group("dir.zarr", mode="w") |
| 创建数组 | root.create_array("name", shape=(...)) |
| 指定压缩 | codecs=[BloscCodec(cname="lz4")] |
| 写数据 | z[0:100] = data |
| 读数据 | z[0:100] |
| 查看信息 | z.info |
| 添加属性 | z.attrs["key"] = "val" |
| 只读打开 | zarr.open_group("dir.zarr", mode="r") |