如何高效存储与加载含numpy数组列的大型pd.DataFrame?
优化带高维数组大型DataFrame的加载速度
针对你430万行、含512维numpy数组列的DataFrame加载需求,以下是几种能显著缩短加载时间的方案:
一、更换更高效的存储格式
Parquet(优先推荐)
相比Feather,Parquet的列存储特性对复杂数值类型的序列化/反序列化效率更高,还支持谓词下推与列筛选。保存时指定pyarrow引擎,按Id范围分区(比如每100万行一个分区):df.to_parquet('data_partitioned', engine='pyarrow', partition_cols=['Id'])加载时可通过
filters参数只读取目标分区,或用usecols指定所需列,大幅减少IO量:loaded_df = pd.read_parquet('data_partitioned', engine='pyarrow', filters=[('Id', '>=', 27693), ('Id', '<=', 30000)], use_threads=True)单线程加载速度通常比Feather快30%-50%,多线程模式下提升更明显。
HDF5(固定格式)
纯数值型数据场景下,HDF5的fixed格式几乎是内存直接映射,序列化开销极低。保存与加载代码:# 保存 df.to_hdf('data.h5', key='features_data', format='fixed') # 加载 loaded_df = pd.read_hdf('data.h5', key='features_data')适合单线程全量加载,初始加载速度比Parquet更快,但并行加载支持较弱。
二、拆分高维数组列
把512维的Features数组拆分为512个独立的数值列(如Features_0到Features_511),列存储格式能对单一数值列做更高压缩比的编码,IO量大幅降低:
# 拆分列 expanded_df = df['Features'].apply(pd.Series).add_prefix('Features_') expanded_df['Id'] = df['Id'] # 保存为Parquet expanded_df.to_parquet('expanded_data', partition_cols=['Id'], engine='pyarrow')
加载时可精准指定所需特征列,进一步减少数据读取量:
loaded_df = pd.read_parquet('expanded_data', usecols=['Id', 'Features_0', 'Features_100'], engine='pyarrow')
三、内存映射(按需访问场景)
如果不需要全量加载,仅需随机访问部分数据,用numpy内存映射可以实现"零延迟"初始加载:
# 保存 # 拼接所有Features为二维数组并保存二进制 features_array = np.vstack(df['Features'].values) features_array.tofile('features_raw.bin') # 单独保存Id列 df['Id'].to_csv('id_list.csv', index=False) # 加载(仅创建映射指针,实际数据按需读取) features_mmap = np.memmap('features_raw.bin', dtype=np.float64, mode='r', shape=(4300000, 512)) ids = pd.read_csv('id_list.csv')['Id'].values
这种方式仅在访问具体行/列时才会从磁盘读取数据,适合随机查询场景。
四、现有分区方案的优化
如果坚持用分区存储,可做以下调整:
- 减少分区数量:将原9个50万行分区合并为4-5个100万行分区,降低文件IO的开闭合开销。
- 启用多线程加载:读取Feather时指定
use_threads=True,利用CPU多核加速:loaded_df = pd.read_feather('partition_path', use_threads=True)
内容的提问来源于stack exchange,提问作者Johnny
相关产品推荐
相关产品推荐

