Zarr形式とは?HDF5・Parquetとの違いと選び方をPythonで比較

Zarr形式は、大きな多次元配列を小さな塊(チャンク)に分けて圧縮し、ファイルやクラウドのストレージに保存する形式です。1つのファイルに階層構造ごと収めるのがHDF5、表形式のデータを列ごとに圧縮して保存するのがParquetで、「配列ならZarrかHDF5、表ならParquet」が基本の選び方です。

この記事では、3つの形式の違いを整理し、同じデータをPythonで実際に書き出して、ファイルの大きさ・ファイル数・部分読み込みの書き方を比べます。実行環境はPython 3.14.8、h5py 3.16.0、zarr 3.4.1、pyarrow 26.0.0、pandas 3.0.6です(2026年10月14日確認)。なお、Zarrの公式サイトに読み方の表記はないため、この記事では「Zarr」と書きます。

3つの形式の違いを一言でいうと

HDF5 Parquet Zarr
得意なデータ 多次元配列と、それをまとめる階層構造 表(行と列のデータ) 大きな多次元配列
保存のされ方 1つのファイル(.h5 / .hdf5) 1つのファイル(.parquet)。複数ファイルに分けることも多い ディレクトリと多数のファイル(チャンクごと)。シャーディングで束ねられる
部分読み込み 配列の一部をスライスで読める 必要な列・条件に合う行だけ読める 配列の一部をスライスで読める(必要なチャンクだけ取得)
主な用途 実験・観測データ、シミュレーション結果 データ分析、データ基盤、BIツール 気候・衛星データ、顕微鏡画像、クラウド上の大規模配列
運営・仕様 The HDF Group(BSDスタイルのライセンス) Apache Software Foundation コミュニティ(仕様はv3.1。NumFOCUSが財政支援)

出典:The HDF Group「The HDF5 Library & File Format」、Apache Parquet「Overview」、zarr.dev、Zarr core specification(いずれも2026年10月14日確認)

Zarr形式とは

Zarrの公式サイトは、Zarrを「チャンク化・圧縮されたN次元配列のためのオープンな基盤」と説明しています。仕様(フォーマットの定義)と、それを読み書きするライブラリが分かれているのが特徴で、Python・Rust・C++・Java・JavaScript・Juliaなど10言語の実装が紹介されています(zarr.dev)。

Zarrの配列は、次のような構造で保存されます。

  • 配列の形・データ型・チャンクの大きさ・圧縮方法などを書いたメタデータ(zarr.json)
  • チャンクごとに分かれた圧縮済みのデータ(1チャンク=1ファイル、またはシャードにまとめたファイル)

読み込むときは、必要な範囲に対応するチャンクだけを取りにいきます。ファイルシステムだけでなく、Amazon S3のようなオブジェクトストレージに置いたまま一部だけ読めるのが、Zarrがクラウド時代の配列形式として使われている理由です。

Zarr v2とv3の違い(zarr-python 3)

現在のPythonライブラリ zarr-python 3系では、新しく作る配列は既定で仕様のバージョン3(v3)になります。v2で作りたい場合は zarr_format=2 を指定します(3.0 Migration Guide)。古い記事で見かける zarr.Array を直接作る書き方や zarr_version 引数は使えなくなっているため、zarr.create_array や zarr.open_array を使います。PyPIによると、2026年10月8日公開のzarr 3.4.1はPython 3.12以上が必要です(PyPI: zarr、2026年10月14日確認)。

同じ配列をHDF5とZarrで保存して比べる

365日分の気温の格子データ(365×180×360、float32)を想定した配列を作り、1日分ずつのチャンクに分けて保存します。HDF5はgzip圧縮、Zarrは既定の圧縮(zstd)です。

import numpy as np, h5py, zarr, pandas as pd, os
print("h5py", h5py.__version__, "zarr", zarr.__version__, "pandas", pd.__version__)
rng = np.random.default_rng(0)
# 気温のような格子データ:365日 x 180 x 360
t = (15 + 10*np.sin(np.linspace(0, 2*np.pi, 365))[:, None, None]
     + rng.normal(0, 1, (365, 180, 360))).astype("float32")
print("配列", t.shape, t.dtype, f"{t.nbytes/1e6:.1f} MB")

with h5py.File("temp.h5", "w") as f:
    d = f.create_dataset("temperature", data=t, chunks=(1, 180, 360), compression="gzip")
    d.attrs["units"] = "degC"

z = zarr.create_array("temp.zarr", shape=t.shape, chunks=(1, 180, 360), dtype="float32", overwrite=True)
z[:] = t
z.attrs["units"] = "degC"

