zarr-python について
zarr-pythonは、大規模なN次元配列を扱うPythonライブラリです。科学データの開発者向けに、チャンク化と圧縮による並列I/Oやクラウドワークフローを支援し、NumPy、Dask、Xarrayと連携します。
主な機能
- 01N次元配列のチャンク保存
- 02チャンク単位のデータ圧縮
- 03配列の読み書き・追加・サイズ変更
- 04ローカル・メモリ・クラウドストレージ
- 05階層配列とメタデータの整理
- 06NumPy、Dask、Xarrayとの連携
活用シーン
- 01大規模な科学データセットの保存
- 02アクセスパターンに応じた配列I/Oの最適化
- 03Daskによるメモリ容量を超えるデータの並列処理
- 04クラウドストレージでのチャンク配列管理
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jackspace/ claudeskillz --skill zarr-pythonインストール後、Claude Code で/zarr-pythonを入力して読み込まれたか確認します。
同梱ファイルSKILL.json
詳細
Zarr Python
概要
Zarr は、チャンク化と圧縮を用いて大規模な N 次元配列を保存するための Python ライブラリです。効率的な並列 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 次元に沿って読み取る場合:
- チャンク (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 操作を減らすためにメタデータを 1 つのファイルに統合してください。
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 回)から 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
関連ライブラリ:
- Xarray: https://docs.xarray.dev/ (ラベル付き配列)
- Dask: https://docs.dask.org/ (並列計算)
- NumCodecs: https://numcodecs.readthedocs.io/ (圧縮コーデック)