当我将数据分割为具有 30 个不同键的 30 个较小的数据帧时,为什么 hdf5 文件大小会急剧增加

afs*_*hin 1 python hdf5 pytables h5py pandas

我有一系列包含大熊猫数据帧的 hdf5 文件。一个典型的文件大约有 1000,000 行。我使用 complib='blosc',complevel=9 进行压缩。原始 hdf5 文件保存为 1 个平面文件。

然后,我尝试将数据帧逻辑分割为 30 个较小的数据帧,并将它们保存在具有 30 个不同键和相同压缩的同一个 hdf5 文件中。

令人震惊的问题是,具有 30 个较小数据帧的文件比平面文件大 40 倍。

平面 hdf5 文件保存如下:

dfx.to_hdf(file_name, key='opp',mode='a',complib='blosc',complevel=9, append=True)

分段的hdf5文件保存如下:

for i in range(30): dfx_small[i].to_hdf(file_name,key='d'+str(i), mode='a',complib='blosc',complevel=9

我做错了什么或者这个尺寸增加是预期的吗?

额外观察

我比较了生成为 1) 平面数据帧与 2)30 块数据帧的所有 hdf5 文件 - 当数据帧保存为 30 个较小的数据帧时,最大的文件大小似乎增加了 8 倍到 10 倍,同时较小的文件大小增加了 100 倍到 1000 倍。然后我尝试在压缩和不压缩的情况下保存 30 个块的 hdf5 文件。看来,当多个具有唯一键的数据帧放置在同一个 hdf5 文件中时,压缩几乎不起作用。我尝试了所有压缩选项,结果相似。

保存具有多个数据集的 hdf5 文件时存在压缩错误。

我添加了问题#45286

kcw*_*w78 5

我创建了一些简单的测试,并发现了一些有趣的行为。

  1. 首先,我创建了一些数据来模仿您的描述,发现文件大小增加了 11 倍,从 1 个 DF 增加到 30 个 DF。所以,显然发生了一些事情......(您必须提供复制 40 倍增长的代码。)
  2. 接下来,使用上面相同的数据帧,我创建了 2 个未压缩的文件——我没有包含压缩参数:complib='blosc',complevel=9. 正如预期的那样,未压缩的文件更大,但从 1 个 DF 到 30 个 DF 的增加要低得多(仅增加 65%)。

熊猫结果

DF 数量 压缩 大小(MB)
1 布洛斯-9 3.1
30 布洛斯-9 33.5
1 24.8
30 54.8

因此,某些行为可能是由于多个较小数据帧的压缩差异造成的。(压缩可能不那么有效。)此外,Pandas 使用“有趣的”模式来存储数据帧。如果您使用 HDF View 检查文件,数据看起来不像表格(就像您使用 PyTables 或 h5py 保存数据时看到的那样)。当您有多个数据帧时,可能会出现重复的开销(但这只是一个推论)。

2021-01-08 更新:
您还可以使用PyTables或保存为 HDF5 格式h5py。(Pandas 构建在 PyTables 之上。)出于好奇,我扩展了上面的示例,用每个包编写 HDF5 文件。一个文件有一个数据集,另一个文件有 30 个数据集(模仿数据帧方法)。我还比较了压缩的效果。结果提供了有趣的见解。

PyTables 结果

DS 数量 压缩 大小(MB)
1 布洛斯-9 0.14
30 布洛斯-9 0.40
1 Zlib-9 0.08
30 Zlib-9 0.17
1 11.9
30 13.5

Pandas 与 PyTables 观察

  1. 在所有情况下,使用 PyTables 写入的文件都比 Pandas 写入的相应文件小得多。
  2. 对于使用 PyTables 编写的文件,数据集的数量对文件大小的影响较小。
  3. 使用 PyTables 从 1->30 个数据集进行压缩的效果不如 Pandas(使用任一压缩库)显着。
  4. PyTables 的压缩比 Pandas 更有效(减少百分比)。
  5. 结论:PyTables 压缩和 HDF5 技术运行良好,并不是问题的根源。

h5py 结果

DS 数量 压缩 大小(MB)
1 Gzip-9 0.15
30 Gzip-9 0.40
1 11.9
30 11.9

Pandas 与 h5py 观察

  1. 在四分之三的情况下,用 h5py 编写的文件比 Pandas 的相应文件小得多。唯一较小的 Pandas 文件是具有 1 DF 的压缩文件。
  2. 对于使用h5py编写的文件,数据集的数量不会影响文件大小。
  3. 使用 h5py 从 1->30 个数据集进行压缩的效果是微不足道的(使用 gzip 库)。
  4. 使用 h5py 进行压缩更加有效(按百分比减少)。

