Space 列表列表列表的高效数据存储。元素是整数,所有列表的大小都在长度上变化
Space efficient data store for list of list of lists. Elements are integers, and size of all lists varies in length
假设我的数据是这样的
thisList = [
[[13, 43, 21, 4], [33, 2, 111, 33332, 23, 43, 2, 2], [232, 2], [23, 11]] ,
[[21, 2233, 2], [2, 3, 2,1, 32, 22], [3]],
[[3]],
[[23, 12], [55, 3]],
....
]
什么是最 space 有效的存储这次数据的方法?
我查看了 Numpy 文件,但是 numpy 只支持统一长度数据
我查看了 Hdf5,它支持 1d 不规则张量,但不支持 2d
因此,可以选择为 thisList
中的每个列表创建一个单独的 hdf5 文件,但我可能会有 10-20 百万个这些列表。
我 运行 使用 JSON、BSON、Numpy 和 HDF5 对保存参差不齐的嵌套列表进行基准测试。
TLDR:使用压缩 JSON,因为它是最 space 高效且最容易 encode/decode。
关于合成数据,结果如下(du -sh test*
):
4.5M test.json.gz
7.5M test.bson.gz
8.5M test.npz
261M test_notcompressed.h5
1.3G test_compressed.h5
Compressed JSON 在存储方面是最高效的,也是最容易编码和解码的,因为不规则列表不必转换为映射。 BSON 排在第二位,但它必须转换为映射,这使编码和解码变得复杂(并且抵消了 BSON 相对于 JSON 的 encoding/decoding 速度优势)。 Numpy 的压缩 NPZ 格式是第三好的,但是和 BSON 一样,在保存之前必须将参差不齐的列表制成字典。 HDF5 出奇地大,尤其是经过压缩的。这可能是因为有许多不同的数据集,压缩会增加每个数据集的开销。
基准
这是基准测试的相关代码。 bson
包是 pymongo
的一部分。我 运行 在具有 ext4
文件系统的 Debian Buster 机器上进行这些基准测试。
def get_ragged_list(length=100000):
"""Return ragged nested list."""
import random
random.seed(42)
l = []
for _ in range(length):
n_sublists = random.randint(1, 9)
sublist = []
for i in range(n_sublists):
subsublist = [random.randint(0, 1000) for _ in range(random.randint(1, 9))]
sublist.append(subsublist)
l.append(sublist)
return l
def save_json_gz(obj, filepath):
import gzip
import json
json_str = json.dumps(obj)
json_bytes = json_str.encode()
with gzip.GzipFile(filepath, mode="w") as f:
f.write(json_bytes)
def save_bson(obj, filepath):
import gzip
import bson
d = {}
for ii, n in enumerate(obj):
for jj, nn in enumerate(n):
key = f"{ii}/{jj}"
d[key] = nn
b = bson.BSON.encode(d)
with gzip.GzipFile(filepath, mode="w") as f:
f.write(b)
def save_numpy(obj, filepath):
import numpy as np
d = {}
for ii, n in enumerate(obj):
for jj, nn in enumerate(n):
key = f"{ii}/{jj}"
d[key] = nn
np.savez_compressed(filepath, d)
def save_hdf5(obj, filepath, compression="lzf"):
import h5py
with h5py.File(filepath, mode="w") as f:
for ii, n in enumerate(obj):
for jj, nn in enumerate(n):
name = f"{ii}/{jj}"
f.create_dataset(name, data=nn, compression=compression)
ragged = get_ragged_list()
save_json_gz(ragged, "ragged.json.gz")
save_bson(ragged, "test.bson.gz")
save_numpy(ragged, "ragged.npz")
save_hdf5(ragged, "test_notcompressed.h5", compression=None)
save_hdf5(ragged, "test_compressed.h5", compression="lzf")
相关包的版本:
python 3.8.2 | packaged by conda-forge | (default, Mar 23 2020, 18:16:37) [GCC 7.3.0]
pymongo bson 3.10.1
numpy 1.18.2
h5py 2.10.0
假设我的数据是这样的
thisList = [
[[13, 43, 21, 4], [33, 2, 111, 33332, 23, 43, 2, 2], [232, 2], [23, 11]] ,
[[21, 2233, 2], [2, 3, 2,1, 32, 22], [3]],
[[3]],
[[23, 12], [55, 3]],
....
]
什么是最 space 有效的存储这次数据的方法?
我查看了 Numpy 文件,但是 numpy 只支持统一长度数据
我查看了 Hdf5,它支持 1d 不规则张量,但不支持 2d
因此,可以选择为 thisList
中的每个列表创建一个单独的 hdf5 文件,但我可能会有 10-20 百万个这些列表。
我 运行 使用 JSON、BSON、Numpy 和 HDF5 对保存参差不齐的嵌套列表进行基准测试。
TLDR:使用压缩 JSON,因为它是最 space 高效且最容易 encode/decode。
关于合成数据,结果如下(du -sh test*
):
4.5M test.json.gz
7.5M test.bson.gz
8.5M test.npz
261M test_notcompressed.h5
1.3G test_compressed.h5
Compressed JSON 在存储方面是最高效的,也是最容易编码和解码的,因为不规则列表不必转换为映射。 BSON 排在第二位,但它必须转换为映射,这使编码和解码变得复杂(并且抵消了 BSON 相对于 JSON 的 encoding/decoding 速度优势)。 Numpy 的压缩 NPZ 格式是第三好的,但是和 BSON 一样,在保存之前必须将参差不齐的列表制成字典。 HDF5 出奇地大,尤其是经过压缩的。这可能是因为有许多不同的数据集,压缩会增加每个数据集的开销。
基准
这是基准测试的相关代码。 bson
包是 pymongo
的一部分。我 运行 在具有 ext4
文件系统的 Debian Buster 机器上进行这些基准测试。
def get_ragged_list(length=100000):
"""Return ragged nested list."""
import random
random.seed(42)
l = []
for _ in range(length):
n_sublists = random.randint(1, 9)
sublist = []
for i in range(n_sublists):
subsublist = [random.randint(0, 1000) for _ in range(random.randint(1, 9))]
sublist.append(subsublist)
l.append(sublist)
return l
def save_json_gz(obj, filepath):
import gzip
import json
json_str = json.dumps(obj)
json_bytes = json_str.encode()
with gzip.GzipFile(filepath, mode="w") as f:
f.write(json_bytes)
def save_bson(obj, filepath):
import gzip
import bson
d = {}
for ii, n in enumerate(obj):
for jj, nn in enumerate(n):
key = f"{ii}/{jj}"
d[key] = nn
b = bson.BSON.encode(d)
with gzip.GzipFile(filepath, mode="w") as f:
f.write(b)
def save_numpy(obj, filepath):
import numpy as np
d = {}
for ii, n in enumerate(obj):
for jj, nn in enumerate(n):
key = f"{ii}/{jj}"
d[key] = nn
np.savez_compressed(filepath, d)
def save_hdf5(obj, filepath, compression="lzf"):
import h5py
with h5py.File(filepath, mode="w") as f:
for ii, n in enumerate(obj):
for jj, nn in enumerate(n):
name = f"{ii}/{jj}"
f.create_dataset(name, data=nn, compression=compression)
ragged = get_ragged_list()
save_json_gz(ragged, "ragged.json.gz")
save_bson(ragged, "test.bson.gz")
save_numpy(ragged, "ragged.npz")
save_hdf5(ragged, "test_notcompressed.h5", compression=None)
save_hdf5(ragged, "test_compressed.h5", compression="lzf")
相关包的版本:
python 3.8.2 | packaged by conda-forge | (default, Mar 23 2020, 18:16:37) [GCC 7.3.0]
pymongo bson 3.10.1
numpy 1.18.2
h5py 2.10.0