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も提供しています。






