静かなる名辞

pythonとプログラミングのこと

2019/03/22:TechAcademyがteratailの質問・回答を盗用していた件
2019/03/26:TechAcademy盗用事件 公式発表と深まる疑念



複数のnumpy配列を同時にシリアライズできるnumpy.savezの使い方を解説

はじめに

 numpy.savezは最近使ってみてけっこう良い感じだったのですが、日本語のわかりやすい説明が少なかったので解説記事を書いてみます。

 なお、以下のドキュメントも併せて参考にしてください。

numpy.savez — NumPy v1.15 Manual

基本的な使い方

 まず、シリアライズする際は以下のようにすることができます。

>>> import numpy as np
>>> a = np.arange(10)
>>> np.savez("dump", a)

 拡張子は自動的に.npzになり、この場合dump.npzというファイル名で保存されます。dump.npzというファイル名を指定するとdump.npzのまま出力されますが、たとえばdump.binなどとするとdump.bin.npzにされるので注意が必要です。

 デシリアライズはnumpy.loadを用います。

>>> import numpy as np
>>> np.load("dump.npz")
<numpy.lib.npyio.NpzFile object at 0x7f7af22d6710>

 numpy.lib.npyio.NpzFileというよくわからない型で返りますが、実体としてはdictのようなものです*1

>>> list(np.load("dump.npz"))
['arr_0']

 dict→list変換では辞書のキーのlistになりますから、"arr_0"というキーでアクセスすれば良いということになります。

>>> np.load("dump.npz")["arr_0"]
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

 ちゃんと元の配列が取り出せました。

 仕掛けとしては、savezの際の位置引数はすべて"arr_0","arr_1",...というキーに変換されています。savezの際のキーワード引数はキーワード引数がそのままキーになるので、こちらを使ったほうが便利です。

>>> a = np.arange(10)
>>> b = np.arange(20).reshape(4,5)
>>> np.savez("dump2", a=a, b=b)
>>> npz = np.load("dump2.npz")
>>> npz["a"]
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> npz["b"]
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19]])

 このように使うことができます。

用途

 たとえば機械学習で使うデータをシリアライズする際に、データとラベルを一緒に保存するとか、学習データとテストデータを一緒に保存するといった用途があります。

 他にも「このデータはひとまとめにしてシリアライズしたい」と思うような場合など様々な用途に幅広く使えます。

numpy.savez_compressed

 numpy.savez_compressedという関数があり、自動的にデータを圧縮してファイル容量を削減してくれます。それでいて使い勝手はまったく同じです。

numpy.savez_compressed — NumPy v1.15 Manual

>>> c = np.arange(15)
>>> d = np.arange(15).reshape(3,5)
>>> np.savez_compressed("dump3", c=c, d=d)
>>> npz = np.load("dump3.npz")
>>> npz["c"]
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14])
>>> npz["d"]
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14]])

 こだわりがなければこちらを使った方がストレージに優しいです。ただし圧縮・解凍に余計な時間がかかりますが、ファイル容量が小さくなる分ストレージからの読み出しが短時間で終わって相殺される可能性もあります。

 難点はちょっと名前が長いことくらいでしょうか。

まとめ

 このように簡単に使えます。複数のnumpy配列をシリアライズしたい場合、辞書に入れてpickleで……とか難しいことを考えずに、こちらを使うことをおすすめします。

*1:collections.abc.Mappingを継承しています