关于 zarr-python
zarr-python 是用于存储大型 N 维数组的 Python 库,适合处理科学数据的开发者。它以分块和压缩存储支持并行 I/O、云端工作流,并可与 NumPy、Dask 和 Xarray 配合使用。
主要功能
- 01分块存储 N 维数组
- 02按块压缩数据
- 03读取、写入、追加和调整数组
- 04支持本地、内存及云存储
- 05组织分层数组与元数据
- 06集成 NumPy、Dask 和 Xarray
适用场景
- 01存储大型科学数据集
- 02按访问模式优化数组读写
- 03通过 Dask 并行处理超出内存的数据
- 04在云存储中管理分块数组
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jackspace/ claudeskillz --skill zarr-python安装后在 Claude Code 中输入/zarr-python确认已加载。
附带文件SKILL.json
详细介绍
Zarr Python
概述
Zarr 是一个 Python 库,用于通过分块和压缩存储大型 N 维数组。使用此技能可实现高效的并行 I/O、云原生工作流,以及与 NumPy、Dask 和 Xarray 的无缝集成。
快速入门
安装
# Using pip
pip install zarr
# Using conda
conda install --channel conda-forge zarr
需要 Python 3.11 或更高版本。若要支持云存储,请安装其他软件包:
pip install s3fs # For S3
pip install gcsfs # For Google Cloud Storage
创建基本数组
import zarr
import numpy as np
# Create a 2D array with chunking and compression
z = zarr.create_array(
store="data/my_array.zarr",
shape=(10000, 10000),
chunks=(1000, 1000),
dtype="f4"
)
# Write data using NumPy-style indexing
z[:, :] = np.random.random((10000, 10000))
# Read data
data = z[0:100, 0:100] # Returns NumPy array
核心操作
创建数组
Zarr 提供了多个便捷的数组创建函数:
# Create empty array
z = zarr.zeros(shape=(10000, 10000), chunks=(1000, 1000), dtype='f4',
store='data.zarr')
# Create filled arrays
z = zarr.ones((5000, 5000), chunks=(500, 500))
z = zarr.full((1000, 1000), fill_value=42, chunks=(100, 100))
# Create from existing data
data = np.arange(10000).reshape(100, 100)
z = zarr.array(data, chunks=(10, 10), store='data.zarr')
# Create like another array
z2 = zarr.zeros_like(z) # Matches shape, chunks, dtype of z
打开现有数组
# Open array (read/write mode by default)
z = zarr.open_array('data.zarr', mode='r+')
# Read-only mode
z = zarr.open_array('data.zarr', mode='r')
# The open() function auto-detects arrays vs groups
z = zarr.open('data.zarr') # Returns Array or Group
读取和写入数据
Zarr 数组支持类似 NumPy 的索引:
# Write entire array
z[:] = 42
# Write slices
z[0, :] = np.arange(100)
z[10:20, 50:60] = np.random.random((10, 10))
# Read data (returns NumPy array)
data = z[0:100, 0:100]
row = z[5, :]
# Advanced indexing
z.vindex[[0, 5, 10], [2, 8, 15]] # Coordinate indexing
z.oindex[0:10, [5, 10, 15]] # Orthogonal indexing
z.blocks[0, 0] # Block/chunk indexing
调整大小和追加
# Resize array
z.resize(15000, 15000) # Expands or shrinks dimensions
# Append data along an axis
z.append(np.random.random((1000, 10000)), axis=0) # Adds rows
分块策略
分块对性能至关重要。请根据访问模式选择分块大小和形状。
分块大小指南
- 最小分块大小:建议为 1 MB,以获得最佳性能
- 权衡:分块越大,元数据操作越少;分块越小,并行访问效果越好
- 内存注意事项:压缩期间,整个分块必须能装入内存
# Configure chunk size (aim for ~1MB per chunk)
# For float32 data: 1MB = 262,144 elements = 512×512 array
z = zarr.zeros(
shape=(10000, 10000),
chunks=(512, 512), # ~1MB chunks
dtype='f4'
)
根据访问模式调整分块
关键:数据访问方式会显著影响分块形状对性能的影响。
# If accessing rows frequently (first dimension)
z = zarr.zeros((10000, 10000), chunks=(10, 10000)) # Chunk spans columns
# If accessing columns frequently (second dimension)
z = zarr.zeros((10000, 10000), chunks=(10000, 10)) # Chunk spans rows
# For mixed access patterns (balanced approach)
z = zarr.zeros((10000, 10000), chunks=(1000, 1000)) # Square chunks
性能示例:对于形状为 (200, 200, 200) 的数组,沿第一个维度读取:
- 使用分块 (1, 200, 200):约 107ms
- 使用分块 (200, 200, 1):约 1.65ms(快 65 倍!)
面向大规模存储的分片
当数组包含数百万个小分块时,可使用分片将分块分组到较大的存储对象中:
from zarr.codecs import ShardingCodec, BytesCodec
from zarr.codecs.blosc import BloscCodec
# Create array with sharding
z = zarr.create_array(
store='data.zarr',
shape=(100000, 100000),
chunks=(100, 100), # Small chunks for access
shards=(1000, 1000), # Groups 100 chunks per shard
dtype='f4'
)
优点:
- 减少数百万个小文件带来的文件系统开销
- 提升云存储性能(减少对象请求)
- 避免浪费文件系统块大小
重要:写入前,整个分片必须能装入内存。
压缩
Zarr 对每个分块单独压缩,以减少存储空间,同时保持快速访问。
配置压缩
from zarr.codecs.blosc import BloscCodec
from zarr.codecs import GzipCodec, ZstdCodec
# Default: Blosc with Zstandard
z = zarr.zeros((1000, 1000), chunks=(100, 100)) # Uses default compression
# Configure Blosc codec
z = zarr.create_array(
store='data.zarr',
shape=(1000, 1000),
chunks=(100, 100),
dtype='f4',
codecs=[BloscCodec(cname='zstd', clevel=5, shuffle='shuffle')]
)
# Available Blosc compressors: 'blosclz', 'lz4', 'lz4hc', 'snappy', 'zlib', 'zstd'
# Use Gzip compression
z = zarr.create_array(
store='data.zarr',
shape=(1000, 1000),
chunks=(100, 100),
dtype='f4',
codecs=[GzipCodec(level=6)]
)
# Disable compression
z = zarr.create_array(
store='data.zarr',
shape=(1000, 1000),
chunks=(100, 100),
dtype='f4',
codecs=[BytesCodec()] # No compression
)
压缩性能提示
- Blosc(默认):压缩和解压速度快,适合交互式工作负载
- Zstandard:压缩率更高,速度略慢于 LZ4
- Gzip:压缩率最高,但性能较慢
- LZ4:压缩速度最快,但压缩率较低
- Shuffle:对数值数据启用 shuffle 过滤器,可提高压缩效果
# Optimal for numeric scientific data
codecs=[BloscCodec(cname='zstd', clevel=5, shuffle='shuffle')]
# Optimal for speed
codecs=[BloscCodec(cname='lz4', clevel=1)]
# Optimal for compression ratio
codecs=[GzipCodec(level=9)]
存储后端
Zarr 通过灵活的存储接口支持多种存储后端。
本地文件系统(默认)
from zarr.storage import LocalStore
# Explicit store creation
store = LocalStore('data/my_array.zarr')
z = zarr.open_array(store=store, mode='w', shape=(1000, 1000), chunks=(100, 100))
# Or use string path (creates LocalStore automatically)
z = zarr.open_array('data/my_array.zarr', mode='w', shape=(1000, 1000),
chunks=(100, 100))
内存存储
from zarr.storage import MemoryStore
# Create in-memory store
store = MemoryStore()
z = zarr.open_array(store=store, mode='w', shape=(1000, 1000), chunks=(100, 100))
# Data exists only in memory, not persisted
ZIP 文件存储
from zarr.storage import ZipStore
# Write to ZIP file
store = ZipStore('data.zip', mode='w')
z = zarr.open_array(store=store, mode='w', shape=(1000, 1000), chunks=(100, 100))
z[:] = np.random.random((1000, 1000))
store.close() # IMPORTANT: Must close ZipStore
# Read from ZIP file
store = ZipStore('data.zip', mode='r')
z = zarr.open_array(store=store)
data = z[:]
store.close()
云存储(S3、GCS)
import s3fs
import zarr
# S3 storage
s3 = s3fs.S3FileSystem(anon=False) # Use credentials
store = s3fs.S3Map(root='my-bucket/path/to/array.zarr', s3=s3)
z = zarr.open_array(store=store, mode='w', shape=(1000, 1000), chunks=(100, 100))
z[:] = data
# Google Cloud Storage
import gcsfs
gcs = gcsfs.GCSFileSystem(project='my-project')
store = gcsfs.GCSMap(root='my-bucket/path/to/array.zarr', gcs=gcs)
z = zarr.open_array(store=store, mode='w', shape=(1000, 1000), chunks=(100, 100))
云存储最佳实践:
- 使用合并元数据以减少延迟:
zarr.consolidate_metadata(store) - 让分块大小与云对象大小相匹配(通常以 5-100 MB 为宜)
- 对大规模数据使用 Dask 启用并行写入
- 考虑使用分片来减少对象数量
组和层级结构
组以层级方式组织多个数组,类似于目录或 HDF5 组。
创建和使用组
# Create root group
root = zarr.group(store='data/hierarchy.zarr')
# Create sub-groups
temperature = root.create_group('temperature')
precipitation = root.create_group('precipitation')
# Create arrays within groups
temp_array = temperature.create_array(
name='t2m',
shape=(365, 720, 1440),
chunks=(1, 720, 1440),
dtype='f4'
)
precip_array = precipitation.create_array(
name='prcp',
shape=(365, 720, 1440),
chunks=(1, 720, 1440),
dtype='f4'
)
# Access using paths
array = root['temperature/t2m']
# Visualize hierarchy
print(root.tree())
# Output:
# /
# ├── temperature
# │ └── t2m (365, 720, 1440) f4
# └── precipitation
# └── prcp (365, 720, 1440) f4
兼容 h5py 的 API
Zarr 为熟悉 HDF5 的用户提供兼容 h5py 的接口:
# Create group with h5py-style methods
root = zarr.group('data.zarr')
dataset = root.create_dataset('my_data', shape=(1000, 1000), chunks=(100, 100),
dtype='f4')
# Access like h5py
grp = root.require_group('subgroup')
arr = grp.require_dataset('array', shape=(500, 500), chunks=(50, 50), dtype='i4')
属性和元数据
使用属性为数组和组附加自定义元数据:
# Add attributes to array
z = zarr.zeros((1000, 1000), chunks=(100, 100))
z.attrs['description'] = 'Temperature data in Kelvin'
z.attrs['units'] = 'K'
z.attrs['created'] = '2024-01-15'
z.attrs['processing_version'] = 2.1
# Attributes are stored as JSON
print(z.attrs['units']) # Output: K
# Add attributes to groups
root = zarr.group('data.zarr')
root.attrs['project'] = 'Climate Analysis'
root.attrs['institution'] = 'Research Institute'
# Attributes persist with the array/group
z2 = zarr.open('data.zarr')
print(z2.attrs['description'])
重要:属性必须可序列化为 JSON(字符串、数字、列表、字典、布尔值、null)。
与 NumPy、Dask 和 Xarray 集成
NumPy 集成
Zarr 数组实现了 NumPy 数组接口:
import numpy as np
import zarr
z = zarr.zeros((1000, 1000), chunks=(100, 100))
# Use NumPy functions directly
result = np.sum(z, axis=0) # NumPy operates on Zarr array
mean = np.mean(z[:100, :100])
# Convert to NumPy array
numpy_array = z[:] # Loads entire array into memory
Dask 集成
Dask 可对 Zarr 数组进行惰性并行计算:
import dask.array as da
import zarr
# Create large Zarr array
z = zarr.open('data.zarr', mode='w', shape=(100000, 100000),
chunks=(1000, 1000), dtype='f4')
# Load as Dask array (lazy, no data loaded)
dask_array = da.from_zarr('data.zarr')
# Perform computations (parallel, out-of-core)
result = dask_array.mean(axis=0).compute() # Parallel computation
# Write Dask array to Zarr
large_array = da.random.random((100000, 100000), chunks=(1000, 1000))
da.to_zarr(large_array, 'output.zarr')
优点:
- 处理大于内存容量的数据集
- 自动跨分块并行计算
- 通过分块存储实现高效 I/O
Xarray 集成
Xarray 通过 Zarr 后端提供带标签的多维数组:
import xarray as xr
import zarr
# Open Zarr store as Xarray Dataset (lazy loading)
ds = xr.open_zarr('data.zarr')
# Dataset includes coordinates and metadata
print(ds)
# Access variables
temperature = ds['temperature']
# Perform labeled operations
subset = ds.sel(time='2024-01', lat=slice(30, 60))
# Write Xarray Dataset to Zarr
ds.to_zarr('output.zarr')
# Create from scratch with coordinates
ds = xr.Dataset(
{
'temperature': (['time', 'lat', 'lon'], data),
'precipitation': (['time', 'lat', 'lon'], data2)
},
coords={
'time': pd.date_range('2024-01-01', periods=365),
'lat': np.arange(-90, 91, 1),
'lon': np.arange(-180, 180, 1)
}
)
ds.to_zarr('climate_data.zarr')
优点:
- 命名维度和坐标
- 基于标签的索引和选择
- 与 pandas 集成,处理时间序列
- 提供类似 NetCDF 的接口,便于气候和地理空间领域的科学家使用
并行计算与同步
线程安全操作
from zarr import ThreadSynchronizer
import zarr
# For multi-threaded writes
synchronizer = ThreadSynchronizer()
z = zarr.open_array('data.zarr', mode='r+', shape=(10000, 10000),
chunks=(1000, 1000), synchronizer=synchronizer)
# Safe for concurrent writes from multiple threads
# (when writes don't span chunk boundaries)
进程安全操作
from zarr import ProcessSynchronizer
import zarr
# For multi-process writes
synchronizer = ProcessSynchronizer('sync_data.sync')
z = zarr.open_array('data.zarr', mode='r+', shape=(10000, 10000),
chunks=(1000, 1000), synchronizer=synchronizer)
# Safe for concurrent writes from multiple processes
注意:
- 并发读取无需同步
- 只有可能跨越块边界的写入才需要同步
- 每个进程/线程写入不同的块时无需同步
整合元数据
对于包含许多数组的分层存储,将元数据整合到单个文件中,以减少 I/O 操作:
import zarr
# After creating arrays/groups
root = zarr.group('data.zarr')
# ... create multiple arrays/groups ...
# Consolidate metadata
zarr.consolidate_metadata('data.zarr')
# Open with consolidated metadata (faster, especially on cloud storage)
root = zarr.open_consolidated('data.zarr')
优点:
- 将元数据读取操作从 N 次(每个数组一次)减少到 1 次
- 对云存储至关重要(可降低延迟)
- 加快
tree()操作和组遍历
注意事项:
- 如果数组更新后未重新整合,元数据可能会过时
- 不适用于频繁更新的数据集
- 多写入方场景可能导致读取结果不一致
性能优化
最佳性能检查清单
-
块大小:每个块尽量为 1-10 MB
# For float32: 1MB = 262,144 elements chunks = (512, 512) # 512×512×4 bytes = ~1MB -
块形状:与访问模式保持一致
# Row-wise access → chunk spans columns: (small, large) # Column-wise access → chunk spans rows: (large, small) # Random access → balanced: (medium, medium) -
压缩:根据工作负载进行选择
# Interactive/fast: BloscCodec(cname='lz4') # Balanced: BloscCodec(cname='zstd', clevel=5) # Maximum compression: GzipCodec(level=9) -
存储后端:与环境相匹配
# Local: LocalStore (default) # Cloud: S3Map/GCSMap with consolidated metadata # Temporary: MemoryStore -
分片:用于大规模数据集
# When you have millions of small chunks shards=(10*chunk_size, 10*chunk_size) -
并行 I/O:大型操作使用 Dask
import dask.array as da dask_array = da.from_zarr('data.zarr') result = dask_array.compute(scheduler='threads', num_workers=8)
性能分析与调试
# Print detailed array information
print(z.info)
# Output includes:
# - Type, shape, chunks, dtype
# - Compression codec and level
# - Storage size (compressed vs uncompressed)
# - Storage location
# Check storage size
print(f"Compressed size: {z.nbytes_stored / 1e6:.2f} MB")
print(f"Uncompressed size: {z.nbytes / 1e6:.2f} MB")
print(f"Compression ratio: {z.nbytes / z.nbytes_stored:.2f}x")
常见模式与最佳实践
模式:时间序列数据
# Store time series with time as first dimension
# This allows efficient appending of new time steps
z = zarr.open('timeseries.zarr', mode='a',
shape=(0, 720, 1440), # Start with 0 time steps
chunks=(1, 720, 1440), # One time step per chunk
dtype='f4')
# Append new time steps
new_data = np.random.random((1, 720, 1440))
z.append(new_data, axis=0)
模式:大型矩阵运算
import dask.array as da
# Create large matrix in Zarr
z = zarr.open('matrix.zarr', mode='w',
shape=(100000, 100000),
chunks=(1000, 1000),
dtype='f8')
# Use Dask for parallel computation
dask_z = da.from_zarr('matrix.zarr')
result = (dask_z @ dask_z.T).compute() # Parallel matrix multiply
模式:云原生工作流
import s3fs
import zarr
# Write to S3
s3 = s3fs.S3FileSystem()
store = s3fs.S3Map(root='s3://my-bucket/data.zarr', s3=s3)
# Create array with appropriate chunking for cloud
z = zarr.open_array(store=store, mode='w',
shape=(10000, 10000),
chunks=(500, 500), # ~1MB chunks
dtype='f4')
z[:] = data
# Consolidate metadata for faster reads
zarr.consolidate_metadata(store)
# Read from S3 (anywhere, anytime)
store_read = s3fs.S3Map(root='s3://my-bucket/data.zarr', s3=s3)
z_read = zarr.open_consolidated(store_read)
subset = z_read[0:100, 0:100]
模式:格式转换
# HDF5 to Zarr
import h5py
import zarr
with h5py.File('data.h5', 'r') as h5:
dataset = h5['dataset_name']
z = zarr.array(dataset[:],
chunks=(1000, 1000),
store='data.zarr')
# NumPy to Zarr
import numpy as np
data = np.load('data.npy')
z = zarr.array(data, chunks='auto', store='data.zarr')
# Zarr to NetCDF (via Xarray)
import xarray as xr
ds = xr.open_zarr('data.zarr')
ds.to_netcdf('data.nc')
常见问题与解决方案
问题:性能缓慢
诊断:检查块大小和对齐方式
print(z.chunks) # Are chunks appropriate size?
print(z.info) # Check compression ratio
解决方案:
- 将块大小增加到 1-10 MB
- 让块与访问模式保持一致
- 尝试不同的压缩编解码器
- 使用 Dask 执行并行操作
问题:内存占用过高
原因:将整个数组或较大的块加载到内存中
解决方案:
# Don't load entire array
# Bad: data = z[:]
# Good: Process in chunks
for i in range(0, z.shape[0], 1000):
chunk = z[i:i+1000, :]
process(chunk)
# Or use Dask for automatic chunking
import dask.array as da
dask_z = da.from_zarr('data.zarr')
result = dask_z.mean().compute() # Processes in chunks
问题:云存储延迟
解决方案:
# 1. Consolidate metadata
zarr.consolidate_metadata(store)
z = zarr.open_consolidated(store)
# 2. Use appropriate chunk sizes (5-100 MB for cloud)
chunks = (2000, 2000) # Larger chunks for cloud
# 3. Enable sharding
shards = (10000, 10000) # Groups many chunks
问题:并发写入冲突
解决方案:使用同步器,或确保写入区域互不重叠
from zarr import ProcessSynchronizer
sync = ProcessSynchronizer('sync.sync')
z = zarr.open_array('data.zarr', mode='r+', synchronizer=sync)
# Or design workflow so each process writes to separate chunks
更多资源
如需详细的 API 文档、高级用法和最新更新,请参阅:
- 官方文档:https://zarr.readthedocs.io/
- Zarr 规范:https://zarr-specs.readthedocs.io/
- GitHub 仓库:https://github.com/zarr-developers/zarr-python
- 社区聊天:https://gitter.im/zarr-developers/community
相关库: