在python中将浮点数列表打包成字节的最快方法

时间:2012-03-30 10:03:53

标签: python struct python-3.x

我有一个说100k浮点数的列表,我想将其转换为字节缓冲区。

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

这很慢。如何只使用标准的Python 3.x库来加快速度。

9 个答案:

答案 0 :(得分:44)

告诉struct你有多少float。在我的慢速笔记本电脑上,100k浮动大约需要1/100秒。

import random
import struct

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

答案 1 :(得分:7)

您可以使用ctypes,并且具有与C中完全相同的双数组(或浮点数组),而不是将数据保存在列表中。这是一个相当低的水平,但如果你需要很好的表现并且你的名单是一个固定的大小,这是一个建议。

您可以创建C的等效项 double array[100]; 在Python中做:

array = (ctypes.c_double * 100)()

ctypes.c_double * 100表达式为双精度数组生成一个Python类,长100项。要将其连接到文件,您只需使用buffer获取其内容:

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

如果您的数据已经在Python列表中,将其打包成双数组可能比调用struct更快或更快,就像在Agf接受的答案中一样 - 我会留下测量哪个更快作为家庭作业,但所有您需要的代码是:

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

要将其视为一个字符串,只需执行:str(buffer(array)) - 这里的一个缺点是你必须处理float size(float vs double)和CPU依赖的float类型 - struct module可以小心这对你来说。

最大的好处是,使用float数组,你仍然可以使用元素作为数字,然后访问就像它在普通的Python列表中一样,同时随后可以作为具有buffer的平面内存区域使用

答案 2 :(得分:2)

这应该有效:

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

答案 3 :(得分:2)

对于单精度浮点数组,有两个选项:使用structarray

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更快。

答案 4 :(得分:1)

与字符串一样,使用.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@'

答案 5 :(得分:0)

正如你所说,你真的想要单精度'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'

这可以为你节省大量的内存,同时还有一个带有大多数list方法的可变长度容器。 array.array方法每个单精度浮点数需要4个字节。 list方法使用指向Python float对象的指针(4或8个字节)加上该对象的大小;在32位CPython实现上,即16:

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

总计:list每个项目最多20个字节,array.array('f')总是每个项目4个字节。

答案 6 :(得分:0)

一些答案​​提示

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

,但是使用'*'不必要地将floatlist转换为元组,然后再将其传递给struct.pack。可以通过以下方法避免这种情况:首先创建一个空缓冲区,然后使用我发现的最快方法填充它:切片分配:

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

某些人可能可以使用的其他性能节省:

  • 您可以通过再次执行分配来重用现有缓冲区,而不必创建新缓冲区。
  • 您可以通过分配给适当的切片来修改现有缓冲区的一部分。

答案 7 :(得分:-1)

大多数缓慢的情况是你反复追加到bytestring。每次都复制bytestring。相反,您应该使用b''.join()

import struct
packed = [struct.pack('f', val) for val in floatList]
return b''.join(packed)

答案 8 :(得分:-1)

我认为最好的方法是创建一个循环:

例如

import struct 
file_i="test.txt"
fd_out= open ("test_bin_file",'wb')
b = bytes()
f_i = open(file_i, 'r')
for riga in file(file_i):
     line = riga
     print i,float(line)
     i+=1
     b=struct.pack('f',float(line))
     fd_out.write(b)
     fd_out.flush()


fd_out.close()

要附加到现有文件,请使用:

fd_out= open ("test_bin_file",'ab')