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

百GB级Pandas(numpy ndarray)数据:部分加载与增量修改方案

numpy ndarray类型DataFrame的Parquet存储问题解决方案

1. 写入效率极低问题

逐行用df.loc插入是Pandas最常见的性能误区,和Parquet本身的写入性能无关:Pandas底层依赖连续内存块存储数据,每次逐行插入都会触发全量DataFrame的内存重分配和拷贝,行数越高单次插入的耗时越长,你观察到的「i越大单步插入越慢」就是这个原因。

解决方式是彻底放弃逐行插入的写法,按列批量生成所有数据后一次性构造DataFrame,同数据规模下耗时可以从8分钟降到10秒以内:

import numpy as np
import pandas as pd

n_rows = 100000
vec_len = 2000
# 按列批量生成所有ndarray,不要逐行拼接
data = {
    "a": [np.random.rand(vec_len) for _ in range(n_rows)],
    "b": [np.random.rand(vec_len) for _ in range(n_rows)]
}
# 一次性构造DataFrame并设置索引
x = pd.DataFrame(data, index=[f"t{i}" for i in range(n_rows)])
x.to_parquet("test.parquet")

如果总数据量大到无法一次性加载到内存,就分块生成数据,调用pyarrow.parquet.ParquetWriter逐块写入Parquet文件即可,不需要攒全量数据。

2. 无法按索引按需读取单行问题

原生pd.read_parquet仅支持列裁剪,要实现不加载全量文件的行级点查,需要基于Parquet的行组元数据做过滤下推,步骤如下:

  • 写入Parquet时主动保留索引列,设置合理的行组大小(建议单组1~2万行),Parquet会自动为每个行组的列生成min/max统计信息
  • 读取时先加载Parquet文件的轻量元数据,通过索引列的统计值定位目标行所在的行组,仅读取对应行组的数据再过滤目标行,全程不需要加载全文件
    参考实现代码:
import pyarrow.parquet as pq

# 写入时指定行组大小,保留索引
x.to_parquet("test.parquet", row_group_size=10000, index=True)

pf = pq.ParquetFile("test.parquet")
target_idx = "t123"
target_row = None
# 遍历行组元数据,定位目标索引所在的行组
for rg_idx in range(pf.metadata.num_row_groups):
    rg_meta = pf.metadata.row_group(rg_idx)
    # 读取索引列的统计值,判断目标索引是否在当前行组范围内
    idx_col_meta = rg_meta.column(2) # 索引列位置根据实际列顺序调整
    min_idx = idx_col_meta.statistics.min
    max_idx = idx_col_meta.statistics.max
    if min_idx <= target_idx <= max_idx:
        # 仅读取目标行组,过滤得到目标行
        rg_df = pf.read_row_group(rg_idx).to_pandas()
        target_row = rg_df.loc[target_idx]
        break

如果点查频率很高,可以在写入时给索引列开启布隆过滤器,不需要遍历所有行组元数据就能定位目标位置,点查延迟可以降到百毫秒级别。

3. 增量修改必须重写全文件问题

Parquet本身是不可变列式存储格式,设计上不支持单文件内的原地修改,不存在直接修改单个100GB Parquet文件、只写增量部分的方案。针对大文件增量修改的需求,用分区存储的思路规避全量重写:

  • 不要把所有数据存成单个大Parquet文件,按索引规则拆分成多个独立的小Parquet分片,比如每10万行存一个分片,单分片大小控制在1~2GB
  • 新增、修改单行时,先定位到该行对应的分片,仅重写这一个1~2GB的分片即可,其余分片完全不需要改动
  • 频繁小批量写入时,先把新增数据写到临时增量分片,攒到1GB以上的体量再和对应范围的旧分片合并,避免产生过多碎片文件
    如果增删改操作非常频繁,可以直接用基于Parquet实现的表格式组件,内置了增量写入、版本管理、小文件合并能力,不需要自己实现分片逻辑。

4. 无法存储多维numpy ndarray问题

你遇到的二维、三维数组写入报错,本质是Parquet原生类型体系没有对多维定长数组的支持,pandas和pyarrow的默认序列化逻辑不会自动处理多维数组的转换,硬存会抛类型错误。根据你的场景选对应方案即可:

  • 如果数组维度固定:写入时把多维数组展平成1维,额外新增列存储每个数组的shape信息,读取后调用reshape还原即可,性能损耗极低
  • 如果数组维度不固定、多维数组占比高:不要硬适配Parquet,Parquet的设计目标是结构化表数据,不是多维数组存储。直接换用Zarr或者HDF5格式,二者原生支持任意维度的numpy数组存储,支持分块读写、压缩、任意切片按需读取,和numpy的适配性远好于Parquet,100GB级别的场景性能表现稳定。其中Zarr为目录式存储,天生支持并行读写和增量修改,适配成本最低。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 03:36:16