压缩序列化 Python 数据最节省空间的方法是什么?

问题描述 投票:0回答:4

来自 Python 文档

默认情况下,pickle 数据格式使用相对紧凑的二进制表示。如果您需要最佳尺寸特性,您可以有效地压缩 pickled 数据。

我将在运行数小时的进程结束时序列化数 GB 的数据,我希望结果在磁盘上尽可能小。然而,Python 提供了几种不同的方式来压缩数据

是否有其中一种对腌制文件特别有效?我正在 pickle 的数据主要由嵌套的字典和字符串组成,所以如果有更有效的压缩方法,例如JSON,那也行。

压缩和解压的时间并不重要,但是这个过程生成数据所花费的时间使得试错不方便。

python serialization compression pickle
4个回答
36
投票

我使用 Pickled 对象做了一些测试,

lzma
给出了最好的压缩。

但是您的结果可能会根据您的数据而有所不同,我建议您使用自己的一些样本数据对其进行测试。

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----        9/17/2019  10:05 PM       23869925 no_compression.pickle
-a----        9/17/2019  10:06 PM        6050027 gzip_test.gz
-a----        9/17/2019  10:06 PM        3083128 bz2_test.pbz2
-a----        9/17/2019  10:07 PM        1295013 brotli_test.bt
-a----        9/17/2019  10:06 PM        1077136 lzma_test.xz

使用的测试文件(您需要

pip install brotli
或删除该算法):

import bz2
import gzip
import lzma
import pickle

import brotli


class SomeObject():

    a = 'some data'
    b = 123
    c = 'more data'

    def __init__(self, i):
        self.i = i


data = [SomeObject(i) for i in range(1, 1000000)]

with open('no_compression.pickle', 'wb') as f:
    pickle.dump(data, f)

with gzip.open("gzip_test.gz", "wb") as f:
    pickle.dump(data, f)

with bz2.BZ2File('bz2_test.pbz2', 'wb') as f:
    pickle.dump(data, f)

with lzma.open("lzma_test.xz", "wb") as f:
    pickle.dump(data, f)

with open('no_compression.pickle', 'rb') as f:
    pdata = f.read()
    with open('brotli_test.bt', 'wb') as b:
        b.write(brotli.compress(pdata))

10
投票

只需添加一个可以轻松为我提供最高压缩比的替代方案,而且速度如此之快,我确信我在某个地方犯了错误(我没有)。真正的好处是解压缩也非常快,因此任何读取大量预处理数据的程序都将从中受益匪浅。一个潜在的警告是提到“小数组(<2GB)" 这里的某个地方,但看起来有解决方法。或者,如果你像我一样懒惰,通常可以选择分解数据。

一些智能饼干想出了python-blosc。根据他们的文档,这是一个“高性能压缩器”。我是从对this question.

的回答中得到的。

一旦安装通过,例如

pip install blosc
conda install python-blosc
,您可以很容易地压缩 pickled 数据,如下所示:

import blosc
import numpy as np
import pickle

data = np.random.rand(3, 3, 1e7)

pickled_data = pickle.dumps(data)  # returns data as a bytes object
compressed_pickle = blosc.compress(pickled_data)

with open("path/to/file/test.dat", "wb") as f:
    f.write(compressed_pickle)

阅读它:

with open("path/to/file/test.dat", "rb") as f:
    compressed_pickle = f.read()

depressed_pickle = blosc.decompress(compressed_pickle)
data = pickle.loads(depressed_pickle)  # turn bytes object back into data

我使用的是 Python 3.7,甚至没有查看所有不同的压缩选项,我得到的压缩比约为 12,读取 + 解压 + 加载压缩的 pickle 文件比加载未压缩的 pickle 文件花费的时间长几分之一秒。

我写这个更多是为了给自己参考,但我希望其他人会觉得这有用。

和平oot


2
投票

我把“有效压缩腌制数据”的意思是通用压缩器往往工作良好。但是 Pickle 是一种协议,而不是一种格式本身。通过在您的自定义类上实现

__reduce__
方法,可以使 pickle 发出压缩的字节串。试图进一步压缩这些效果不会很好。

在标准库压缩器中,LZMA 往往会为您提供典型数据流的最佳比率,但它也是最慢的。您可能可以使用 ZPAQ 做得更好(例如,通过

pyzpaq
),但这甚至更慢。


1
投票

mgzip 是一个更快的解决方案。 lzma 慢得令人痛苦,尽管它的压缩率比 mgzip 好 25%。

with mgzip.open(pathname, 'wb') as f:
    pickle.dump(data, f)

装载:

with mgzip.open(pathname, 'rb') as f:
    data = pickle.load(f)
© www.soinside.com 2019 - 2024. All rights reserved.