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

pandas处理大数据时melt与concat触发MemoryError的解决方案

问题原因

你的报错本质是内存不足,和pd.melt()、pd.concat()的语法无关。
你之前的分块写法完全没有降低峰值内存:你把所有分块melt后的结果全部存在pivot_list列表里,最后一次性做concat时,需要在内存里申请连续空间存储总共18.2亿行、object类型的全量结果,总共需要27.1GiB内存,超出了你当前环境的可用内存上限,才触发MemoryError。
加上你用了multiprocessing多进程,每个子进程会独立拷贝一份数据副本,会进一步放大内存占用。

可行解决方案

1. 先优化数据类型,从根源降低内存占用

pandas默认的数值类型、字符串类型内存浪费非常严重,处理前先把各列转成内存占用最小的类型,通常能直接减少60%以上的内存占用:

  • 整数类列(ID、Year)用pd.to_numeric(..., downcast='integer')转成最小位宽的整数类型,不要用默认int64
  • 取值重复率高的字符串列(Col2、Col3、melt后的New_Var列)转成category类型
  • 数值类的Value列用pd.to_numeric(..., downcast='float')转成最小位宽的浮点类型,不要用默认float64或object类型
    示例代码:
# 预处理原数据固定列类型
df['ID'] = pd.to_numeric(df['ID'], downcast='integer')
df['Year'] = pd.to_numeric(df['Year'], downcast='integer')
df[['Col2', 'Col3']] = df[['Col2', 'Col3']].astype('category')

2. 边处理边落盘,不要在内存中攒全量结果

不要用列表存储所有分块的处理结果,第一块处理完直接写入磁盘文件,后续块处理完以追加模式写入,从根本上避免全量数据占用内存:

  • 优先选parquet格式存储,比csv压缩比高3-10倍,读写速度更快
  • 每块处理完立刻删除临时变量、触发垃圾回收释放内存
  • 如果不是必须全局按ID排序,可以去掉最后的sort_values('ID')步骤,排序本身也会占用大量额外内存
    示例代码:
import gc
import pandas as pd

chunk_size = 100000
output_path = "./melted_result.parquet"
first_chunk = True

for i in range(0, len(df), chunk_size):
    # 取当前分块
    chunk = df.iloc[i:i+chunk_size]
    # melt处理
    chunk_melted = pd.melt(
        chunk,
        id_vars=['ID', 'Col2', 'Col3', 'Year'],
        var_name='New_Var',
        value_name='Value'
    )
    # 优化当前分块的列类型
    chunk_melted['New_Var'] = chunk_melted['New_Var'].astype('category')
    chunk_melted['Value'] = pd.to_numeric(chunk_melted['Value'], downcast='float', errors='ignore')
    
    # 写入文件,第一块写表头,后续块追加
    chunk_melted.to_parquet(
        output_path,
        engine="pyarrow",
        compression="snappy",
        append=not first_chunk,
        index=False
    )
    
    # 释放当前分块占用的内存
    del chunk, chunk_melted
    gc.collect()
    first_chunk = False

3. 换支持外存计算的框架处理超大数据

如果必须在流程中做全量排序、聚合等操作,不要用纯pandas,换用Dask等支持外存计算的框架,语法和pandas几乎一致,会自动分块调度,不需要把全量数据加载到内存:
示例代码:

import dask.dataframe as dd

# 把pandas df转成dask分块对象,也可以直接用dask读磁盘文件
ddf = dd.from_pandas(df, npartitions=10)
# 做melt操作,和pandas语法完全一致
melted_ddf = ddf.melt(
    id_vars=['ID', 'Col2', 'Col3', 'Year'],
    var_name='New_Var',
    value_name='Value'
).set_index("ID") # 按ID建索引即完成排序,不需要全量加载到内存
# 结果直接落盘
melted_ddf.to_parquet("./melted_dask_result/", write_index=True)
额外注意
  • 多进程场景下每个子进程会独立持有数据副本,内存占用会随进程数线性增长,如果单进程能处理不要开多进程
  • object类型的数组内存开销远大于数值、category类型,只要能转成其他类型就不要保留object类型

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 14:57:28