背景
あるプロジェクトで約1GB程度あるcsvファイルを可視化するWebアプリケーションの作成に携わった。
実際に表示する部分はcsvファイルの一部分であり、csvファイルで管理ではパフォーマンス上問題が多いことがありDBの利用を模索していたが、他のメンバーのスキルや開発環境の統一の面倒からデータをHDF5ファイルで管理することが選択された。
しかし実際にHDF5のファイルを生成すると考えていたよりも大きなファイルとなったので調査してみた。
前提条件
CSVファイル
今回比較するファイルは使用するファイルは横200列程度、縦1万行程度のcsvファイルであり基本float64型だが10列程度string型があるファイルとなっており、約200MBのファイルです。
HDF5ファイル
表形式ではなく、1列ごとにDatasetを作成する形となっています。
圧縮方法について
HDF5を扱うためにPython
+ h5py
を利用しています。
参考資料
圧縮する際はcreate_dataset
やrequire_dataset
関数を呼び出す際にcompression="gzip"
とchunks=True
を指定することでいい感じにチャンク単位で圧縮されます。
ドキュメント
例) float64
group.require_dataset(name=col_name, shape=(data.index.size,), maxshape=(None,), dtype=np.float64, chunks=True, compression="gzip")
例) string
group.require_dataset(name=col_name, shape=(data.index.size,), maxshape=(None,), dtype=h5py.string_dtype(), chunks=True, compression="gzip")
本題
前提条件での条件でHDF5ファイルを生成すると約198.3MBのファイルが生成されました。
個人的には全て文字列としているCSVファイルに比べて変化がないのはよく分からなかったので調査をしました。
調査1 数値データだけにする
文字列を含む列を全て取り除き、HDF5ファイルを作成してみました。
結果、10MB程度とかなり小さくなることがわかったので原因は文字列の扱いにあると考えました。
調査2 文字列に関する取り扱いを変化させる
文字列は公式ドキュメントで1章分作られるほど取り扱いが面倒なようです。
https://docs.h5py.org/en/stable/strings.html
そこで固定長データとして扱うことにしてみました。
group.require_dataset(name=col_name, shape=(data.index.size,), maxshape=(None,), dtype=h5py.string_dtype(length=30), chunks=True, compression="gzip")
string_dtype(length=30)
を追加しています。
このlength
は文字列の最大長として扱われ、この長さを超えると文字は全て切り捨てられます。
この結果19MB程度とかなり小さくなりました。
調査3 lengthはサイズに影響するのか
調査2で指定したstring_dtype(length=30)
ですが、lengthを変化させるとサイズに影響するのか確かめました。
length=20
とかなりギリギリに指定してみた結果18.5MBとあまり変化が無いようでした。
この結果より空文字となっているところがgzipによりうまく圧縮されているようです。
そのためlengthは安全寄りの数値を選択することを推奨します。
まとめ
最後に結果をまとめます。
圧縮なし | gzipのみ | length=30のみ | gzip + length=30 | |
---|---|---|---|---|
サイズ | 385.9MB | 198.3MB | 308.2MB | 19MB |
圧縮率 | -- | 49% | 20% | 95% |
固定長で圧縮をかけると全然違いますね。
結論
HDF5で文字列を扱う際は可変長ではなく固定長を使おう!