Pandas HDFStore:查询不匹配字符串时速度较慢

Pandas HDFStore:查询不匹配字符串时速度较慢,pandas,hdf5,pytables,hdfstore,Pandas,Hdf5,Pytables,Hdfstore,我的问题是,当我试图查找数据帧(存储在hdf5文件中)中不包含的字符串时,完成查询需要很长时间。例如: 我有一个包含2*10^9行的df。它存储在HDF5文件中。我有一个名为“code”的字符串列,它被标记为“data_column”(因此它被索引) 当我搜索数据集中存在的代码(store.select('df','code=valid_code'))时,大约需要10秒钟才能得到70K行 但是,当我搜索数据集中不存在的代码(store.select('df','code=NOT_valid_co

我的问题是,当我试图查找数据帧(存储在hdf5文件中)中不包含的字符串时,完成查询需要很长时间。例如:

我有一个包含2*10^9行的df。它存储在HDF5文件中。我有一个名为“code”的字符串列,它被标记为“data_column”(因此它被索引)

当我搜索数据集中存在的代码(store.select('df','code=valid_code'))时,大约需要10秒钟才能得到70K行

但是,当我搜索数据集中不存在的代码(store.select('df','code=NOT_valid_code'))时,大约需要980秒才能获得查询结果(0行)

我创建的商店如下所示: store=pd.HDFStore('data.h5',complevel=1,complib='zlib') 第一个附加类似于: store.append('df',chunk,data_columns=['code'],expectedrows=2318185498)

这种行为是正常的还是出了什么问题

谢谢

附:这个问题可能与

更新:

按照杰夫的建议,我复制了他的实验,在Mac电脑上得到了以下结果。这是生成的表:

!ptdump -av test.h5
/ (RootGroup) ''
  /._v_attrs (AttributeSet), 4 attributes:
   [CLASS := 'GROUP',
    PYTABLES_FORMAT_VERSION := '2.1',
    TITLE := '',
    VERSION := '1.0']
/df (Group) ''
  /df._v_attrs (AttributeSet), 14 attributes:
   [CLASS := 'GROUP',
    TITLE := '',
    VERSION := '1.0',
    data_columns := ['A'],
    encoding := None,
    index_cols := [(0, 'index')],
    info := {1: {'type': 'Index', 'names': [None]}, 'index': {}},
    levels := 1,
    nan_rep := 'nan',
    non_index_axes := [(1, ['A'])],
    pandas_type := 'frame_table',
    pandas_version := '0.10.1',
    table_type := 'appendable_frame',
    values_cols := ['A']]
/df/table (Table(50000000,)) ''
  description := {
  "index": Int64Col(shape=(), dflt=0, pos=0),
  "A": StringCol(itemsize=8, shape=(), dflt='', pos=1)}
  byteorder := 'little'
  chunkshape := (8192,)
  autoindex := True
  colindexes := {
    "A": Index(6, medium, shuffle, zlib(1)).is_csi=False,
    "index": Index(6, medium, shuffle, zlib(1)).is_csi=False}
  /df/table._v_attrs (AttributeSet), 11 attributes:
   [A_dtype := 'string64',
    A_kind := ['A'],
    CLASS := 'TABLE',
    FIELD_0_FILL := 0,
    FIELD_0_NAME := 'index',
    FIELD_1_FILL := '',
    FIELD_1_NAME := 'A',
    NROWS := 50000000,
    TITLE := '',
    VERSION := '2.7',
    index_kind := 'integer']
结果如下:

In [8]: %timeit pd.read_hdf('test.h5','df',where='A = "foo00002"')
1 loops, best of 3: 277 ms per loop

In [9]: %timeit pd.read_hdf('test_zlib.h5','df',where='A = "foo00002"')
1 loops, best of 3: 391 ms per loop

In [10]: %timeit pd.read_hdf('test.h5','df',where='A = "bar"')
1 loops, best of 3: 533 ms per loop

In [11]: %timeit pd.read_hdf('test_zlib2.h5','df',where='A = "bar"')
1 loops, best of 3: 504 ms per loop
In [9]:%timeit pd.read_hdf('test.h5','df',where='A = "foo00002"')
1 loops, best of 3: 1.02 s per loop

In [10]:%timeit pd.read_hdf('test_zlib.h5','df',where='A = "foo00002"')
1 loops, best of 3: 980 ms per loop

In [11]:%timeit pd.read_hdf('test.h5','df',where='A = "bar"')
1 loops, best of 3: 7.02 s per loop

In [12]:%timeit pd.read_hdf('test_zlib.h5','df',where='A = "bar"')
1 loops, best of 3: 7.27 s per loop
由于差异可能还不够大,我尝试了同样的实验,但使用了更大的数据帧。另外,我在另一台机器上做了这个实验,一台是Linux

这是代码(我刚刚将原始数据集乘以10):

这是表格:

!ptdump -av test.h5
/ (RootGroup) ''
  /._v_attrs (AttributeSet), 4 attributes:
   [CLASS := 'GROUP',
    PYTABLES_FORMAT_VERSION := '2.1',
    TITLE := '',
    VERSION := '1.0']
/df (Group) ''
  /df._v_attrs (AttributeSet), 14 attributes:
   [CLASS := 'GROUP',
    TITLE := '',
    VERSION := '1.0',
    data_columns := ['A'],
    encoding := None,
    index_cols := [(0, 'index')],
    info := {1: {'type': 'Index', 'names': [None]}, 'index': {}},
    levels := 1,
    nan_rep := 'nan',
    non_index_axes := [(1, ['A'])],
    pandas_type := 'frame_table',
    pandas_version := '0.10.1',
    table_type := 'appendable_frame',
    values_cols := ['A']]
/df/table (Table(500000000,)) ''
  description := {
  "index": Int64Col(shape=(), dflt=0, pos=0),
  "A": StringCol(itemsize=9, shape=(), dflt='', pos=1)}
  byteorder := 'little'
  chunkshape := (15420,)
  autoindex := True
  colindexes := {
    "A": Index(6, medium, shuffle, zlib(1)).is_csi=False,
    "index": Index(6, medium, shuffle, zlib(1)).is_csi=False}
  /df/table._v_attrs (AttributeSet), 11 attributes:
   [A_dtype := 'string72',
    A_kind := ['A'],
    CLASS := 'TABLE',
    FIELD_0_FILL := 0,
    FIELD_0_NAME := 'index',
    FIELD_1_FILL := '',
    FIELD_1_NAME := 'A',
    NROWS := 500000000,
    TITLE := '',
    VERSION := '2.7',
    index_kind := 'integer']
以下是文件:

-rw-rw-r-- 1 user user 8.2G Oct  5 14:00 test.h5
-rw-rw-r-- 1 user user 9.9G Oct  5 14:30 test_zlib.h5
结果如下:

In [8]: %timeit pd.read_hdf('test.h5','df',where='A = "foo00002"')
1 loops, best of 3: 277 ms per loop

In [9]: %timeit pd.read_hdf('test_zlib.h5','df',where='A = "foo00002"')
1 loops, best of 3: 391 ms per loop

In [10]: %timeit pd.read_hdf('test.h5','df',where='A = "bar"')
1 loops, best of 3: 533 ms per loop

In [11]: %timeit pd.read_hdf('test_zlib2.h5','df',where='A = "bar"')
1 loops, best of 3: 504 ms per loop
In [9]:%timeit pd.read_hdf('test.h5','df',where='A = "foo00002"')
1 loops, best of 3: 1.02 s per loop

In [10]:%timeit pd.read_hdf('test_zlib.h5','df',where='A = "foo00002"')
1 loops, best of 3: 980 ms per loop

In [11]:%timeit pd.read_hdf('test.h5','df',where='A = "bar"')
1 loops, best of 3: 7.02 s per loop

In [12]:%timeit pd.read_hdf('test_zlib.h5','df',where='A = "bar"')
1 loops, best of 3: 7.27 s per loop
以下是我的Pandas和Pytable版本:

user@host:~/$ pip show tables
---
Name: tables
Version: 3.1.1
Location: /usr/local/lib/python2.7/dist-packages
Requires: 

user@host:~/$ pip show pandas
---
Name: pandas
Version: 0.14.1
Location: /usr/local/lib/python2.7/dist-packages
Requires: python-dateutil, pytz, numpy
虽然我很确定这个问题与熊猫无关,因为我观察到在不使用熊猫的情况下只使用Pytable时也有类似的行为

更新2:

我已经切换到Pytables 3.0.0,问题得到了解决。这使用的文件与Pytables 3.1.1生成的文件相同

In [4]:%timeit pd.read_hdf('test.h5','df',where='A = "bar"')
1 loops, best of 3: 205 ms per loop

In [4]:%timeit pd.read_hdf('test_zlib.h5','df',where='A = "bar"')
10 loops, best of 3: 101 ms per loop

我认为你的问题是我们前一段时间和PyTables的家伙们提出的一个bug。本质上,当使用压缩存储并指定expectedrows和使用索引列时,会导致索引错误

解决方案只是不使用expectedrows,而是使用指定的chunkshape(或AUTO)重新打包文件。无论如何,这是一个很好的做法。此外,不确定是否预先指定了压缩,但最好通过ptrepack实现,请参阅文档。它们也是一个问题,所以关于这一点(现在找不到它,基本上是如果您正在创建文件,不要预先编制索引,但当您完成附加后,如果可以的话)

在任何情况下,创建测试存储:

In [1]: df = DataFrame({'A' : [ 'foo%05d' % i for i in range(50000) ]})

In [2]: df = pd.concat([ df ] * 20)
追加50万行

In [4]: store = pd.HDFStore('test.h5',mode='w')

In [6]: for i in range(50):
   ...:     print "%s" % i
   ...:     store.append('df',df,data_columns=['A'])
   ...:     
这是桌子

In [9]: !ptdump -av test.h5
/ (RootGroup) ''
  /._v_attrs (AttributeSet), 4 attributes:
   [CLASS := 'GROUP',
    PYTABLES_FORMAT_VERSION := '2.1',
    TITLE := '',
    VERSION := '1.0']
/df (Group) ''
  /df._v_attrs (AttributeSet), 14 attributes:
   [CLASS := 'GROUP',
    TITLE := '',
    VERSION := '1.0',
    data_columns := ['A'],
    encoding := None,
    index_cols := [(0, 'index')],
    info := {1: {'type': 'Index', 'names': [None]}, 'index': {}},
    levels := 1,
    nan_rep := 'nan',
    non_index_axes := [(1, ['A'])],
    pandas_type := 'frame_table',
    pandas_version := '0.10.1',
    table_type := 'appendable_frame',
    values_cols := ['A']]
/df/table (Table(50000000,)) ''
  description := {
  "index": Int64Col(shape=(), dflt=0, pos=0),
  "A": StringCol(itemsize=8, shape=(), dflt='', pos=1)}
  byteorder := 'little'
  chunkshape := (8192,)
  autoindex := True
  colindexes := {
    "A": Index(6, medium, shuffle, zlib(1)).is_csi=False,
    "index": Index(6, medium, shuffle, zlib(1)).is_csi=False}
  /df/table._v_attrs (AttributeSet), 11 attributes:
   [A_dtype := 'string64',
    A_kind := ['A'],
    CLASS := 'TABLE',
    FIELD_0_FILL := 0,
    FIELD_0_NAME := 'index',
    FIELD_1_FILL := '',
    FIELD_1_NAME := 'A',
    NROWS := 50000000,
    TITLE := '',
    VERSION := '2.7',
    index_kind := 'integer']
创建blosc和zlib版本

In [12]: !ptrepack --complib blosc --chunkshape auto --propindexes test.h5 test_blosc.h5

In [13]: !ptrepack --complib zlib --chunkshape auto --propindexes test.h5 test_zlib.h5

In [14]: !ls -ltr *.h5
-rw-rw-r-- 1 jreback users 866182540 Oct  4 20:31 test.h5
-rw-rw-r-- 1 jreback users 976674013 Oct  4 20:36 test_blosc.h5
-rw-rw-r-- 1 jreback users 976674013 Oct  4  2014 test_zlib.h5
Perf非常相似(对于找到的行)

和缺少行(尽管压缩的行在这里的性能更好)

所以。尝试不使用预期的行说明符,并使用ptrepack


如果您希望此列的条目密度相对较低(例如,唯一条目的数量较少),则另一种可能性是。就是选择整个列,
store。在本例中,选择_列('df','A')。unique()
,并将其用作快速查找机制(因此您根本不需要搜索)。

感谢Jeff的帮助,我通过将Pytables降级到3.0.0版解决了这个问题。该问题已向Pytables的开发人员报告。

能否显示df.info()和pandas/Pytables版本嘿,非常感谢你的帮助,这个问题让我发疯了。我只是复制了你的实验并用结果更新了帖子。您使用的是什么版本的Pytables?再次非常感谢!这与PyTables 3.0.0相同,但3.1.1和应该相同(和pandas master),但0.14.1也应该相同。我切换到PyTables 3.0.0,问题就解决了。我将向Pytables的开发人员报告这一点。谢谢有趣的是,3.1.1中的回归。好吧。@Jeff谢谢你在对我(单独)问题的评论中指出这一点。在我的例子中,这不是一个
expectedrows
问题,而是pytables 3.1.1中关于StringCols索引的明显回归。我发现了OP的github问题。以防您感兴趣:
In [11]: %timeit pd.read_hdf('test.h5','df',where='A = "bar"')
10 loops, best of 3: 82.4 ms per loop

In [17]: %timeit pd.read_hdf('test_blosc.h5','df',where='A = "bar"')
10 loops, best of 3: 32.2 ms per loop

In [18]: %timeit pd.read_hdf('test_zlib.h5','df',where='A = "bar"')
10 loops, best of 3: 32.3 ms per loop