python - 为什么我的 hdf5 文件看起来太大了?

标签 python pandas dataframe hdf5

我正在处理一个巨大的数据集(数百 GB),其中约有 4000 万个标识符存储为 32 个字符的字符串,每个标识符都有数百或数千行数字数据。

为了节省空间并提高从磁盘读取数据的效率,似乎最好不要在数据集中一遍又一遍地重复标识符。比如一张数据表,看起来像

verylongstringidentifier1, 1.2
verylongstringidentifier1, 2.3
verylongstringidentifier1, 3.4
.
.
verylongstringidentifier2, 2.1
verylongstringidentifier2, 1.0
.
.

如果字符串标识符不重复,则可以更有效地存储。一种选择是为每个标识符保存单独的文件,我可能会走这条路,但拥有数百万个单独的小文件有点烦人,而且从磁盘 I/O 的角度来看可能效率低下。

我对 hdf5 完全陌生,但我读到的内容表明它应该适用于这种情况,因为数据集可以使用标识符作为键来存储。但是,当我保存到一个 hdf5 文件时,生成的文件大约比我简单地写入一个平面 csv 文件时得到的文件大 40 倍。我是否遗漏了有关 hdf5 文件存储方式的信息,或者我只是做错了什么?下面的测试代码是我用来验证(并尝试诊断)问题的代码。

# trying to figure out why hdf5 file sizes are so huge
import time
import string 
import random
import numpy as np 
import pandas as pd
from pandas import HDFStore

# generate 1000 random 32-character strings
strings = [''.join(random.choices(string.ascii_lowercase, k=32)) for _ in range(1000)] 

# for each of these random strings, create 200 rows of three random floats
# concatenate into one big dataframe
df = pd.DataFrame()
for s in strings:
  vars = np.random.rand(200,3)
  ss = np.full((200,1),s)
  s_data = np.concatenate((ss, vars), axis=1)
  df = pd.concat([df, pd.DataFrame(s_data)], axis=0)

df.columns = ['string', 'v1', 'v2', 'v3']

# write to one big csv file
df.to_csv('/tmp/test.csv', index=False)

# write to compressed bzip2 file
df.to_csv('/tmp/test.csv.bz2', index=False, compression='bz2')

# write to separate csv files for each string
unique_strings = df.string.unique()
for s in unique_strings:
  s_chunk = df[df.string == s]
  fname = '/tmp/test_' + s + '.csv.bz2'
  # don't need to store the string, since it can be retrieved as the filename
  s_chunk[['v1', 'v2', 'v3']].to_csv(fname, index=False, compression='bz2')

# write to hdf5 file with strings as keys
# what I'm trying to do here is *not* save the strings in the datasets, but instead
# use the strings as the names (keys) for the datasets
# My understanding is this would enable me to retrieve the data for a given string
# with pd.read_hdf(h5data, key=<string for which I want data>)
h5data = HDFStore('/tmp/test.h5')
for s in unique_strings:
  s_chunk = df[df.string == s]
  # don't need to store the string, because we'll use it as the key
  s_chunk[['v1', 'v2', 'v3']].to_hdf(h5data, key=s, format='table', complib='bzip2')
h5data.close()

生成的文件大小:

 18M  /tmp/test.csv
4.7M  /tmp/test.csv.bz2
 80M  /tmp/test.h5

最佳答案

发生这种情况的原因可能是 Pandas 将每个组/数据集的大量无关信息转储到 HDF5 文件中。当我运行您的代码并使用 HDFView 检查文件时, 这很明显。

我更喜欢使用 h5py用于创建和管理 HDF5 文件的库,因为它允许更加简单和控制。

我尝试使用 h5py 构建文件,其中每个组都被命名为一个唯一的字符串,并且在每个组内是 DataFrame 的每一列的数据集。我在您的脚本中使用了以下内容来写入 HDF5:

with h5py.File("/tmp/test.h5", "w") as h5data:
    for s in unique_strings:
        s_chunk = df[df.string == s]
        # create group with name = string
        g = h5data.create_group(s)
        # create datasets within group for each data column
        dset_v1 = g.create_dataset("v1", data=s_chunk["v1"].values.astype(np.float32), compression="gzip")
        dset_v2 = g.create_dataset("v2", data=s_chunk["v2"].values.astype(np.float32), compression="gzip")
        dset_v3 = g.create_dataset("v3", data=s_chunk["v3"].values.astype(np.float32), compression="gzip")

结果(注意我使用的是 gzip 而不是 bz2):

 18M    /tmp/test.csv
5.2M    /tmp/test.csv.bz2
 11M    /tmp/test.h5

进一步的优化是在每个组中只有一个数据集,该数据集是一个二维数组。在这种情况下,三个 create_dataset 调用将被一个替换:

dset = g.create_dataset("data", data=s_chunk[["v1", "v2", "v3"]].values.astype(np.float32), compression="gzip")

结果:

 18M    /tmp/test.csv
5.0M    /tmp/test.csv.bz2
6.0M    /tmp/test.h5

使用 bz2 作为压缩会进一步缩小。

关于python - 为什么我的 hdf5 文件看起来太大了?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65119241/

相关文章:

javascript - JSONify 返回奇怪的值

python - 将 Pandas 数据框的子集与 Scipy Kmeans 一起使用?

python - 将时间戳转换为 str 值 python pandas dataframe

python - 一行python中的if-else语句

python - pandas.DataFrame.groupby.nunique() 不会删除 groupby 列。这是一个错误吗?

python - 为什么 str + Markup 返回一个 Markup 对象?

python - 如何将 "bytes"对象转换为 pandas Dataframe,Python3.x 中的文字字符串?

python pandas 按一天中的小时求和

python - 根据列对某些行赋予权重

python - 循环遍历 Pandas 数据框中的行以检查两个单独列中的值