You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pandas中HDF与Parquet加载的DataFrame索引操作速度差异原因

问题描述

我分别从hdf和parquet文件中加载了两个完全一致的pandas DataFrame,加载逻辑如下:

import pandas as pd
import pyarrow.parquet as pq
from pandas.testing import assert_frame_equal

hdf = "data/h5.h5"
parquet = "data/parquet.parquet"

hdf = pd.DataFrame(pd.read_hdf(hdf))
parquet = pd.DataFrame(pq.read_table(source=parquet, use_threads=False).to_pandas())
assert_frame_equal(hdf,
                   parquet,
                   check_index_type=True,
                   check_column_type=True,
                   check_exact=True)

通过assert_frame_equal校验可确认两个DataFrame的结构、数据完全一致,随后我定义了如下重置并重新设置索引的操作函数:

def reset_and_set_index(df):
    ix = df.index.names
    new = df.reset_index()
    new.set_index(ix)

使用%timeit魔法命令分别对两个DataFrame执行该函数的耗时进行测试,测试代码与结果如下:

%timeit reset_and_set_index(hdf)
3.62 s ± 79.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%timeit reset_and_set_index(parquet)
1.92 s ± 14.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

按照常规认知,DataFrame完成加载后均已驻留内存,相同操作不应存在明显耗时差异,为何从parquet加载的DataFrame执行上述索引操作的速度显著快于从hdf加载的版本?

原因解答

这个差异和数据是否驻留内存无关,核心是两种加载方式生成的DataFrame底层内存块布局不一样——assert_frame_equal只校验逻辑层面的值、类型、索引/列结构完全一致,不会检查底层的内存排布细节:

  • pd.read_hdf加载HDF5文件时,会直接沿用HDF存储时的分块规则生成pandas内部的Block结构,只要写入HDF时做过分块、或者表内列的数据类型比较杂,加载出来的DataFrame会被拆成很多个独立的小内存块。
  • pyarrow读取parquet转pandas的过程中,会自动把同数据类型的列合并成连续的大内存块,最终生成的DataFrame通常只有很少几个Block,比如所有数值列占一个连续块、所有字符串列占一个块,Block数量远少于HDF加载的版本。

而reset_index、set_index这类操作的耗时,对Block数量非常敏感:执行过程中pandas需要逐个遍历每个Block做数据拷贝、索引对齐,Block越多,遍历操作、内存拷贝、元数据维护的额外开销就越高,哪怕总数据量完全一样,耗时差出几倍很正常。
你可以直接执行print(len(hdf._mgr.blocks), len(parquet._mgr.blocks))验证,基本能看到HDF加载出来的DataFrame Block数是parquet版本的2倍左右,刚好对应你测出来的耗时差。
如果想让两者速度拉平,对HDF加载的DataFrame做一次全量拷贝hdf = hdf.copy(),触发pandas自动合并零散的内存块,再跑同样的计时测试,差距就基本消失了。


内容的提问来源于stack exchange,提问作者PanchoVarallo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 23:18:47