PyTables 与 h5py 观察结果

  1. 在大多数情况下,用 PyTables 编写的文件与 h5py 中的相应文件大小非常相似。唯一较小的 h5py 文件是包含 30 个数据集的未压缩文件。
  2. 对于用h5py编写的未压缩文件,数据集的数量不会影响文件大小。
  3. 对于使用 Pytables 编写的未压缩文件,增加数据集数量会增加文件大小(在此测试数据中增加 14%)。

创建测试数据(作为列表):

import string

col1 = list(string.ascii_letters)
col2 = [ x for x in range(1,53)]
col3 = [ float(x) for x in range(1,53)]

ncopies = 18_000
nslices = 30
fact = len(col1)*ncopies//nslices
col_c = []; col_int = []; col_float = []
for i in range(ncopies):
    col_c.extend(col1)
    col_int.extend(col2)
    col_float.extend(col3)
    
Run Code Online (Sandbox Code Playgroud)

用 Pandas 写入测试数据:

import pandas as pd

dfx = pd.DataFrame({'col_c': col_c, 'col_int': col_int, 'col_float': col_float})
dfx.to_hdf('pd_file_1_blosc.h5',key='test_data',mode='a',complib='blosc',complevel=9)
dfx.to_hdf('pd_file_1_uc.h5',key='test_data',mode='a')        

for i in range(nslices):
    dfx_small = dfx[i*fact:(i+1)*fact]
    dfx_small.to_hdf('pd_file_30_blosc.h5',key=f'd{i:02}', mode='a',complib='blosc',complevel=9)
    dfx_small.to_hdf('pd_file_30_uc.h5',key=f'd{i:02}', mode='a')
Run Code Online (Sandbox Code Playgroud)

为 PyTables 和 h5py 创建 NumPy rearray:

import numpy as np

arr_dt = np.dtype([ ('col_c', 'S1'), ('col_int', int), ('col_float', float) ]) 
recarr = np.empty(shape=(len(col_c),), dtype=arr_dt)
recarr['col_c'] = col_c
recarr['col_int'] = col_int
recarr['col_float'] = col_float   
Run Code Online (Sandbox Code Playgroud)

使用 PyTables 写入测试数据:

import tables as tb

f_blosc = tb.Filters(complib ="blosc", complevel=9)
f_zlib  = tb.Filters(complib ="zlib", complevel=9)

with tb.File('tb_file_1_blosc.h5','w') as h5f:
    h5f.create_table('/','test_data', obj=recarr, filters=f_blosc)
   
with tb.File('tb_file_1_zlib.h5','w') as h5f:
    h5f.create_table('/','test_data', obj=recarr, filters=f_zlib)
    
with tb.File('tb_file_1_uc.h5','w') as h5f:
    h5f.create_table('/','test_data', obj=recarr)
    
with tb.File('tb_file_30_blosc.h5','w') as h5f:
    for i in range(nslices):
        h5f.create_table('/',f'test_data_{i:02}', obj=recarr[i*fact:(i+1)*fact],
                           filters=f_blosc)

with tb.File('tb_file_30_zlib.h5','w') as h5f:
    for i in range(nslices):
        h5f.create_table('/',f'test_data_{i:02}', obj=recarr[i*fact:(i+1)*fact],
                           filters=f_zlib)
        
with tb.File('tb_file_30_uc.h5','w') as h5f:
    for i in range(nslices):
        h5f.create_table('/',f'test_data_{i:02}', obj=recarr[i*fact:(i+1)*fact])
Run Code Online (Sandbox Code Playgroud)

用h5py写入测试数据:

import h5py

with h5py.File('h5py_file_1_gzip.h5','w') as h5f:
    h5f.create_dataset('test_data', data=recarr, compression="gzip", compression_opts=9)
   
with h5py.File('h5py_file_1_uc.h5','w') as h5f:
    h5f.create_dataset('test_data', data=recarr)
    
with h5py.File('h5py_file_30_gzip.h5','w') as h5f:
    for i in range(nslices):
        h5f.create_dataset(f'test_data_{i:02}', data=recarr[i*fact:(i+1)*fact],
                           compression="gzip", compression_opts=9)
        
with h5py.File('h5py_file_30_uc.h5','w') as h5f:
    for i in range(nslices):
        h5f.create_dataset(f'test_data_{i:02}', data=recarr[i*fact:(i+1)*fact])
Run Code Online (Sandbox Code Playgroud)