在Python中读取和切片二进制数据文件的最快方法

4
我是一名有用的助手,可以为您翻译文本。

我有一个处理脚本,旨在拉取类型为“uint16”的二进制数据文件,并以每次6400个块进行各种处理。该代码最初是使用Matlab编写的,但由于分析代码是用Python编写的,我们希望通过在Python中完成所有操作来简化流程。问题是我注意到我的Python代码比Matlab的fread函数慢得足够多。

简单来说,Matlab代码如下:

fid = fopen(filename); 
frame = reshape(fread(fid,80*80,'uint16'),80,80);  

我的Python代码非常简单:

with open(filename, 'rb') as f: 
    frame = np.array(unpack("H"*6400, f.read(12800))).reshape(80, 80).astype('float64')

文件大小从500 MB -> 400 GB差异很大,因此我相信在Python中找到更快的解析数据的方法可能会在较大的文件上产生回报。一个500 MB的文件通常有~50000个块(chunk),而这个数字随着文件大小呈线性增长。我看到的速度差异大约是:

Python = 4 x 10^-4 seconds / chunk

Matlab = 6.5 x 10^-5 seconds / chunk

处理时间表明,Matlab比我实现的Python方法快约5倍。我已经尝试了诸如numpy.fromfile和numpy.memmap等方法,但由于这些方法在某些时候需要将整个文件打开到内存中,因此限制了使用情况,因为我的二进制文件相当大。是否有一种Pythonic的方法可以做到这一点,而我却错过了呢?我原本认为Python在打开和读取二进制文件方面非常快。非常感谢您的任何建议。


1
你有没有可能在h5py中使用dask?去年我使用这两个软件包进行了数百万粒子的大规模模拟。 - romeric
1
此外还可以在这里查看。 - romeric
@romeric 我不确定dask是否适用,因为我正在使用“.bin”文件,将其转换为h5py之类的东西对于我的用例来说是适得其反的。很遗憾,数据文件格式目前是我无法控制的。第二篇帖子似乎只是在使用f.seek命令与np.fromfile相结合。我看到的问题是,我可以到达正确的位置,但它会将文件的其余部分读入numpy数组中,这会在我的用例中导致内存超载。 - Dustin K.
fromfile 函数有计数或大小参数吗? - hpaulj
如果您能将“读取”、“解包”和“数组”步骤的时间分开,可能会有所帮助。 - hpaulj
1个回答

2

将数据块写入文件:

In [117]: dat = np.random.randint(0,1028,80*80).astype(np.uint16)
In [118]: dat.tofile('test.dat')
In [119]: dat
Out[119]: array([266, 776, 458, ..., 519,  38, 840], dtype=uint16)

按照你的方式导入:

In [120]: import struct
In [121]: with open('test.dat','rb') as f:
     ...:     frame = np.array(struct.unpack("H"*6400,f.read(12800)))
     ...:     
In [122]: frame
Out[122]: array([266, 776, 458, ..., 519,  38, 840])

使用fromfile进行导入

In [124]: np.fromfile('test.dat',count=6400,dtype=np.uint16)
Out[124]: array([266, 776, 458, ..., 519,  38, 840], dtype=uint16)

比较时间:

In [125]: %%timeit
     ...:  with open('test.dat','rb') as f:
     ...:      ...:     frame = np.array(struct.unpack("H"*6400,f.read(12800)))
     ...: 
1000 loops, best of 3: 898 µs per loop

In [126]: timeit np.fromfile('test.dat',count=6400,dtype=np.uint16)
The slowest run took 5.41 times longe....
10000 loops, best of 3: 36.6 µs per loop

fromfile 更快。

仅使用 f.readstruct.unpack 时间为 266 微秒;仅使用 f.read 的时间为 23 微秒。所以是 unpack 加上更通用和健壮的 np.array 导致了花费更长的时间。文件读取本身不是问题。(np.array 可以处理许多种输入,如列表、对象列表等,因此必须花费更多的时间来解析和评估输入。)

一种稍微更快的 fromfile 变体是您的读取加上 frombuffer

In [133]: with open('test.dat','rb') as f:
     ...:      frame3 = np.frombuffer(f.read(12800),dtype=np.uint16)

我之前无法让f.seek和np.fromfile的组合起作用,但是你提供的替代方案很有效!使用np.frombuffer(这是我以前从未听说过的)我能够将Python降至每块24微秒左右,大约比Matlab快2倍。干杯! - Dustin K.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接