Pandas读取非数值数据的hdf文件速度非常慢

6
使用pandas.read_hdf()读取大型hdf文件的读取速度非常慢。我的hdf文件有5000万行,3列为整数,2列为字符串。使用带表格式和索引的to_hdf()写入需要花费近10分钟。虽然这也很慢,但我不太关心,因为读取速度更重要。
我尝试了将其保存为固定/表格格式,带/不带压缩,但读取时间在2-5分钟之间。相比之下,对同一数据使用read_csv()需要4分钟。
我还尝试直接使用pytables读取hdf。这样的速度快得多,只需要6秒钟,这是我想要看到的速度。
h5file = tables.open_file("data.h5", "r")
table = h5file.root.data.table.read()

我注意到文档中所有的速度比较都只使用数字数据,我自己运行测试也得到了类似的性能表现。

我想问一下是否有什么方法可以优化读取性能?

编辑

这里是数据样例:

               col_A     col_B    col_C     col_D                 col_E
30649671  1159660800  10217383        0  10596000                LACKEY
26198715  1249084800   0921720        0         0           KEY CLIFTON
19251910   752112000   0827092      104    243000                WEMPLE
47636877  1464739200  06247715        0         0                 FLOYD
14121495  1233446400  05133815        0    988000        OGU ALLYN CH 9
41171050  1314835200  7C140009        0     39000             DEBERRY A
45865543  1459468800   0314892       76    254000               SABRINA
13387355   970358400  04140585       19   6956000              LA PERLA
4186815    849398400  02039719        0  19208000  NPU UNIONSPIELHAGAN1
32666568   733622400  10072006        0   1074000                 BROWN

以下是有关数据框的信息:

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 52046850 entries, 0 to 52046849
Data columns (total 5 columns):
col_A        int64
col_B        object
col_C        int64
col_D        int64
col_E        object
dtypes: int64(3), object(2)
memory usage: 1.9+ GB

你能提供一个小的数据样本吗?例如 df.head(10) - MaxU - stand with Ukraine
我已经编辑了问题,并在数据帧上放置了数据和信息的样本。 - kayoz
你必须使用HDF格式工作,还是也可以考虑其他格式?你是否总是需要读取整个数据集,或者只需要读取部分数据(有条件地)? - MaxU - stand with Ukraine
我不必使用HDF。实际上,我正在寻找最快读取速度的最佳格式。在看到很多有关HDF速度快的评论后,我认为这可能是解决方案。您有其他建议吗? - kayoz
我正在写一个答案,并提供一些示例...您是否需要有条件地读取数据?也就是说,当读取HDF文件时,您是否会使用where='...query...'参数? - MaxU - stand with Ukraine
如果可以加快数据检索的速度,那会很好。典型的使用情况不太可能需要所有的数据。但是如果使用 pandas 进行内存处理更快,我很乐意接受。 - kayoz
1个回答

7

这是一个小示例:

生成样本数据框(1M行):

N = 10**6

df = pd.DataFrame({
    'n1': np.random.randint(10**6, size=N),
    'n2': np.random.randint(10**6, size=N),
    'n3': np.random.randint(10**6, size=N),
    's1': pd.util.testing.rands_array(10, size=N),
    's2': pd.util.testing.rands_array(40, size=N),
})

让我们将其写入CSV、HDF5(固定、表格和表格+data_columns=True)以及Feather格式的磁盘中。

df.to_csv(r'c:/tmp/test.csv', index=False)
df.to_hdf(r'c:/tmp/test_fix.h5', 'a')
df.to_hdf(r'c:/tmp/test_tab.h5', 'a', format='t')
df.to_hdf(r'c:/tmp/test_tab_idx.h5', 'a', format='t', data_columns=True)

import feather
feather.write_dataframe(df, 'c:/tmp/test.feather')

阅读:
In [2]: %timeit pd.read_csv(r'c:/tmp/test.csv')
1 loop, best of 3: 4.48 s per loop

In [3]: %timeit pd.read_hdf(r'c:/tmp/test_fix.h5','a')
1 loop, best of 3: 1.24 s per loop

In [4]: %timeit pd.read_hdf(r'c:/tmp/test_tab.h5','a')
1 loop, best of 3: 5.65 s per loop

In [5]: %timeit pd.read_hdf(r'c:/tmp/test_tab_idx.h5','a')
1 loop, best of 3: 5.6 s per loop

In [6]: %timeit feather.read_dataframe(r'c:/tmp/test.feather')
1 loop, best of 3: 589 ms per loop

条件读取 - 只选择那些 n2 <= 100000 的行。

In [7]: %timeit pd.read_hdf(r'c:/tmp/test_tab_idx.h5','a', where="n2 <= 100000")
1 loop, best of 3: 1.18 s per loop

