Python Tables 学习笔记

实在是太烦了,虽然以前也用过python tables来存储大数据,但是还是有些功能不太懂。我只用了最简单的create array直接把所有的数据一次性写入hdf5文件。但是现在的电脑内存比较小,处理的数据太大,一次性写入,内存会不足。另一方面,一边处理数据,一边写入数据,是一种更好的策略。于是自己又重写学了python tables,也花了不少时间。在此总结下,也为自己做好总结立个标杆。要总结好,不然很快就会忘记了。


本文将从tables的读,写,以及一些简单的操作进行简要描述。使得tables这个tool能够为我们所用。
首先,我们先介绍下HDF5(Hierarchical Data Format).HDF 是用于存储和分发科学数据的一种自我描述、多对象文件格式。HDF 是由美国国家超级计算应用中心(NCSA)创建的,以满足不同群体的科学家在不同工程项目领域之需要。HDF 可以表示出科学数据存储和分布的许多必要条件。其有以下一些特征:
- 自述性
- 通用性
- 灵活性
- 扩展性
- 跨平台


HDF文件是安装树状结构组织起来的。其顶部是根节点(),根节点下可以接很多分组(group),每个分组下有可以有很多节点,包括表(table),数组(array),压缩数组(compression array,Earray),可扩展压缩数组(enlargeable array,Earray),变长数组(variable length array,VLarray)。每个节点下还有叶子节点,即最终存储的数据。

简单知道HDF5文件的特征,我们接着介绍下,在python下如何使用tables(其实还有H5Py这个包也是可以用)这个package来实现对HDF5文件的操作。
假设大家已经把tables及其依赖的库如numpy等包安装好在python的环境下。

1 写数据到表

这里简单以pytable里面的例子进行说明。对于table,我们首先要定义表格中每个元素(field)的数据类型,长度等信息。如下图,我们定义了一个particle类,其名称,数据类型,及长度。比如说name这个field是字符串类型,最大长度是16个字节。

from tables import *
class particle(IsDescription):
    '''definition '''
    name = StringCol(16) #16-character string
    _id = Int64Col()  #signed 64-bit interger
    energy = Float64Col() #double-precision


新建一个hdf5文件,文件名是tutorial.h5, 写的模式,描述为test file。

f = open_file('tutorial.h5',mode='w',title='test file')
  • 1

为了更好地组织数据,我们在根节点下新建一个分组叫detector,描述为Detector information。

group = f.create_group('/','detector','Detector information')


“/”可以由f.root代替。
接着,我们在detector这个分组下建立一个新的表(table)叫做‘readout’,并初始化为particle形式。

table=f.create_table(group,'readout',particle,'Readout_example')


接着,我们往表里添加数据。其中append这个函数用于把数据写入到I/O buffer中。

for i in range(10):
    particle['name']='particle: %6d' %(i)
    particle['_id'] = i *(2 **34)
    particle['energy'] = float(i *i)
    particle.append()
table.flush()


最后还需要调用flush函数把数据写入到硬盘中,并且会释放被占用的内存,这样我们就可以处理海量的数据,而不用担心内存不足。

2 从表中读数据

我们把数据存到硬盘里面,接下的步骤要从硬盘把数据读出来。我们首先定位到表。接着我们还可以查询我们感兴趣的数据。我们的查询是通过 Table.iterrows() 这个迭代器,它把表中的每行提取出来。

table = f.root.detector.readout
energy = []
for x in table.iterrows():
    energy.append(x)

3 创建数组对象(Array object)

把数据放在table是数据组织很好的方式,但是当我们的数据并没有那么复杂,并没有包含很多属性,我们可以简单地使用数组来组织数据。比如我们想存储矩阵数据,

features = np.random.rand(100,100000)
f = tables.openFile('feat.h5','w')
f.createArray(f.root,'feature',features)
f.close()

4 压缩数组(Compression Array)

HDF文件还可以进行压缩存储,压缩方式有blosc, zlib, 和 lzo。Zlib和lzo压缩方式需要另外的包,blosc在tables是自带的。我们需要定义一个filter来说明压缩方式及压缩深度。另外,我们使用creatCArray来创建压缩矩阵。

f = tables.open_file('feat.h5', mode='w')
features = np.random.rand(100,100000)
filters=Filters(complevel=5,complib='blosc')
data_storage = f.createCArray(f.root,'feature',
                                    Atom.from_dtype(features.dtype),
                                    shape=features.shape,filters=filters)
data_storage[:]= features
f.close()

5 压缩可扩展数组(Compression & Enlargeable Array)

压缩数组,初始化之后就不能改变大小,但现实很多时候,我们只知道维度,并不知道我们数据的长度。这个时候,我们需要这个数组是可以扩展的。HDF文件也提供这样的接口,我们能够扩展其一个维度。同CArray一样,我们也先要定filter来声明压缩类型及深度。最重要的是,我们把可以扩展这个维度的shape设置为0。扩展数据的时候就使用append,这个数据shape必须为(1,data.shape[1])

f = tables.open_file('feat.h5', mode='w')
features = np.random.rand(100,100000)
filters=Filters(complevel=5,complib='blosc')

data_storage = f.createEArray(f.root,'feature',
                                    Atom.from_dtype(features.dtype),
                                    shape=(0,features.shape[1]),filters=filters)

for x in range(features.shape[0]):
    data_storage.append(features[:,x])
f.close()


上述我们存储的数据是浮点型,当我们要存储字符串类型的,我们同样定义:

image_name  = '123fjo.jpg'
imageName_storage = f.createEArray(f.root,'filename',tables.StringAtom(itemsize=32),shape=(0,))
image_name = np.array([image_name],dtype=np.object)

imageName_storage.append(image_name)

  • 5

如果我们不知道字符串的字节数,我们在初始化的时候就不需要填。接着,我们需要把python string类型转换为numpy object类型。这样在添加数据就没有问题了。

当然,HDF远远不止这些内容,但是目前只用到这么多。如果以后有需要,我们再来深入学习。