创建块时如何将2D numpy数组的块附加到二进制文件?

时间:2019-07-22 22:34:45

标签: python python-2.7 numpy

我有一个很大的输入文件,其中包含数据帧(一个数据系列(complex64),每个帧中都有一个标识头。它大于我的可用内存。标头重复,但顺序随机,例如,输入文件可能如下所示:

<FRAME header={0}, data={**first** 500 numbers...}>,
<FRAME header={18}, data={first 500 numbers...}>,
<FRAME header={4}, data={first 500 numbers...}>,
<FRAME header={0}, data={**next** 500 numbers...}>
...

我想将数据排序到一个新文件中,该文件是一个形状为(len(headers), len(data_series))的numpy数组。它必须在读取帧时构建输出文件,因为我无法将其全部容纳在内存中。

我看过numpy.savetxt和python csv包,但是出于磁盘大小,精度和速度的原因,我希望输出文件是二进制的。 numpy.save很好,除了我无法弄清楚如何使其附加到未知的数组大小上。

我必须在Python2.7中工作,因为读取这些框架需要一些依赖。到目前为止,我所做的就是使函数能够将具有匹配标头的所有帧写入单个二进制文件:

input_data = Funky_Data_Reader_that_doesnt_matter(input_filename)

 with open("singleFrameHeader", 'ab') as f:
     current_data = input_data.readFrame() # This loads the next frame in the file
     if current_data.header == 0:
          float_arr = np.array(current_data.data).view(float)
          float_arr.tofile(f)

这很好用,但是我需要将其扩展为二维。我开始将h5py作为一种选择,但希望有一个更简单的解决方案。

很棒的是类似的东西

input_data = Funky_Data_Reader_that_doesnt_matter(input_filename)

 with open("bigMatrix", 'ab') as f:
     current_data = input_data.readFrame() # This loads the next frame in the file
     index = current_data.header
     float_arr = np.array(current_data.data).view(float)
     float_arr.tofile(f, index)

感谢您的帮助。我认为这将是在附加模式下读写2D二进制文件的更常见用例。

1 个答案:

答案 0 :(得分:0)

您有两个问题:一个是文件包含顺序数据,另一个是numpy二进制文件不存储形状信息。

开始解决此问题的简单方法是坚持最初的想法,即按标头将数据转换为文件,然后将所有二进制文件合并为一个大型产品(如果您仍然需要这样做)。

您可以维护到目前为止找到的标头与其输出文件,数据大小等的映射。这将使您能够更智能地合并数据,例如,如果缺少块或标头之类的东西

from contextlib import ExitStack
from os import remove
from tempfile import NamedTemporaryFile
from shutil import copyfileobj
import sys

class Header:
    __slots__ = ('id', 'count', 'file', 'name')
    def __init__(self, id):
        self.id = id
        self.count = 0
        self.file = NamedTemporaryFile(delete=False)
        self.name = self.file.name
    def write_frame(self, frame):
        data = np.array(current_data.data).view(float)
        self.count += data.size
        data.tofile(self.file)

input_data = Funky_Data_Reader_that_doesnt_matter(input_filename)
file_map = {}

with ExitStack() as stack:
    while True:
        frame = input_data.next_frame()
        if frame is None: break  # recast this loop as necessary
        if frame.header not in file_map:
            header = Header(frame.header)
            stack.enter_context(header.file)
            file_map[frame.header] = header
        else:
            header = file_map[frame.header]
        header.write_frame(frame)

max_header = max(file_map)
max_count = max(h.count for h in file_map)

with open('singleFrameHeader', 'b') as output:
    output.write(max_header.to_bytes(8, sys.byteorder))
    output.write(max_count.to_bytes(8, sys.byteorder))
    for i in range max_header:
        if i in file_map:
            h = file_map[i]
            with open(h.name, 'rb') as input:
                copyfileobj(input, output)
            remove(h.name)
            if h.count < max_count:
                np.full(max_count - h.count, np.nan, dtype=np.float).tofile(output)
        else:
            np.full(max_count, np.nan, dtype=np.float).tofile(output)

前16个字节分别是头的int64个数和每个头的元素数。请记住,该文件是本机字节顺序,可能是任意字节,因此不可移植。