def du(p):
    if os.path.isfile(p): return os.path.getsize(p)
    return sum(os.path.getsize(os.path.join(r, x)) for r, _, fs in os.walk(p) for x in fs)
def nfiles(p):
    return 1 if os.path.isfile(p) else sum(len(fs) for _, _, fs in os.walk(p))
for p in ["temp.h5", "temp.zarr"]:
    print(p, f"{du(p)/1e6:.1f} MB", "ファイル数", nfiles(p))

with h5py.File("temp.h5", "r") as f:
    day100 = f["temperature"][100]
    print("HDF5 100日目", day100.shape, f["temperature"].attrs["units"])
z2 = zarr.open_array("temp.zarr", mode="r")
print("Zarr 100日目", z2[100].shape, z2.attrs["units"], np.array_equal(z2[100], day100))
print(z2.info)

実行結果です。

h5py 3.16.0 zarr 3.4.1 pandas 3.0.6
配列 (365, 180, 360) float32 94.6 MB
temp.h5 78.9 MB ファイル数 1
temp.zarr 78.4 MB ファイル数 366
HDF5 100日目 (180, 360) degC
Zarr 100日目 (180, 360) degC True
Type               : Array
Zarr format        : 3
Data type          : Float32(endianness='little')
Fill value         : 0.0
Shape              : (365, 180, 360)
Chunk shape        : (1, 180, 360)
Order              : C
Read-only          : True
Store type         : LocalStore
Filters            : ()
Serializer         : BytesCodec(endian='little')
Compressors        : (ZstdCodec(level=0, checksum=False),)
No. bytes          : 94608000 (90.2M)

乱数を含むデータなので、どちらも圧縮はあまり効いていません(約94.6MBが約79MB)。大きな違いはファイル数です。HDF5は1ファイルに収まりますが、Zarrはチャンクの数(365)にメタデータ1つを加えた366ファイルになりました。100日目だけを読み出すコードはどちらもスライスで書け、読み出した値は一致しています。

ファイル数が多すぎるときはシャーディング

チャンクが細かいとファイルが何万個にもなり、ファイルシステムやストレージの扱いが重くなります。Zarr v3には、複数のチャンクを1つのファイル(シャード)にまとめるシャーディングという仕組みが仕様として用意されています(Sharding codec)。

import numpy as np, zarr, os
t = np.random.default_rng(0).normal(15, 1, (365, 180, 360)).astype("float32")
z = zarr.create_array("temp_sharded.zarr", shape=t.shape, dtype="float32",
                      chunks=(1, 180, 360), shards=(73, 180, 360), overwrite=True)
z[:] = t
n = sum(len(fs) for _, _, fs in os.walk("temp_sharded.zarr"))
print("ファイル数", n)
print(np.array_equal(zarr.open_array("temp_sharded.zarr")[100], t[100]))
ファイル数 6
True

73日分のチャンクを1つのシャードにまとめたので、データファイルは5つ、メタデータを含めて6ファイルになりました。読み込みは引き続きチャンク単位で行えます。

HDF5:1つのファイルに階層ごとまとめる

HDF5は、フォルダのような「グループ」と、配列を入れる「データセット」を組み合わせて、1つのファイルの中に階層構造を作れます。グループやデータセットには属性(attrs)として単位や日付などのメタデータを付けられます。

import h5py, numpy as np
with h5py.File("experiment.h5", "w") as f:
    run = f.create_group("run_001")
    run.attrs["date"] = "2026-10-14"
    run.create_dataset("temperature", data=np.arange(5, dtype="f4"))
    run.create_dataset("pressure", data=np.linspace(1000, 1004, 5))
with h5py.File("experiment.h5", "r") as f:
    f.visit(print)
    print(dict(f["run_001"].attrs), f["run_001/pressure"][:])
run_001
run_001/pressure
run_001/temperature
{'date': '2026-10-14'} [1000. 1001. 1002. 1003. 1004.]

実験ごと・装置ごとにデータを整理して1つのファイルで受け渡したいときに便利です。一方で、1つのファイルを複数のプロセスから同時に書き換える使い方や、クラウドのオブジェクトストレージ上で一部だけを読む使い方は、ZarrやParquetほど手軽ではありません。h5pyでの圧縮や部分読み込みの詳細はh5pyとは?HDF5をPythonで扱う方法、pandasから使う方法はPandasでHDF5を使いこなすで解説しています。

Parquet:表データを列ごとに圧縮する

Parquetは列指向のファイル形式で、効率的な保存と読み出しのために、圧縮とエンコードの仕組みを備えています(Apache Parquet「Overview」)。100万行の観測データの表をCSVとParquetで保存し、必要な列と行だけを読み込んでみます。

