如何将38GB超大JSON文件转换为DataFrame并避免内存错误?
38GB超大JSON转Pandas DataFrame解决内存错误方案
我有一个38GB的超大JSON文件,结构如下:
{ "Text": [ { "a": 6, "b": 2022, "c": 11, "d": "2022-11-24", "e": "567", "f": "ww", "i": "00", "j": 4, "k": "y", "l": null, "m": 3, "n": 7, "o": "54", "b": null, "q": "yes", "r": 77, "c": "yes", "t": 6, "y": 8, "v": "yy", "w": "yy", "x": "o", "y": "100 ", "z": "r" }, {...}, {...} ] }
我尝试用以下代码转换为Pandas DataFrame,但运行时出现内存错误:
df=pd.DataFrame([]) with open('data.json',encoding='utf-8-sig') as f: l=f.read() c=json.loads(l) v=list(c.keys())[0] data=(c[v]) for line in itertools.islice(data,2): json_nor=json_normalize(line) df=df.append(json_nor, ignore_index = True)
解决方法
1. 流式解析JSON,避免一次性加载全量数据
你的代码直接把38GB文件全部读入内存,必然触发内存错误。可以用ijson库流式读取JSON数组中的每个对象,分批次处理:
import ijson import pandas as pd from pandas import json_normalize # 先收集小DataFrame列表,最后一次性合并 df_list = [] with open('data.json', encoding='utf-8-sig') as f: # 定位到Text数组下的每个元素 objects = ijson.items(f, 'Text.item') for obj in objects: df_list.append(json_normalize(obj)) # 合并所有小DataFrame df = pd.concat(df_list, ignore_index=True)
如果内存依然紧张,可以每处理固定数量的对象就保存到磁盘,避免内存堆积:
import ijson import pandas as pd from pandas import json_normalize chunk_size = 10000 chunk_count = 0 df_list = [] with open('data.json', encoding='utf-8-sig') as f: objects = ijson.items(f, 'Text.item') for idx, obj in enumerate(objects): df_list.append(json_normalize(obj)) # 每10000条数据保存一次 if (idx + 1) % chunk_size == 0: chunk_count += 1 pd.concat(df_list, ignore_index=True).to_csv(f'chunk_{chunk_count}.csv', index=False) df_list = [] # 清空列表释放内存 # 处理剩余的未达批量的数据 if df_list: chunk_count += 1 pd.concat(df_list, ignore_index=True).to_csv(f'chunk_{chunk_count}.csv', index=False) # 合并所有分块CSV df = pd.concat([pd.read_csv(f'chunk_{i}.csv') for i in range(1, chunk_count+1)], ignore_index=True)
2. 提前指定数据类型,压缩内存占用
Pandas自动推断数据类型可能造成内存浪费,提前为字段指定更紧凑的类型:
dtype_spec = { 'a': 'int8', 'b': 'Int32', # 用Nullable类型处理null值 'c': 'category', # 重复值多的字符串用category类型 'd': 'datetime64[ns]', 'e': 'int16', # 其他字段根据实际值范围和类型指定 } # 合并时指定类型 df = pd.concat(df_list, ignore_index=True, dtype=dtype_spec) # 读取CSV时也可指定 df = pd.read_csv('chunk_1.csv', dtype=dtype_spec, parse_dates=['d'])
3. 替换低效的df.append(),改用pd.concat()
df.append()会频繁复制数据,效率极低。改用列表收集小DataFrame后一次性pd.concat(),能大幅降低内存开销和运行时间。
4. 用Dask处理超大规模数据
如果单进程Pandas仍无法处理,可以用Dask DataFrame,它支持并行分块处理,语法和Pandas兼容:
import dask.dataframe as dd from dask import delayed import ijson @delayed def process_chunk(chunk): return json_normalize(chunk) chunks = [] current_chunk = [] chunk_size = 10000 with open('data.json', encoding='utf-8-sig') as f: objects = ijson.items(f, 'Text.item') for idx, obj in enumerate(objects): current_chunk.append(obj) if (idx + 1) % chunk_size == 0: chunks.append(process_chunk(current_chunk)) current_chunk = [] if current_chunk: chunks.append(process_chunk(current_chunk)) # 构建Dask DataFrame dask_df = dd.from_delayed(chunks) # 直接导出到分块CSV dask_df.to_csv('dask_output_*.csv', index=False)
内容的提问来源于stack exchange,提问作者Fatima
相关产品推荐
相关产品推荐

