读写API

HDFStore支持使用read_hdf进行读取和使用to_hdf进行写入的top-level API,类似于read_csv和to_csv的工作方式。

默认情况下,HDFStore不会丢弃全部为na的行。可以通过设置dropna = True来更改此行为。

1
2
3
4
5
import pandas as pd
import h5py
df_tl = pd.DataFrame({'A': list(range(5)), 'B': list(range(5))})
df_tl.to_hdf('store_tl.h5', 'table', append=True)
pd.read_hdf('store_tl.h5', 'table', where=['index>2'])

HDF数据类型

  • Fixed Format
    • 一次写入,重复读取,不可追加
    • 不可以使用where查询,比如每次获取指定key的全部内容
    • 不支持dataframe有非唯一的column
    • 相比于table格式更快的读写速度
    • 使用put或者to_hdf时的默认类型,也可以通过format=’fixed’或format=’f’指定类型
  • Table Format
    • 支持append操作
    • 支持删除和查询类型操作
    • 执行put或者to_hdf操作时通过设置format=’table’或format=’t’指定table格式
    • pd.set_option(‘io.hdf.default_format’,’table’) 设置默认hdf format类型

在第一次append/put操作之后,您无法更改数据列(也不能转换索引)(当然,您只需读取数据并创建新表!)。

警告HDFStore对于写入不是线程安全的。底层PyTables仅支持并发读取(通过线程或进程)。如果您需要同时进行读写,则需要在单个进程中在单个线程中序列化这些操作。否则你将破坏你的数据。

TODO 验证append是否会按照index排序的例子

Hierarchical Keys

key可以包含路径,例如’/food/apple’, 存储时会自动创建sub-stores(在pytables中是groups),可以省略路径开头的’/‘,’/food/apple’和’food/apple’表示相同的key。

注意:删除操作会删除子路径下的所有内容,请小心使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
df = pd.DataFrame({'A': list(range(2)), 'B': list(range(2))})
store = pd.HDFStore('hk_test.h5')
store.append('/food/apple', df)
store.append('food/orange', df)
store.append('df',df)
print(store.keys())

for (path, subgroups, subkeys) in store.walk():
for subgroup in subgroups:
print('GROUP: {}/{}'.format(path, subgroup))
for subkey in subkeys:
key = '/'.join([path, subkey])
print('KEY: {}'.format(key))
print(store.get(key))

print(store['food/orange'])

查询

select和delete操作可以通过查询语句对数据子集进行操作,好处是可以在非常大的数据集上只检索一小部分数据。

查询表达式:

  • 支持使用index和columns查询dataframe
  • 支持使用major_axis、minor_axis和items查询Panel
  • 如果指定data_columns,则它将被作为附加索引器

有效的比较运算符是:

=, ==, !=, >, >=, <, <=

有效的布尔表达式包括:

  • | : 或操作
  • & : 与操作
  • ( 和 ) : 分组

例子:

  • ‘index >= date’
  • “columns = [‘A’, ‘D’]“
  • “columns in [‘A’, ‘D’]“
  • ‘columns = A’
  • ‘columns == A’
  • “~(columns = [‘A’, ‘B’])”
  • ‘index > df.index[3] & string = “bar”‘
  • ‘(index > df.index[3] & index <= df.index[6]) | string = “bar”‘
  • “ts >= Timestamp(‘2012-02-01’)”
  • “major_axis>=20130101”

不建议通过将字符串插入查询表达式来将字符串传递给查询。只需将感兴趣的字符串分配给变量,并在表达式中使用该变量。
例如:

1
2
3
4
5
string = "HolyMoly'"
store.select('df', 'index == string')
store.select('dfq', "index>pd.Timestamp('20130104') & columns=['A', 'B']")
store.select('dfq', where="A>0 or C>0")
store.select('df', "columns=['A', 'B']")

删除

1
store.remove('wp', 'major_axis > 20000102')

警告: 请注意HDF5不会自动回收h5文件中的空格。因此,反复删除(或删除节点)并再次添加,将趋于增加文件大小。

压缩

  • complevel指定压缩强度,complevel = 0和complevel = None禁用压缩,0 <complevel <10启用压缩。
  • complib 指定压缩库,默认使用zlib
    • zlib:默认的压缩库。压缩方面的经典之作可以实现良好的压缩率,但速度有些慢。
    • lzo:快速压缩和减压。
    • bzip2:良好的压缩率。
    • blosc:快速压缩和解压缩。
    • blosc:blosclz这是blosc的默认压缩器
    • blosc:lz4:紧凑,非常流行和快速的压缩机。
    • blosc:lz4hc:LZ4的调整版本,以牺牲速度为代价产生更好的压缩比。
    • blosc:snappy:在许多地方使用的流行压缩器。
    • blosc:zlib:经典;比以前慢一些,但实现了更好的压缩比。
    • blosc:zstd:非常平衡的编解码器;它提供了上述其他压缩比,并且速度相当快。
1
2
3
store_compressed = pd.HDFStore('store_compressed.h5', complevel=9,complib='blosc:blosclz')

store.append('df', df, complib='zlib', complevel=5)

ptrepack

重新生成压缩文件,重写文件将回收已删除的空间,也可以改变complevel

1
ptrepack --chunkshape=auto --propindexes --complevel=9 --complib=blosc in.h5 out.h5

性能

  • fixed stores 读写速度快于 tables 格式,但tables支持追加、删除和查询操作
  • 可以设置chunksize=\< int >来指定chunsize大小,这将会降低写入时的内存使用量
  • 设置expectedrows 可以优化读写性能
  • Duplicate rows将会被写入tables,在select时会被过滤掉

我的微信公众号:pyquant