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

时间:2017-05-24 22:05:29

标签: python matlab performance python-2.7 numpy

我有一个处理脚本,用于提取类型为" 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个块,并且该数量随文件大小线性增加。我看到的速度差异大致是:

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 个答案:

答案 0 :(得分: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要快得多。

没有struct.unpack的{​​{1}}的时间是266μs;仅适用于np.array,23。因此f.read加上更通用且更强大的unpack需要花费更长的时间。文件读取本身不是问题。 (np.array可以处理多种输入,列表列表,对象列表等,因此必须花费更多时间来解析和评估输入。)

np.array上稍微更快的变体是您的阅读加fromfile

frombuffer