Python中不使用Pickle存储大型变量至文件的方法
解决大numpy数组字典存储的MemoryError问题
我完全懂这种处理超大数值数据时卡内存的痛苦!先帮你算笔账:每个(1058694, 10, 9)的numpy数组,如果是float64类型(默认),单个数组就占了1058694*10*9*8 ≈ 750MB,7个加起来就是5GB+。Pickle的问题在于它需要把整个数据集加载到内存再完成序列化,这就难怪会触发MemoryError了。给你几个针对性的解决方案,按推荐程度排序:
1. 用HDF5格式存储(首推)
HDF5是专门为大规模数值数据设计的存储格式,核心优势是支持分块读写——不用把整个数据集塞进内存,而是按需处理小块数据。你可以用h5py库实现,把字典的每个键对应成HDF5文件里的一个数据集:
import h5py import numpy as np # 假设你的目标字典是big_dict big_dict = { 'feature_set_1': np.random.rand(1058694, 10, 9), 'feature_set_2': np.random.rand(1058694, 10, 9), # ... 剩下5个键值对 } # 写入HDF5文件,开启分块存储 with h5py.File('large_dataset.h5', 'w') as hdf_file: for key, arr in big_dict.items(): # chunks=True让h5py自动分块,也可以手动指定chunk大小比如(1000,10,9) hdf_file.create_dataset(key, data=arr, chunks=True) # 读取时的灵活操作 with h5py.File('large_dataset.h5', 'r') as hdf_file: # 读取完整数组 full_arr = hdf_file['feature_set_1'][()] # 只读取前1000行(无需加载整个数组) partial_arr = hdf_file['feature_set_1'][:1000, :, :]
这种方式不仅解决了内存问题,还支持随机访问数据片段,后续做数据分析也更方便。
2. 分文件存储单个numpy数组
如果不想引入HDF5的依赖,也可以把字典里的每个numpy数组单独存成.npy文件,再用一个小型映射文件记录键与文件名的对应关系:
import numpy as np import pickle import os # 创建存储目录 os.makedirs('array_storage', exist_ok=True) big_dict = { 'feature_set_1': np.random.rand(1058694, 10, 9), # ... 其他键值对 } # 保存每个数组+记录映射关系 key_filename_map = {} for idx, (key, arr) in enumerate(big_dict.items()): filename = f'array_storage/array_{idx}.npy' np.save(filename, arr) key_filename_map[key] = filename # 保存映射字典(这个文件很小,用Pickle完全没问题) with open('key_mapping.pkl', 'wb') as f: pickle.dump(key_filename_map, f) # 读取数据 with open('key_mapping.pkl', 'rb') as f: key_filename_map = pickle.load(f) loaded_dict = {} for key, filename in key_filename_map.items(): loaded_dict[key] = np.load(filename)
这种方案的好处是简单直观,每个数组独立存储,内存只需要处理单个数组的大小,完全不会触发MemoryError。
3. 优化Pickle的序列化方式(不推荐)
如果非要用Pickle,只能尝试拆分序列化流程,但本质上还是要加载单个数组到内存,只能缓解不能彻底解决问题:
import pickle import numpy as np big_dict = { 'feature_set_1': np.random.rand(1058694, 10, 9), # ... 其他键值对 } # 拆分写入:先存键,再逐个存数组 with open('big_data.pkl', 'wb') as f: pickle.dump(list(big_dict.keys()), f) for arr in big_dict.values(): pickle.dump(arr, f) # 读取时对应还原 with open('big_data.pkl', 'rb') as f: keys = pickle.load(f) loaded_dict = {} for key in keys: loaded_dict[key] = pickle.load(f)
这种方法只是避免了一次性把所有数组塞进内存,但单个数组的内存占用还是存在,所以只适合内存刚好差一点的情况,远不如前两种方案靠谱。
内容的提问来源于stack exchange,提问作者Gian Mauro Musso
相关产品推荐
相关产品推荐

