【NumPy】np.frombuffer() – 既存のデータを高速に読み込みndarrayを生成する

NumPyのnumpy.frombuffer(以下np.frombuffer)についてのメモです。

Pythonのいくつかのオブジェクトはbufferというプロトコルを用意しています。bufferは、そのオブジェクトの生のバイト列へのアクセスを可能にしています。直接メモリにアクセスできるため、大規模なデータをコピーせず処理したい場合などに有効です。

np.frombuffer()は、buffer(をサポートしているオブジェクト)を引数にとり、1次元の配列(ndarray)を作るメソッドです。

引数がbufferと言われても馴染みがないかもしれませんが、string型や、read()で読み込んだファイルなどもnp.frombufferの引数になり得ます。

numpy.frombuffer(buffer, dtype=float, count=-1, offset=0)

buffer : bufferへのアクセスをサポートしているオブジェクト。
読み込むオブジェクト。
dtype(オプション) : data type型
返り値となる配列のデータタイプ。デフォルトはfloat型。
count(オプション) : int型
読み込むバイト数。デフォルトは-1で、すべてのデータを読み込むことを示す。
offset(オプション) : int型
オフセット=先頭から何バイトスキップするか。デフォルトは0。

例:

import numpy as np
s = 'hello world'
np.frombuffer(s, dtype='S1', count=5, offset=6)
'''
array(['w', 'o', 'r', 'l', 'd'],
dtype='|S1')
'''




np.frombuffer()はどれだけ速いか

ndarrayを生成するNumPyのメソッドはいくつかありますがnp.array()np.fromiter()np.frombuffer()の処理速度の比較を行ってみました。8バイトfloat型の0を1億回繰り返したPythonの配列(array)からndarrayを生成します。

import numpy as np
import array
from datetime import datetime
test = array.array('d', [0]*100000000)

t = datetime.now()
np.array(test)
print datetime.now() - t

t = datetime.now()
np.fromiter(test, dtype=np.int)
print datetime.now() - t

t = datetime.now()
np.frombuffer(test)
print datetime.now() - t

np.array(): 58.441448 秒
np.fromiter(): 10.787999 秒
np.frombuffer(): 0.003243 秒

np.fromiter()に比べ、np.frombuffer()の方が3326倍速いです。

参考:efficient python array to numpy array conversion (Stack Overflow)

np.fromstring()とnp.frombuffer()の速度比較

次は文字列から配列を作る処理についてnp.fromstring()np.frombuffer()を比較します。’python’を1億回繰り返した文字列から配列を作ります。

s = 'python' * 100000000

t = datetime.now()
np.fromstring(s)
print datetime.now() - t

t = datetime.now()
np.frombuffer(s)
print datetime.now() - t

結果:
np.fromstring(): 0.449931 秒
np.frombuffer(): 0.000220 秒

np.fromstring()に比べ、np.frombuffer()の方が2045倍速いです。

参考:NumPy – What is the difference between frombuffer and fromstring?(Stack Overflow)

バイトオーダの指定

以下はnp.frombuffer()よりdtypeの説明になってしまいますが、バイトオーダの指定について説明します。

マシンのバイトオーダとデータのそれが異なる場合、バイトオーダを明記しなければいけません。バイトオーダは、data typeの属性として指定します。

dt = np.dtype(int)
dt = dt.newbyteorder('>')
np.frombuffer(buf, dtype=dt)

1~2行目の部分は、次のように短く書くこともできます。

dt = np.dtype('>i8')

バイトオーダがビッグエンディアンであれば‘>’(大なり)、リトルエンディアンであれば‘<'(小なり)を指定します。マシンと同じバイトオーダで読み込むならば‘=’(イコール)を指定します。実行結果の表示にdtype=’|S8’のように、‘|’(バーティカルバー)を含んだ表示がでることがありますが、これも’=’と同じくマシンと同じバイトオーダであることを示します。

返り値の配列のデータはバイトスワップ(バイト列の順序の並び替え)されませんが、処理の際は、正しく解釈されるようです。

参考:
SciPy.org – numpy.frombuffer
Bufferプロトコル(Python3.5.1ドキュメント)
bufferオブジェクトとmemoryviewオブジェクト(Python2.7 ドキュメント)

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です