web-dev-qa-db-ja.com

pythonでフロートのリストをバイトにパックする最速の方法

たとえば100kの浮動小数点数のリストがあり、それをバイトバッファに変換したい。

buf = bytes()
for val in floatList:
   buf += struct.pack('f', val)
return buf

これは非常に遅いです。標準のPython 3.xライブラリのみを使用して高速化するにはどうすればよいですか。

25

structの数floatsを教えてください。私の遅いラップトップでは、100kのフロートは約1/100秒かかります。

import random
import struct

floatlist = [random.random() for _ in range(10**5)]
buf = struct.pack('%sf' % len(floatlist), *floatlist)
46
agf

Ctypesを使用して、データをリストに保持する代わりに、Cの場合とまったく同じようにdouble配列(またはfloat配列)を使用できます。これはかなり低いレベルですが、優れたパフォーマンスが必要で、リストのサイズが固定されている場合に推奨されます。

PythonでC double array[100];に相当するものを作成できます。

array = (ctypes.c_double * 100)()

ctypes.c_double * 100式は、Python double、100項目の長さの配列のクラスです。ファイルに配線するには、bufferを使用してその内容を取得できます。

>>> f = open("bla.dat", "wb")
>>> f.write(buffer(array))

あなたのデータがすでにPythonリストにある場合、ダブル配列にパックすることは、Agfの受け入れられた答えでstructasを呼び出すよりも速いかもしれません。宿題のように速くなりますが、必要なコードはすべてこれです:

>>> import ctypes
>>> array = (ctypes.c_double * len(floatlist))(*floatlist)

str(buffer(array))-ここでの1つの欠点は、floatサイズ(float vs double)とCPU依存のfloat型を処理する必要があることです-structモジュールが処理できますこれはあなたのため。

大きな利点は、フロート配列を使用すると、プレーンPythonリストでありながら平面メモリ領域としてすぐに利用できるようにアクセスすることで、要素を数値として使用できることです。 bufferで。

7
jsbueno

いくつかの答えが示唆しています

import struct
buf = struct.pack(f'{len(floatlist)}f', *floatlist)

しかし、 '*'を使用すると、struct.packに渡す前にfloatlistをTupleに不必要に変換します。これは、最初に空のバッファを作成し、次に、私が見つけた最速の方法:スライス割り当てを使用してデータを入力することにより、回避できます。

import ctypes
buf = (ctypes.c_double * len(floatlist))()
buf[:] = floatlist

一部の人々が使用できるその他のパフォーマンスの節約:

  • 新しいバッファを作成せずに、割り当てを再度行うだけで、既存のバッファを再利用できます。
  • 適切なスライスに割り当てることにより、既存のバッファーの一部を変更できます。
2

単精度浮動小数点数の配列には、structまたはarrayを使用する2つのオプションがあります。

In[103]: import random
import struct
from array import array

floatlist = [random.random() for _ in range(10**5)]

In[104]: %timeit struct.pack('%sf' % len(floatlist), *floatlist)
100 loops, best of 3: 2.86 ms per loop

In[105]: %timeit array('f', floatlist).tostring()
100 loops, best of 3: 4.11 ms per loop

したがって、structは高速です。

2
Sklavit

それはうまくいくはずです:

return struct.pack('f' * len(floatList), *floatList)
2
katzenversteher

文字列と同様に、.join()を使用すると、継続的に連結するよりも高速になります。例えば:

import struct
b = bytes()
floatList = [5.4, 3.5, 7.3, 6.8, 4.6]
b = b.join((struct.pack('f', val) for val in floatList))

結果:

b'\xcd\xcc\xac@\x00\x00`@\x9a\x99\xe9@\x9a\x99\xd9@33\x93@'
1
Gareth Latty

本当に単精度の 'f'フロートが必要だと言うので、 array module (1.x以降の標準ライブラリで)を試してみてください。

_>>> mylist = []
>>> import array
>>> myarray = array.array('f')
>>> for guff in [123.45, -987.654, 1.23e-20]:
...    mylist.append(guff)
...    myarray.append(guff)
...
>>> mylist
[123.45, -987.654, 1.23e-20]
>>> myarray
array('f', [123.44999694824219, -987.6539916992188, 1.2299999609665927e-20])
>>> import struct
>>> mylistb = struct.pack(str(len(mylist)) + 'f', *mylist)
>>> myarrayb = myarray.tobytes()
>>> myarrayb == mylistb
True
>>> myarrayb
b'f\xe6\xf6B\xdb\xe9v\xc4&Wh\x1e'
_

これにより、ほとんどのリストメソッドを備えた可変長コンテナを使用しながら、メモリの大量の負荷を節約できます。 array.arrayアプローチは、単精度浮動小数点数ごとに4バイトを使用します。リストアプローチは、Python floatオブジェクト(4または8バイト)プラスそのオブジェクトのサイズへのポインターを消費します。32ビットCPython実装では16です。

_>>> import sys
>>> sys.getsizeof(123.456)
16
_

合計:listの場合はアイテムごとに20バイト、array.array('f')の場合は常にアイテムごとに4バイト。

0
John Machin