mcpField
Z
zarr-python

zarr-python

github.com/crazymsnデータ・データベース2335 回インストール22 週間前に更新
リポジトリ

zarr-python について

zarr-pythonは、大規模なN次元配列を保存・処理するPythonライブラリです。チャンク化と圧縮を必要とするデータ・科学計算ワークフロー向けで、NumPy、Dask、Xarrayと連携します。

主な機能

  1. 01N次元配列をチャンク化して圧縮保存
  2. 02配列スライスの読み書き、サイズ変更、追記
  3. 03階層グループによる配列とメタデータの整理
  4. 04ローカル、メモリ、ZIP、クラウドのストレージに対応
  5. 05NumPy、Dask、Xarrayとの連携
  6. 06並列処理向けのシャーディングと同期

活用シーン

  1. 01大規模な多次元データセットのチャンク保存
  2. 02Daskでメモリ容量を超えるデータを並列処理
  3. 03Xarrayでラベル付きデータセットにアクセス
  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 は、チャンク化と圧縮を用いて大規模な N 次元配列を保存するための Python ライブラリです。効率的な並列 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 次元に沿って読み取る場合:

  • チャンク (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. チャンクサイズ: チャンクあたり 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 ドキュメント、高度な使い方、最新情報については以下をご覧ください:

関連ライブラリ:

コメント