NumPy配列ndarrayをバイナリファイル(npy, npz)で保存
NumPyでは、配列ndarrayをNumPy独自フォーマットのバイナリファイル(npy, npz)で保存できる。データ型dtypeや形状shapeなどの情報を保持したまま書き込み・読み込み(出力・入力)ができる。
バイナリファイルではなくCSV(カンマ区切り)やTSV(タブ区切り)などのテキストファイルで読み書きしたい場合は以下の記事を参照。
本記事のサンプルコードのNumPyのバージョンは以下の通り。バージョンによって仕様が異なる可能性があるので注意。
import numpy as np
print(np.__version__)
# 1.26.1
npy, npzで保存するメリットとデメリット
メリット
NumPy配列ndarrayをNumPy独自フォーマットのバイナリファイル(npy, npz)で保存する場合、データ型dtypeや形状shapeなどの情報がそのまま保存される。
例えば、三次元以上の多次元配列はCSVなどのテキストファイルではそのまま保存できないので形状を変換するといった処理が必要だが、バイナリでは特に考慮しなくてもよい。
また、テキストファイルで保存する場合は小数点以下の桁数の設定によって値が丸まってしまったりするが、バイナリではデータがそのまま保存される。
デメリット
バイナリファイル(npy, npz)のフォーマットは公開されているが、基本的にはNumPyでのみ使用が可能。
CSVファイルのように他のアプリケーションで開いて中身を確認したり編集したりできない。
npy, npzを読み込み: np.load()
バイナリファイル(npy, npz)の読み込みにはnp.load()を使う。
基本的には引数にファイルのパスを指定するだけだが、npy(一つの配列を格納)とnpz(複数の配列を格納)で扱いが異なる。
それぞれの場合の処理方法は以下のnp.save(), np.savez(), np.savez_compressed()と合わせて説明する。
一つのndarrayをnpyで保存: np.save()
一つの配列ndarrayをバイナリファイルとして保存するにはnp.save()を使う。
以下のndarrayを例とする。
a = np.arange(6, dtype=np.int8).reshape(1, 2, 3)
print(a)
# [[[0 1 2]
# [3 4 5]]]
print(a.shape)
# (1, 2, 3)
print(a.dtype)
# int8
np.save()の第一引数にパス文字列またはpathlib.Path、第二引数に保存したいndarrayを指定する。
np.save('data/temp/np_save', a)
第一引数に指定したパスに拡張子.npyが付与されたファイル名で保存される。指定したパスに.npyが含まれていればそのまま使われる。
読み込みはnp.load()。ndarrayがそのまま返される。データ型dtypeや形状shapeが保持されていることが確認できる。
a_load = np.load('data/temp/np_save.npy')
print(a_load)
# [[[0 1 2]
# [3 4 5]]]
print(a_load.shape)
# (1, 2, 3)
print(a_load.dtype)
# int8
複数のndarrayをnpzで保存: np.savez()
複数の配列ndarrayをまとめて一つのバイナリファイルに保存するにはnp.savez()を使う。
以下の2つのndarrayを例とする。なお、ここでは特に確認しないが、npzでも上述のnpyと同じくndarrayのデータ型dtypeや形状shapeがそのまま保持される。
a1 = np.arange(5)
print(a1)
# [0 1 2 3 4]
a2 = np.arange(5, 10)
print(a2)
# [5 6 7 8 9]
第一引数にパス文字列またはpathlib.Path、以降に保存したいndarrayをカンマ区切りで指定する。例はndarrayが2つだけだが、可変長引数になっているので、3つ以上の場合もカンマ区切りで指定していけばよい。
np.savez('data/temp/np_savez', a1, a2)
第一引数に指定したパスに拡張子.npzが付与されたファイル名で保存される。指定したパスに.npzが含まれていればそのまま使われる。
読み込みはnpyと同じくnp.load()を使うが、返されるのはNpzFileオブジェクト。
npz = np.load('data/temp/np_savez.npz')
print(type(npz))
# <class 'numpy.lib.npyio.NpzFile'>
格納されたndarrayを取得するには、各ndarrayの名前を[]で指定する。各ndarrayの名前はfiles属性で確認できる。
print(npz.files)
# ['arr_0', 'arr_1']
print(npz['arr_0'])
# [0 1 2 3 4]
print(npz['arr_1'])
# [5 6 7 8 9]
デフォルトでは上の例のように保存時に引数に指定した順番にarr_0, arr_1...という名前が自動的に付けられる。
np.savez()でndarrayを指定するときにキーワード引数の形にすると、任意の名前を付けることが可能。多数のndarrayをまとめて保存する場合は分かりやすい名前を付けておくと便利。
np.savez('data/temp/np_savez_kw', x=a1, y=a2)
npz_kw = np.load('data/temp/np_savez_kw.npz')
print(npz_kw.files)
# ['x', 'y']
print(npz_kw['x'])
# [0 1 2 3 4]
print(npz_kw['y'])
# [5 6 7 8 9]
あまり使うことはないかもしれないが、以下のように一部のndarrayのみキーワード引数で名前を付けることもできる。
np.savez('data/temp/np_savez_kw2', a1, y=a2)
npz_kw2 = np.load('data/temp/np_savez_kw2.npz')
print(npz_kw2.files)
# ['y', 'arr_0']
print(npz_kw2['arr_0'])
# [0 1 2 3 4]
print(npz_kw2['y'])
# [5 6 7 8 9]
複数のndarrayを圧縮してnpzで保存: np.savez_compressed()
np.savez_compressed()はnp.savez()と同じく複数の配列ndarrayをまとめて一つのバイナリファイルに保存する。
唯一の違いはnp.savez_compressed()は圧縮して保存するということ。出力されるファイルのサイズがnp.savez()よりも小さくなる。
拡張子はnp.savez()と同じ.npzで、np.load()での扱いも同じ。
np.savez_compressed('data/temp/np_savez_comp', a1, a2)
npz_comp = np.load('data/temp/np_savez_comp.npz')
print(type(npz_comp))
# <class 'numpy.lib.npyio.NpzFile'>
print(npz_comp.files)
# ['arr_0', 'arr_1']
print(npz_comp['arr_0'])
# [0 1 2 3 4]
print(npz_comp['arr_1'])
# [5 6 7 8 9]
キーワード引数も使える。
np.savez_compressed('data/temp/np_savez_comp_kw', x=a1, y=a2)
npz_comp_kw = np.load('data/temp/np_savez_comp_kw.npz')
print(npz_comp_kw.files)
# ['x', 'y']
print(npz_comp_kw['x'])
# [0 1 2 3 4]
print(npz_comp_kw['y'])
# [5 6 7 8 9]