Pandas 处理百万行数据内存溢出怎么优化代码

文章导读
遇到 Pandas 处理大规模数据或内存受限场景,最直接的优化方向是减少单次加载的数据量或降低数据类型占用,适合数据量超过物理内存或接近内存阈值的场景。需注意,纯数值型的百万行数据通常在 Pandas 承受范围内,内存溢出多发生于包含大量文本列或数据量达到千万级以上的情况。
📋 目录
  1. A 定位内存占用
  2. B 完整优化代码示例
  3. C 验证优化效果
  4. D 超大数据替代方案
  5. E 常见坑
  6. F 参考来源
A A

遇到 Pandas 处理大规模数据或内存受限场景,最直接的优化方向是减少单次加载的数据量或降低数据类型占用,适合数据量超过物理内存或接近内存阈值的场景。需注意,纯数值型的百万行数据通常在 Pandas 承受范围内,内存溢出多发生于包含大量文本列或数据量达到千万级以上的情况。

先说结论:优先通过分块读取和类型优化降低内存峰值,必要时更换处理工具。

  • 先定位:使用 memory_usage 确认占用最高的列
  • 先做:调整 dtype 或使用 chunksize 分片处理
  • 再验证:观察进程内存变化及程序是否完成

定位内存占用

在优化前,先读取少量样本数据确认各列数据类型及内存占用,避免盲目优化。

import pandas as pd

# 仅读取前 1000 行进行诊断
df_sample = pd.read_csv('data.csv', nrows=1000)
print(df_sample.info(memory_usage='deep'))
print(df_sample.memory_usage(deep=True))

重点关注 object 类型的列,它们通常占用内存最大。

完整优化代码示例

以下是一个完整的可运行脚本,包含类型映射、分块处理及异常处理逻辑。

import pandas as pd
import tracemalloc
import os

def optimize_dtypes(df):
    """优化数据类型以降低内存"""
    for col in df.columns:
        col_type = df[col].dtype
        if col_type == 'object':
            # 文本列转为 category,若唯一值过多则保持原样
            if df[col].nunique() / len(df) < 0.5:
                df[col] = df[col].astype('category')
        elif col_type == 'int64':
            df[col] = df[col].astype('int32')
        elif col_type == 'float64':
            df[col] = df[col].astype('float32')
    return df

def process(chunk):
    """具体的业务处理逻辑"""
    # 示例:简单的聚合操作
    return chunk.sum(numeric_only=True)

def main():
    file_path = 'data.csv'
    if not os.path.exists(file_path):
        print("文件不存在")
        return

    tracemalloc.start()
    chunk_size = 100000
    results = []

    try:
        # 使用分块读取,指定需要的列和类型
        for chunk in pd.read_csv(file_path, chunksize=chunk_size, usecols=['col1', 'col2']):
            chunk = optimize_dtypes(chunk)
            result = process(chunk)
            results.append(result)
            # 显式删除大对象,依赖 Python 自动内存管理
            del chunk
    except Exception as e:
        print(f"处理过程中出错:{e}")
    finally:
        current, peak = tracemalloc.get_traced_memory()
        tracemalloc.stop()
        print(f"峰值内存占用:{peak / 1024 / 1024:.2f} MB")

if __name__ == '__main__':
    main()

验证优化效果

通过对比优化前后的内存峰值,确认优化是否生效。可使用 tracemalloc 或系统监控工具。

# 优化前
start_mem = df.memory_usage(deep=True).sum() / 1024 ** 2

# 执行优化逻辑...
# df = optimize_dtypes(df)

# 优化后
end_mem = df.memory_usage(deep=True).sum() / 1024 ** 2
print(f"内存减少:{(1 - end_mem / start_mem) * 100:.2f}%")

若进程 RSS 内存稳定在阈值以下且无 Swap 交换,说明优化有效。

Pandas 处理百万行数据内存溢出怎么优化代码

超大数据替代方案

若数据量远超物理内存(如数十 GB),Pandas 可能不再适用,建议考虑以下工具:

  • Dask:兼容 Pandas API,支持并行和分块计算。
  • Polars:基于 Rust 编写,内存效率更高,支持懒执行。
# Polars 示例
import polars as pl
df = pl.scan_csv('data.csv').filter(pl.col('col1') > 0).collect()

常见坑

1. 隐式拷贝:某些操作如 df['col'] = ... 可能会触发 Copy-on-Write 机制产生副本,注意检查。

2. 分类变量滥用:如果唯一值数量接近行数,使用 category 类型反而会增加内存。

3. inplace 参数:新版 Pandas 不建议过度依赖 inplace,有时显式赋值更可控。

4. 手动 GC:避免频繁调用 gc.collect(),使用 del 删除无用变量即可,依赖 Python 自动内存管理。

参考来源

  • Pandas Official Documentation, User Guide: IO tools, https://pandas.pydata.org/docs/user_guide/io.html
  • Pandas Official Documentation, User Guide: Enhancing performance, https://pandas.pydata.org/docs/user_guide/enhancingperf.html