第 2 章 · 04 数据存储基准 本节摘要:本节是「市场与基本面数据」的收尾,解决一个工程现实问题:拿到一堆量级不菲的数据后,该用什么格式存?原项目 做了一组系统对比,比较 CSV、HDF5(分 fixed 与 table 两种格式)、Parquet 在「读写速度 + 文件体积」上的表现,并用 魔法函数计时。结论很清晰:纯数值数据 HDF5 最快;数值与文本混合时 Parquet 显著占优;CSV 各方面都最差,只用作可读交换格式。读完本节,你能为不同形态的数据选对存储格式,避免在 IO 上浪费几小时。 内容来源:原项目 与 ,汉化并套用体系化模板。
本节摘要:本节是「市场与基本面数据」的收尾,解决一个工程现实问题:拿到一堆量级不菲的数据后,该用什么格式存?原项目
02_market_and_fundamental_data/05_storage_benchmark/storage_benchmark.ipynb做了一组系统对比,比较 CSV、HDF5(分 fixed 与 table 两种格式)、Parquet 在「读写速度 + 文件体积」上的表现,并用%%timeit魔法函数计时。结论很清晰:纯数值数据 HDF5 最快;数值与文本混合时 Parquet 显著占优;CSV 各方面都最差,只用作可读交换格式。读完本节,你能为不同形态的数据选对存储格式,避免在 IO 上浪费几小时。
内容来源:原项目
02_market_and_fundamental_data/05_storage_benchmark/storage_benchmark.ipynb与README.md,汉化并套用体系化模板。
⚠️ 学习提示:存储格式看似细节,但在百万级样本 × 千只股票的数据规模下,选错格式可能让一次回测从 1 分钟变成 1 小时;务必实测自己的数据形态,不要照搬结论。
阅读完本节,你应当能够:
%%timeit 做读写与体积的基准测试。整本书要反复加载各种数据:Quandl 的日频股价(几千只股票 × 十几年 × 多列)、SEC 的 XBRL 数值点(九年 × 几千公司 × 上万 tag)、ITCH 的 tick 消息(单日就 12GB)。如果每次实验都从 CSV 读起,IO 时间会淹没真正的计算时间。所以需要选一种读快、写快、体积小的格式。
原 notebook 用一个可配置的测试 DataFrame(纯数值 / 数值+文本 / 不同行数),对每种格式测三个维度:文件大小、读时间、写时间,用 %%timeit 多次取平均。
| 格式 | 来源 | 在 pandas 中的接口 | 特点 |
|---|---|---|---|
| CSV | 通用文本 | pd.read_csv / df.to_csv |
人类可读,体积大,慢 |
| HDF5 fixed | NCSA 超算中心,通过 PyTables | pd.HDFStore(..., format='fixed') |
二进制,极快,不可查询 |
| HDF5 table | 同上 | pd.HDFStore(..., format='table') |
二进制,可查询可追加,稍慢 |
| Parquet | Apache Hadoop 生态,通过 pyarrow | pd.read_parquet / df.to_parquet |
列式存储,压缩好,文本强 |
💡 核心心法:CSV 是「给人看」的格式,HDF5/Parquet 是「给机器读」的格式。任何需要反复加载的数据,第一次预处理完就转成二进制格式,后续实验能省下数量级的时间。
HDF5 通过 PyTables 提供两种 pandas 接口:
# fixed 格式:整张表作为一个二进制块 df.to_hdf('store.h5', key='df', format='fixed') # table 格式:按表结构存,可查询可追加 df.to_hdf('store.h5', key='df', format='table') store.select('df', where="index > '2020-01-01'") # 可按条件查
| 维度 | fixed | table |
|---|---|---|
| 写入速度 | 极快 | 中等 |
| 读取速度 | 极快 | 较快 |
| 文件体积 | 较大(约 table 的两倍) | 较小(与 CSV 接近) |
| 可查询 | 否 | 是(where 条件) |
| 可追加 | 否 | 是 |
| 文本数据 | 支持 | 不支持写(原 notebook 明确警告) |
⚠️ 警告:原 notebook 明确写道「
writein table format does not work with text data」——如果你的 DataFrame 有字符串列,table 格式会写失败。这种场景只能用 fixed 或 Parquet。
原 notebook 用 %%timeit cell magic,自动多次执行取平均:
%%timeit df.to_hdf('test.h5', key='df', format='fixed') %%timeit pd.read_hdf('test.h5', key='df')
测试数据用 numpy.random 生成可控大小、可控类型(纯数值 / 加文本列)的 DataFrame,确保对比公平。最终把每种格式的大小、读、写结果汇总成一张表对比。
README.md 直接给出结论:
「纯数值数据,HDF5 表现最好,table 格式与 CSV 共享最小的内存占用约 1.6GB,fixed 格式占两倍空间,Parquet 占 2GB。数值与文本混合数据,Parquet 显著更快,而 HDF5 相对 CSV 的优势主要体现在读。**」
整理成表:
| 数据形态 | 体积最优 | 读最快 | 写最快 | 综合推荐 |
|---|---|---|---|---|
| 纯数值 | HDF5 table / CSV | HDF5 fixed | HDF5 fixed | HDF5 fixed |
| 数值 + 文本 | Parquet | Parquet | Parquet | Parquet |
| 需要查询/追加 | HDF5 table | HDF5 table | HDF5 table | HDF5 table(无文本时) |
| 可读交换 | CSV | — | — | CSV(仅交换用) |
CSV 的问题不止「慢」,更在于类型丢失:
parse_dates。所以 CSV 适合「与人/Excel/其他系统交换」,不适合作为反复实验的中间存储。原书在数据处理完后,默认存 HDF5(如 assets.h5),后续章节直接 pd.HDFStore 读,这是好习惯。
💡 核心心法:建立「原始 CSV/二进制 → 预处理 → 二进制存储 → 反复实验」的工作流。预处理只做一次,后续每次实验都从二进制格式起,这是金融机器学习工程的常识,但很多人忽略。
%%timeit:用 cell magic 做基准,多测取平均才公平。下一章,我们进入「数据篇」的扩展——另类数据获取,看市场与基本面之外的数据怎么用、有哪些坑,并用一个实战案例抓取财报电话会议文本。