Python:压缩大数据并将其保存/加载到内存中

2024-03-29 09:40:34 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个巨大的字典,其中numpy数组作为值,几乎消耗了所有的RAM。不可能完全腌制或压缩。我已经检查了solutionssome以使用zlib进行块读/写,但是当我想从RAM读/写时,它们处理的是文件,StringIO等等。在

Here is the closest example to what I want,但它只有写入部分。我怎样才能在保存后读取对象,因为块是一起写的,而压缩的块当然有不同的长度?在

import zlib


class ZlibWrapper():
    # chunksize is used to save memory, otherwise huge object will be copied
    def __init__(self, filename, chunksize=268435456): # 256 MB
        self.filename = filename
        self.chunksize = chunksize


    def save(self, data): 
        """Saves a compressed object to disk
        """
        mdata = memoryview(data)
        with open(self.filename, 'wb') as f:
          for i in range(0, len(mdata), self.chunksize):
             mychunk = zlib.compress(bytes(mdata[i:i+self.chunksize]))
             f.write(mychunk)

    def load(self):

        # ???

        return data

不幸的是,未压缩的对象太大,无法通过网络发送,而将它们压缩到外部会造成额外的复杂性。在

Pickle不幸地开始消耗RAM和系统挂起。在

在与Charles Duffy讨论之后,我尝试序列化(目前不起作用-甚至不压缩字符串):

^{pr2}$

Tags: to对象selfdataobjectissavedef
2条回答

您的第一个重点应该是有一种合理的方法来序列化和反序列化您的数据。我们对问题本身或评论中提供的数据有几个限制:

  • 您的数据由一个字典组成,其中包含大量的键/值对
  • 所有键都是unicode字符串
  • 所有的值都是numpy数组,它们各自都很短,可以在任何给定的时间方便地放入内存中(甚至可以允许任何单个值的多个副本),尽管总的来说所需的存储空间变得非常大。在

这意味着一个相当简单的实现:

def serialize(f, content):
    for k,v in content.items():
        # write length of key, followed by key as string
        k_bstr = k.encode('utf-8')
        f.write(struct.pack('L', len(k_bstr)))
        f.write(k_bstr)
        # write length of value, followed by value in numpy.save format
        memfile = io.BytesIO()
        numpy.save(memfile, v)
        f.write(struct.pack('L', memfile.tell()))
        f.write(memfile.getvalue())

def deserialize(f):
    retval = {}
    while True:
        content = f.read(struct.calcsize('L'))
        if not content: break
        k_len = struct.unpack('L', content)[0]
        k_bstr = f.read(k_len)
        k = k_bstr.decode('utf-8')
        v_len = struct.unpack('L', f.read(struct.calcsize('L')))[0]
        v_bytes = io.BytesIO(f.read(v_len))
        v = numpy.load(v_bytes)
        retval[k] = v
    return retval

作为一个简单的测试:

^{pr2}$

…那么,我们现在就知道了,如何添加压缩?很容易。在

with gzip.open('filename.gz', 'wb') as gzip_file:
    serialize(gzip_file, your_data)

…或者,在减压方面:

with gzip.open('filename.gz', 'rb') as gzip_file:
    your_data = deserialize(gzip_file)

这是因为gzip库已经按请求流式输出数据,而不是一次压缩或解压所有数据。不需要自己进行窗口和分块,只需将其放在较低的层即可。在

要将字典写入磁盘,zipfile模块非常适合。在

  • 保存时-将每个块保存为zip中的一个文件。在
  • 加载时-迭代zip中的文件并重建数据。在

相关问题 更多 >