核心内容
先通过 内存分析 定位瓶颈,再通过 分块读取(chunksize / iterator) 应对超出内存的大文件。
一、内存分析
df.info(memory_usage=‘deep’)
import pandas as pd
df = pd.read_csv('large.csv')
# 默认只统计各列底层数据
df.info(memory_usage='deep')
# 输出包含:
# <class 'pandas.core.frame.DataFrame'>
# RangeIndex: 1000000 entries, 0 to 999999
# Data columns (total 10 columns):
# ...
# memory usage: 85.3 MB| 参数 | 说明 |
|---|---|
verbose=True | 显示每列 dtype 与缺失值 |
show_counts=True | 显示每列非空计数 |
memory_usage='deep' | 深层次统计(包含 object 列内部对象内存) |
df.memory_usage(deep=True)
# 每列内存占用(字节)
df.memory_usage(deep=True)
# Index 128
# col_1 8000000
# col_2 8000000
# dtype: int64
# 仅看数据列
df.memory_usage(deep=True, index=False)
# 总内存(MB)
df.memory_usage(deep=True).sum() / 1024**2其他辅助方法
# 查看各列 dtype 分布
df.dtypes.value_counts()
# 对象列各元素字符串长度
df['col'].str.len().describe()
# 估算唯一值比例
df.nunique() / len(df)二、分块读取
使用 chunksize 或 iterator=True 将大文件分批次读入内存。
chunksize — 分块迭代
import pandas as pd
chunk_iter = pd.read_csv('huge.csv', chunksize=100_000)
# 逐块处理
results = []
for chunk in chunk_iter:
# 每块是一个 DataFrame,包含 100_000 行
results.append(chunk.groupby('group')['value'].sum())
# 合并结果
final = pd.concat(results).groupby(level=0).sum()iterator — 返回 TextFileReader
reader = pd.read_csv('huge.csv', iterator=True)
# 每次读取指定行数
chunk1 = reader.get_chunk(50_000)
chunk2 = reader.get_chunk(50_000)
# 判断是否还有数据
chunk = reader.get_chunk(50_000) if reader.hasNext() else None
# 读取全部(等效于无 iterator)
full = reader.read()分块聚合示例
def process_in_chunks(filepath, chunksize, agg_func):
reader = pd.read_csv(filepath, chunksize=chunksize)
acc = {}
for chunk in reader:
result = agg_func(chunk)
for key, val in result.items():
acc[key] = acc.get(key, 0) + val
return acc
result = process_in_chunks(
'huge.csv', 100_000,
lambda df: df.groupby('category')['sales'].sum()
)三、迭代器
除文本文件外,chunksize / iterator 也适用于其他 IO:
# JSON 逐行读取
pd.read_json('big.json', lines=True, chunksize=10_000)
# Parquet 分块(pyarrow 引擎)
import pyarrow.parquet as pq
pf = pq.ParquetFile('big.parquet')
for batch in pf.iter_batches(batch_size=100_000):
df_batch = batch.to_pandas()文本文件迭代底层
# 完全逐行读取(最省内存) with open('huge.csv') as f: for line in f: # 逐行处理 pass
内存分析实战流程
# 1. 读取前预估
df = pd.read_csv('large.csv', nrows=10_000)
sample_memory = df.memory_usage(deep=True).sum()
estimated = sample_memory * (total_rows / 10_000)
# 2. 读取后分析
df.info(memory_usage='deep')
# 3. 优化 dtype 后再统计
df_opt = optimize_dtypes(df) # 见「数据类型优化」笔记
print(df_opt.memory_usage(deep=True).sum() / 1024**2, 'MB')注意事项
memory_usage(deep=True)对超大 DataFrame 本身也消耗时间- 分块时注意聚合结果的合并逻辑,避免结果也超出内存
相关笔记
- 21.4 数据类型优化 - 在分块前先优化 dtype
- 23.6 文本格式导出 - 导出时配合 compression 压缩
- 21.1 第三方库集成 - 内存不足时改用 Dask / Polars