今回は、プロトコルの1つであるバッファーを配列(ndarray)に変換する関数の1つである、np.frombuffer関数について解説していきます。

Pythonが得意とする科学技術計算のアルゴリズムはNumPyもそうですが、高速化のためにCやFortranなどのコンパイルされた低レベルなコードが動いていることが多いです。このようなネイティブコードとPythonコードとをやりとりするために、Buffer Protocolという機構がPythonには備わっています。

例えば、bytesやarray.arrayなどのオブジェクトでは、Cレベルのバイト列を扱うことができるようになっています。

NumPyの関数にも、このようなバイト列を直接扱うことができます。np.frombuffer関数は、メモリのバイト列を直接読み込むため、大容量のデータをコピーせずに処理することが可能で、処理速度の高速化につながります。

np.frombuffer

まずは、この関数のAPIドキュメントから見ていきましょう。

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

params:

|パラメータ名|型|概要| |:———–|:-|:—| |buffer|buffer_like
バッファーに相当するもの|バッファーとして読み込むオブジェクトを指定します。| |dtype|data-type
データ型|(省略可能)初期値float
配列を返すときの要素のデータ型を指定します。| |count|int|(省略可能)初期値-1
いくつのアイテムを読み込むかを指定します。デフォルトの-1では全てのデータを読み込みます。| |offset|int|(省略可能)初期値0
バイト単位で、どこの地点からデータを読み込むかを指定します。| |like|array_like|NumPy配列以外の参照オブジェクトで生成します。
__array_function__プロトコルを継承しているオブジェクトを指定することができます|

returns:

渡されたバッファーを変換した配列を返します。

この関数は、引数bufferとして渡されたbufferを1次元配列に変換するものです。countやoffsetでデータを読み込む個数や開始点を指定できます。また、dtypeで返される配列のデータ型を指定することができます。

この関数を使うことでndarrayに高速変換することが期待できるので、大容量のデータを扱う方にはおすすめです。

今回は、音声ファイル(waveファイル形式)を配列に収納する時間を比べて見ます。まずは下準備です。今回使用したファイルは、録音したステレオサウンドのデータとなります。

In [1]: import numpy as np

In [2]: import wave


In [4]: wf = wave.open('sample_sound.wav')

In [7]: channels = wf.getnchannels()

In [8]: wf.getparams()
Out[8]: _wave_params(nchannels=2, sampwidth=2, framerate=44100, nframes=5980680, comptype='NONE', compname='not compressed')

In [9]: chunk_size = wf.getnframes()

In [10]: chunk_size
Out[10]: 5980680

In [11]: data = wf.readframes(chunk_size) # まずは読み取ったファイルの全てを格納する。  

変数dataに格納したファイルデータを配列に変換していきます。ここで、np.frombufferを使ってみます。同様の関数として、np.fromiterがあるのでそれとの処理速度の差を確かめて見ます。

In [12]: %timeit data2 = np.frombuffer(data, dtype = 'int16')
1.82 µs ± 57.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [13]: %timeit data3 = np.fromiter(data, dtype='int16')
999 ms ± 29.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

np.frombufferのほうが100万倍ほど早い結果になりましたね。スケールが違いすぎてかなり戸惑うところですが、音声処理などをPythonでやりたい場合、まずはnp.frombuffer関数で配列に格納したほうが処理速度の高速化が見込めます。

参考