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]