mcpField
Z
zarr-python

zarr-python

github.com/crazymsn数据与数据库2335 次安装22 周前更新
代码仓库

关于 zarr-python

zarr-python 是用于存储和处理大型 N 维数组的 Python 库,适合需要分块压缩存储的数据与科学计算工作流,并可与 NumPy、Dask 和 Xarray 集成。

主要功能

  1. 01以分块和压缩方式存储 N 维数组
  2. 02读取和写入数组切片,并支持调整大小与追加
  3. 03通过层级组组织数组及元数据
  4. 04支持本地、内存、ZIP 和云存储后端
  5. 05集成 NumPy、Dask 和 Xarray
  6. 06为并行工作负载提供分片和同步机制

适用场景

  1. 01分块存储大型多维数据集
  2. 02用 Dask 并行处理超出内存容量的数据
  3. 03通过 Xarray 访问带标签的数据集
  4. 04利用分片和集中元数据减少云存储请求

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/crazymsn/academic-skills --skill zarr-python

安装后在 Claude Code 中输入/zarr-python确认已加载。

附带文件references/api_reference.md

详细介绍

Zarr Python

概述

Zarr 是一个 Python 库,用于通过分块和压缩存储大型 N 维数组。使用此技能可实现高效的并行 I/O、云原生工作流,以及与 NumPy、Dask 和 Xarray 的无缝集成。

快速入门

安装
uv pip install zarr

需要 Python 3.11 或更高版本。若要支持云存储,请安装其他软件包:

uv pip install s3fs  # For S3
uv 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. 块大小:每个块尽量为 1-10 MB

    # For float32: 1MB = 262,144 elements
    chunks = (512, 512)  # 512×512×4 bytes = ~1MB
    
  2. 块形状:与访问模式保持一致

    # Row-wise access → chunk spans columns: (small, large)
    # Column-wise access → chunk spans rows: (large, small)
    # Random access → balanced: (medium, medium)
    
  3. 压缩:根据工作负载进行选择

    # Interactive/fast: BloscCodec(cname='lz4')
    # Balanced: BloscCodec(cname='zstd', clevel=5)
    # Maximum compression: GzipCodec(level=9)
    
  4. 存储后端:与环境相匹配

    # Local: LocalStore (default)
    # Cloud: S3Map/GCSMap with consolidated metadata
    # Temporary: MemoryStore
    
  5. 分片:用于大规模数据集

    # When you have millions of small chunks
    shards=(10*chunk_size, 10*chunk_size)
    
  6. 并行 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 文档、高级用法和最新更新,请参阅:

相关库:

评论

支持 Markdown