用 Python 编写 HDF5 文件的最快方法?

Posted

技术标签:

【中文标题】用 Python 编写 HDF5 文件的最快方法?【英文标题】:Fastest way to write HDF5 files with Python? 【发布时间】:2011-07-24 22:19:20 【问题描述】:

给定一个包含混合文本/数字的大型(10 GB)CSV 文件,创建具有相同内容的 HDF5 文件同时保持合理内存使用的最快方法是什么?

如果可能,我想使用h5py 模块。

在下面的玩具示例中,我发现了一种将数据写入 HDF5 的非常慢和非常快的方法。以 10,000 行左右的数据块写入 HDF5 是否是最佳实践?或者有没有更好的方法将大量数据写入这样的文件?

import h5py

n = 10000000
f = h5py.File('foo.h5','w')
dset = f.create_dataset('int',(n,),'i')

# this is terribly slow
for i in xrange(n):
  dset[i] = i

# instantaneous
dset[...] = 42

【问题讨论】:

读入一个numpy数组并通过发送整个数组来避免循环 @Benjamin:如果数组太大而无法保存在内存中怎么办? 我认为您需要告诉我们您希望 hdf5 文件的结构方式 然后将其分块读入尽可能大的块,并使用循环(可能是 10 次迭代?)而不是逐个单元格地读取。顺便说一句,我在内存中保存超过 25,000,000 个浮点数数组没有问题。 【参考方案1】:

我会避免对数据进行分块,并将数据存储为一系列单数组数据集(按照 Benjamin 的建议)。我刚刚完成了将我一直在开发的企业应用程序的输出加载到 HDF5 中,并且能够将大约 45 亿个复合数据类型打包为 450,000 个数据集,每个数据集包含 10,000 个数据数组。写入和读取现在看起来相当即时,但当我最初尝试对数据进行分块时却非常缓慢。

只是一个想法!

更新:

这些是从我的实际代码中提取的几个 sn-ps(我使用 C 与 Python 进行编码,但您应该了解我在做什么)并为清晰起见进行了修改。我只是在数组中写入长无符号整数(每个数组 10,000 个值),并在需要实际值时读取它们

这是我典型的编写器代码。在这种情况下,我只是将长无符号整数序列写入数组序列,并在创建时将每个数组序列加载到 hdf5 中。

//Our dummy data: a rolling count of long unsigned integers
long unsigned int k = 0UL;
//We'll use this to store our dummy data, 10,000 at a time
long unsigned int kValues[NUMPERDATASET];
//Create the SS adata files.
hid_t ssdb = H5Fcreate(SSHDF, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
//NUMPERDATASET = 10,000, so we get a 1 x 10,000 array
hsize_t dsDim[1] = NUMPERDATASET;
//Create the data space.
hid_t dSpace = H5Screate_simple(1, dsDim, NULL);
//NUMDATASETS = MAXSSVALUE / NUMPERDATASET, where MAXSSVALUE = 4,500,000,000
for (unsigned long int i = 0UL; i < NUMDATASETS; i++)
    for (unsigned long int j = 0UL; j < NUMPERDATASET; j++)
        kValues[j] = k;
        k += 1UL;
    
    //Create the data set.
    dssSet = H5Dcreate2(ssdb, g_strdup_printf("%lu", i), H5T_NATIVE_ULONG, dSpace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
    //Write data to the data set.
    H5Dwrite(dssSet, H5T_NATIVE_ULONG, H5S_ALL, H5S_ALL, H5P_DEFAULT, kValues);
    //Close the data set.
    H5Dclose(dssSet);

//Release the data space
H5Sclose(dSpace);
//Close the data files.
H5Fclose(ssdb);

这是我的阅读器代码的略微修改版本。有更优雅的方法可以做到这一点(即,我可以使用超平面来获得价值),但就我相当自律的敏捷/BDD 开发过程而言,这是最简洁的解决方案。

unsigned long int getValueByIndex(unsigned long int nnValue)
    //NUMPERDATASET = 10,000
    unsigned long int ssValue[NUMPERDATASET];
    //MAXSSVALUE = 4,500,000,000; i takes the smaller value of MAXSSVALUE or nnValue
    //to avoid index out of range error 
    unsigned long int i = MIN(MAXSSVALUE-1,nnValue);
    //Open the data file in read-write mode.
    hid_t db = H5Fopen(_indexFilePath, H5F_ACC_RDONLY, H5P_DEFAULT);
    //Create the data set. In this case, each dataset consists of a array of 10,000
    //unsigned long int and is named according to its integer division value of i divided
    //by the number per data set.
    hid_t dSet = H5Dopen(db, g_strdup_printf("%lu", i / NUMPERDATASET), H5P_DEFAULT);
    //Read the data set array.
    H5Dread(dSet, H5T_NATIVE_ULONG, H5S_ALL, H5S_ALL, H5P_DEFAULT, ssValue);
    //Close the data set.
    H5Dclose(dSet);
    //Close the data file.
    H5Fclose(db);
    //Return the indexed value by using the modulus of i divided by the number per dataset
    return ssValue[i % NUMPERDATASET];

主要内容是编写代码中的内部循环以及整数除法和 mod 操作,以获取数据集数组的索引和该数组中所需值的索引。让我知道这是否足够清楚,以便您可以在 h5py 中组合类似或更好的东西。在 C 语言中,这非常简单,与分块数据集解决方案相比,它给了我更好的读/写时间。另外,由于我无论如何都不能对复合数据集使用压缩,因此分块的明显优势是一个有争议的问题,因此我所有的复合数据都以相同的方式存储。

【讨论】:

如果可能的话,您介意多介绍一下您的数据结构吗?如果您能提供一个具体的(代码)示例,我很乐意接受答案。 我已经用代码更新了我的回复。让我知道这是否有帮助! 您的代码看起来不错,但它是单线程的?你知道如何在 Spark 下编写 HDF5 吗?【参考方案2】:

使用numpy.loadtxt 的灵活性将数据从文件中获取到numpy array,这反过来非常适合初始化hdf5 数据集。

import h5py
import numpy as np

d = np.loadtxt('data.txt')
h = h5py.File('data.hdf5', 'w')
dset = h.create_dataset('data', data=d)

【讨论】:

【参考方案3】:

我不确定这是否是最有效的方法(我从未使用过它;我只是将一些我独立使用过的工具整合在一起),但您可以将 csv 文件读入一个 numpy recarray使用matplotlib helper methods for csv。

您可能也可以找到一种方法来分块读取 csv 文件,以避免将整个文件加载到磁盘。然后使用recarray(或其中的切片)将整个(或其中的大块)写入h5py数据集。我不完全确定 h5py 如何处理recarrays,但文档表明它应该没问题。

基本上,如果可能,尝试一次写入大块数据,而不是遍历单个元素。

读取 csv 文件的另一种可能性是 numpy.genfromtxt

您可以使用关键字usecols 获取您想要的列,然后通过正确设置skip_headerskip_footer 关键字仅读取指定的一组行。

【讨论】:

以上是关于用 Python 编写 HDF5 文件的最快方法?的主要内容,如果未能解决你的问题,请参考以下文章

将包含 Numpy 数组的整个 HDF5 读入内存的最快方法

将 HDF5 文件转换为其他格式

如何在 Python 中查找 HDF5 文件组/键?

为啥用 Pandas 编写时 CSV 文件比 HDF5 文件小?

用 h5py 创建的 HDF5 文件不能被 h5py 打开

用于 Python 的 HDF5:高级与低级接口。 h5py