如何在16GB内存设备加载8.3GB超大Pickle文件用于数据探索?
针对Pickle大数据集的采样解决方案
下面是几个无需增加内存的可行方案,适配你按列存储的数据集场景:
1. 流式逐对象读取,按需采样
Pickle是按序列化对象逐个写入的,不管存储形式是列还是行,都可以循环调用pickle.load()逐对象读取,中途控制采样比例:
- 打开文件后,每次读取一个列对象
- 对每个列直接截取10%的数据(比如取前10%或随机采样10%)
- 累计读取到的总数据量达到目标后停止
示例代码:
import pickle import os file_path = "你的数据集文件.pkl" total_file_size = os.path.getsize(file_path) target_read_size = total_file_size * 0.1 current_read_size = 0 sampled_data = {} with open(file_path, "rb") as f: while current_read_size < target_read_size: try: # 假设存储格式是(列名, 列数据)的键值对 col_name, col_values = pickle.load(f) # 对单列采样10%,这里用简单的切片,也可以用随机采样 sampled_data[col_name] = col_values[:int(len(col_values)*0.1)] # 更新已读取的字节数 current_read_size = f.tell() except EOFError: break # 转成DataFrame方便后续分析(如果需要) import pandas as pd sample_df = pd.DataFrame(sampled_data)
2. 用pickletools解析结构,精准截取对象边界
既然你已经用pickletools.dis()分析过样本,可以用它定位主文件中每个列对象的字节边界,然后只读取指定比例的列:
- 先解析文件的opcode序列,统计总列数
- 读取前10%的列对象,或者每个列截取部分数据
示例代码:
import pickletools import pickle file_path = "你的数据集文件.pkl" total_cols = 0 # 先统计总共有多少列 with open(file_path, "rb") as f: while True: try: pickle.load(f) total_cols += 1 except EOFError: break target_cols = int(total_cols * 0.1) sampled_data = {} with open(file_path, "rb") as f: col_count = 0 while col_count < target_cols: try: col_name, col_values = pickle.load(f) sampled_data[col_name] = col_values col_count += 1 except EOFError: break
3. 转存为分块友好的格式(Parquet/Feather)
把Pickle转成Parquet这类支持分块读取的格式,后续采样会更方便:
- 流式读取Pickle的列数据,分批次写入Parquet文件
- 之后直接从Parquet中读取10%的样本
示例代码:
import pickle import pandas as pd import pyarrow.parquet as pq import pyarrow as pa file_path = "你的数据集文件.pkl" chunk_size = 8 # 每次处理8列,可根据内存调整 current_chunk = [] with open(file_path, "rb") as f: while True: try: col_name, col_values = pickle.load(f) current_chunk.append((col_name, col_values)) if len(current_chunk) == chunk_size: # 转成DataFrame并追加写入Parquet chunk_df = pd.DataFrame(dict(current_chunk)) pq.write_to_dataset( pa.Table.from_pandas(chunk_df), root_path="转换后的数据集.parquet", append=True ) current_chunk = [] except EOFError: # 处理剩余的列 if current_chunk: chunk_df = pd.DataFrame(dict(current_chunk)) pq.write_to_dataset( pa.Table.from_pandas(chunk_df), root_path="转换后的数据集.parquet", append=True ) break # 读取10%的样本 full_table = pq.read_table("转换后的数据集.parquet") sampled_table = full_table.sample(fraction=0.1) sampled_df = sampled_table.to_pandas()
4. 内存映射文件辅助读取
用mmap把文件映射到虚拟内存,避免一次性加载全部数据,再逐对象读取采样:
import pickle import mmap file_path = "你的数据集文件.pkl" sampled_data = {} target_ratio = 0.1 total_objs = 0 # 先统计总对象数 with open(file_path, "rb") as f: while True: try: pickle.load(f) total_objs += 1 except EOFError: break target_objs = int(total_objs * target_ratio) # 内存映射读取指定数量的对象 with open(file_path, "rb") as f: mm = mmap.mmap(f.fileno(), length=0, access=mmap.ACCESS_READ) obj_count = 0 while obj_count < target_objs: try: col_name, col_values = pickle.load(mm) sampled_data[col_name] = col_values obj_count += 1 except EOFError: break mm.close()
内容的提问来源于stack exchange,提问作者christallclear
相关产品推荐
相关产品推荐