import numpy as np, pandas as pd, pyarrow as pa, pyarrow.parquet as pq, os
print("pyarrow", pa.__version__)
rng = np.random.default_rng(0)
n = 1_000_000
df = pd.DataFrame({
    "date": pd.date_range("2026-01-01", periods=365).repeat(n // 365 + 1)[:n],
    "station": rng.choice(["tokyo", "osaka", "sapporo", "fukuoka"], n),
    "temp": rng.normal(15, 8, n).round(1),
    "humidity": rng.integers(20, 100, n),
})
df.to_parquet("obs.parquet")            # 既定の圧縮は snappy
df.to_csv("obs.csv", index=False)
for p in ["obs.csv", "obs.parquet"]:
    print(p, f"{os.path.getsize(p)/1e6:.1f} MB")

meta = pq.ParquetFile("obs.parquet").metadata
print("行数", meta.num_rows, "行グループ数", meta.num_row_groups,
      "圧縮", meta.row_group(0).column(0).compression)

# 必要な列だけ・必要な行だけ読む
sub = pd.read_parquet("obs.parquet", columns=["station", "temp"],
                      filters=[("station", "==", "sapporo")])
print(sub.shape)
print(sub.groupby("station")["temp"].mean().round(2))
pyarrow 26.0.0
obs.csv 25.8 MB
obs.parquet 2.4 MB
行数 1000000 行グループ数 1 圧縮 SNAPPY
(249974, 2)
station
sapporo    14.98
Name: temp, dtype: float64

同じ表がCSVでは25.8MB、Parquet(既定のsnappy圧縮)では2.4MBになりました。地点名のように同じ値が繰り返される列は、Parquetでは辞書の番号として保存されるため、特に小さくなります。columns で列を、filters で行の条件を指定すると、必要な部分だけを読み込めます。

Parquetは表を扱うための形式なので、上の気温のような3次元配列を保存するには「日付・緯度・経度・値」の長い表に並べ替える必要があります。配列のまま扱いたいならHDF5かZarrのほうが自然です。

用途別の選び方

やりたいこと 選ぶ形式 理由
CSVの代わりに表データを小さく速く保存したい Parquet 列ごとの圧縮と、列・行を絞った読み込みができる
分析ツールやほかのプログラミング言語と表データを受け渡す Parquet 公式の説明でも多くの言語・分析ツールが対応しているとされる
実験データを階層構造ごと1ファイルで渡したい HDF5 グループと属性で整理でき、ファイルが1つで済む
既存の装置・ソフトがHDF5を出力する HDF5 変換の手間がかからない
クラウドに置いた巨大な配列の一部だけを読みたい Zarr 必要なチャンクだけを取得できる
複数のプロセスで配列の別々の部分を並列に書きたい Zarr チャンクが別々に保存されるため、書き込み先が分かれる

迷ったら、まず「データは表か、配列か」を決めます。表ならParquet。配列なら、ローカルで1ファイルにまとめたいときはHDF5、クラウドや並列処理を前提にするならZarr、という順で考えると決めやすくなります。

こうしたデータの保存形式の選択は、Pythonでデータを扱う実務では頻繁に出てきます。テックジムは東京・永田町に本校がある通学型のプログラミングスクールで、自分のペースで課題を進め、詰まったときにトレーナーに質問できます。まずは無料講座でPythonに触れてみるのも一つの方法です。

よくある質問

Q. Zarr形式とは何ですか?

大きな多次元配列をチャンクに分けて圧縮し、ファイルシステムやクラウドのストレージに保存する形式です。必要な範囲のチャンクだけを読み書きできます。仕様と、各言語のライブラリが分かれています。

Q. Zarrの読み方は?

公式サイト(zarr.dev)には読み方の表記が見当たりません。この記事では英字のまま「Zarr」と表記しています。

Q. HDF5とParquetはどちらが速いですか?

得意なデータが違うため一概には比べられません。表データで一部の列だけを読むならParquet、多次元配列の一部をスライスで読むならHDF5が向いています。実際のデータで試して決めるのが確実です。

Q. ZarrとHDF5の違いは何ですか?

どちらも多次元配列をチャンク単位で圧縮して保存できますが、HDF5は1つのファイルにまとめ、Zarrはチャンクごとに別のファイル(またはシャード)に保存します。そのためZarrはクラウドのストレージや並列書き込みと相性がよく、HDF5はファイル1つで受け渡せるのが利点です。

Q. .h5ファイルは何で開けますか?

Pythonなら h5py で開けます。グループやデータセットの一覧は visit() で確認できます。The HDF Groupは、中身をGUIで確認できるHDFViewも提供しています。

プログラミング学習に興味がある方は、まずは無料体験会へ

無料体験会・お申し込みはこちら