我们筛选后需要选择的数据越少,速度就越快:

In [8]: %timeit pd.read_hdf(r'c:/tmp/test_tab_idx.h5','a', where="n2 <= 100000 and n1 > 500000")
1 loop, best of 3: 763 ms per loop

In [10]: %timeit pd.read_hdf(r'c:/tmp/test_tab_idx.h5','a', where="n2 <= 100000 and n1 > 500000 and n3 < 50000")
1 loop, best of 3: 379 ms per loop

更新:对于Pandas版本0.20.0+,我们可以直接读写feather格式(感谢@jezrael的提示):

In [3]: df.to_feather(r'c:/tmp/test2.feather')

In [4]: %timeit pd.read_feather(r'c:/tmp/test2.feather')
1 loop, best of 3: 583 ms per loop

生成的DF示例:

In [13]: df
Out[13]:
            n1      n2      n3          s1                                        s2
0       719458  808047  792611  Fjv4CoRv2b  2aWQTkutPlKkO38fRQh2tdh1BrnEFavmIsDZK17V
1       526092  950709  804869  dfG12EpzVI  YVZzhMi9sfazZEW9e2TV7QIvldYj2RPHw0TXxS2z
2       109107  801344  266732  aoyBuHTL9I  ui0PKJO8cQJwcvmMThb08agWL1UyRumYgB7jjmcw
3       873626  814409  895382  qQQms5pTGq  zvf4HTaKCISrdPK98ROtqPqpsG4WhSdEgbKNHy05
4       212776  596713  924623  3YXa4PViAn  7Y94ykHIHIEnjKvGphYfAWSINRZtJ99fCPiMrfzl
5       375323  401029  973262  j6QQwYzfsK  PNYOM2GpHdhrz9NCCifRsn8gIZkLHecjlk82o44Y
6       232655  937230   40883  NsI5Y78aLT  qiKvXcAdPVbhWbXnyD3uqIwzS7ZsCgssm9kHAETb
7        69010  438280  564194  N73tQaZjey  ttj1IHtjPyssyADMYiNScflBjN4SFv5bk3tbz93o
8       988081    8992  968871  eb9lc7D22T  sb3dt1Ndc8CUHyvsFJgWRrQg4ula7KJ76KrSSqGH
9       127155   66042  881861  tHSBB3RsNH  ZpZt5sxAU3zfiPniSzuJYrwtrytDvqJ1WflJ4vh3
...        ...     ...     ...         ...                                       ...
999990  805220   21746  355944  IMCMWuf97L  bj7tSrgudA5wLvWkWVQyNVamSGmFGOeQlIUoKXK3
999991  232596  293850  741881  JD0SVS5uob  kWeP8DEw19rwxVN3XBBcskibMRGxfoToNO9RDeCT
999992  532752  733958  222003  9X4PopnltN  dKhsdKFK1EfAATBFsB5hjKZzQWERxzxGEQZWAvSe
999993  308623  717897  703895  Fg0nuq63hA  kHzRecZoaG5tAnLbtlq1hqtfd2l5oEMFbJp4NjhC
999994  841670  528518   70745  vKQDiAzZNf  M5wdoUNfkdKX2VKQEArvBLYl5lnTNShjDLwnb8VE
999995  986988  599807  901853  r8iHjo39NH  72CfzCycAGoYMocbw3EbUbrV4LRowFjSDoDeYfT5
999996  384064  429184  203230  EJy0mTAmdQ  1jfUQCj2SLIktVqIRHfYQW2QYfpvhcWCbRLO5wqL
999997  967270  565677  146418  KWp2nH1MbM  hzhn880cuEpjFhd5bd7vpgsjjRNgaViANW9FHwrf
999998  130864  863893    5614  L28QGa22f1  zfg8mBidk8NTa3LKO4rg31Z6K4ljK50q5tHHq8Fh
999999  528532  276698  553870  0XRJwqBAWX  0EzNcDkGUFklcbKELtcr36zPCMu9lSaIDcmm0kUX

[1000000 rows x 5 columns]

1
在新版本的pandas中,feathers有了新的函数 - 链接 - jezrael
如果可能的话,请升级您的pandas,并为其添加时间。 - jezrael
感谢您的回复,@MaxU。我已经使用更可比较的文件大小运行了相同的测试,并获得了更好的读取速度,大约为30秒。我将返回我的原始数据,找出可能导致缓慢的原因并报告。不过,我仍然很好奇为什么read_hdf比pytables的open要慢这么多,这个差距在这种情况下大约是30倍。 - kayoz
再次感谢@MaxU进行这些测试。不幸的是,经过大量测试后,我在我的真实数据上无法获得相同的性能。我决定选择数据库解决方案。 - kayoz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接