用Pandas读了个5GB的CSV内存直接爆了——大文件处理策略
本文目录
先说结论
Pandas处理大文件有四种策略:① chunksize分块读取(最简单);② dtype优化减少内存(最有效的单步优化);③ 只读需要的列(usecols参数);④ 换引擎——用Polars/DuckDB/PySpark替代Pandas处理大于内存的文件。数据量小于2GB优先优化Pandas,大于2GB考虑换引擎。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
策略一:chunksize分块读取
# 不要一次性读取整个文件
# df = pd.read_csv('huge.csv') # MemoryError!
# 分块读取,每次处理100万行
chunks = pd.read_csv('huge.csv', chunksize=1_000_000)
results = []
for chunk in chunks:
# 每个chunk是一个100万行的DataFrame
# 在这里做过滤/聚合,只保留需要的结果
filtered = chunk[chunk['amount'] > 0]
summary = filtered.groupby('country')['amount'].sum()
results.append(summary)
# 合并所有chunk的结果
final = pd.concat(results).groupby(level=0).sum()
# 这样内存中同时只有一个chunk(100万行 ≈ 几百MB)
# 而不是整个文件(几个GB)
# 注意:chunksize不适合需要全量排序或全局JOIN的操作
# 因为每个chunk只有部分数据,无法做全局操作
策略二:dtype优化——最有效的单步优化
# Pandas默认用int64/float64/object,非常浪费内存
# 优化前:Pandas自动推断类型
df = pd.read_csv('data.csv')
df.info(memory_usage='deep') # 800MB
# 优化后:手动指定更小的类型
df = pd.read_csv('data.csv', dtype={
'user_id': 'int32', # int64(8字节) => int32(4字节)
'amount': 'float32', # float64(8字节) => float32(4字节)
'country': 'category', # object(可变) => category(枚举编码)
'event_name': 'category', # 重复字符串用category最省
'is_paid': 'bool', # object => bool(1字节)
})
df.info(memory_usage='deep') # 250MB(省了70%!)
# category类型的原理:
# country列有50个不同值,100万行
# object类型:每行一个字符串对象 ≈ 100万 × 60字节 = 60MB
# category:50个字符串 + 100万个int8编码 = 3KB + 1MB ≈ 1MB
# 省了60倍!
策略三:只读需要的列
# CSV有50列,你只需要5列
# 错误:读全部列再选
df = pd.read_csv('data.csv') # 读了50列
df = df[['user_id', 'country', 'amount', 'event_name', 'dt']]
# 虽然最终只保留5列,但读取时全部50列都进了内存
# 正确:只读需要的列
df = pd.read_csv('data.csv',
usecols=['user_id', 'country', 'amount', 'event_name', 'dt'])
# 只读5列,IO量和内存都只有原来的1/10
# 对Parquet格式效果更好(列式存储)
df = pd.read_parquet('data.parquet',
columns=['user_id', 'country', 'amount'])
# Parquet的列裁剪在文件层面就跳过了不需要的列
# 比CSV的usecols更快(CSV还是要扫描整行才能提取某列)
策略四:换引擎——超过内存的数据
| 工具 | 适用数据量 | 核心优势 | 大数据场景 |
|---|---|---|---|
| Pandas | 小于1-2GB | 生态最成熟 | 小数据分析、结果后处理 |
| Polars | 小于10GB | 快5-10倍,惰性求值 | 中等数据的高性能处理 |
| DuckDB | 小于100GB | SQL接口,可查文件 | 直接SQL查Parquet/CSV |
| PySpark | GB到TB级 | 分布式集群处理 | 大规模ETL |
# DuckDB:直接用SQL查大文件(不需要全部加载到内存)
import duckdb
# 直接查5GB的Parquet文件
result = duckdb.sql("""
SELECT country, SUM(amount) AS total
FROM 'huge_data.parquet'
WHERE dt = '2024-01-15'
GROUP BY country
ORDER BY total DESC
LIMIT 20
""").df() # 返回Pandas DataFrame
# DuckDB使用流式处理+磁盘溢出
# 不需要把整个文件加载到内存
# 5GB数据只需要几百MB内存
# Polars的惰性求值
import polars as pl
result = (pl.scan_parquet('huge_data.parquet') # lazy,不立即加载
.filter(pl.col('dt') == '2024-01-15')
.group_by('country')
.agg(pl.col('amount').sum())
.sort('amount', descending=True)
.head(20)
.collect()) # 这里才真正执行
实战经验:3.5GB CSV的处理方案
在某短剧出海公司,运营需要分析一批广告投放数据——从MaxCompute导出的3.5GB CSV(约2000万行、40列)。分析师的笔记本只有16GB内存。
方案一(Pandas优化):usecols只读8列 + dtype指定category/float32 + chunksize=50万。内存从爆掉降到约1.5GB,但处理速度较慢(每个chunk聚合后合并)。
方案二(DuckDB,最终采用):直接用DuckDB的SQL查Parquet文件(先把CSV转成Parquet格式),GROUP BY+聚合一条SQL搞定,内存只用了400MB,3秒出结果。
教训:2026年了,处理超过1GB的数据不一定非得用Pandas。DuckDB和Polars在单机上的体验已经非常好了,很多场景比Pandas更快更省内存。
文件格式的选择——CSV vs Parquet
| 维度 | CSV | Parquet |
|---|---|---|
| 格式 | 文本(行式) | 二进制(列式) |
| 体积 | 大(无压缩) | 小(压缩后通常是CSV的1/5-1/10) |
| 列裁剪 | 不支持(必须扫描整行) | 支持(只读需要的列) |
| 类型信息 | 无(需要推断) | 有(存在文件元数据中) |
| 读取速度 | 慢(文本解析开销) | 快(二进制直接映射) |
| 适用场景 | 数据交换、人可读 | 分析处理、长期存储 |
建议:数据分析和ETL场景优先用Parquet格式。CSV只用于临时的数据交换(导出给Excel等)。同样的数据Parquet比CSV小5-10倍、读取快3-5倍、还支持列裁剪和谓词下推。
面试高频追问
追问一:Pandas的read_csv有哪些优化参数?
关键参数:① usecols——只读需要的列;② dtype——指定列类型减少内存;③ chunksize——分块读取;④ nrows——只读前N行(快速预览);⑤ parse_dates——指定日期列避免后续转换;⑥ na_values——指定缺失值标识;⑦ engine='pyarrow'——用PyArrow引擎读取(比默认的C引擎快2倍)。
追问二:Pandas 2.0有什么重要变化?
Pandas 2.0支持了PyArrow后端——df = pd.read_csv('data.csv', dtype_backend='pyarrow')。用Arrow格式存储数据而不是NumPy数组,内存更省(特别是字符串列)、支持更多原生类型(date/time/decimal)、和PySpark/Polars的Arrow生态互通。建议新项目默认开启PyArrow后端。
追问三:DuckDB和SQLite有什么区别?
两者都是嵌入式数据库,但定位不同。SQLite是事务型数据库(OLTP),擅长点查和小数据量CRUD。DuckDB是分析型数据库(OLAP),擅长大数据量的聚合查询,支持直接查Parquet/CSV文件。DuckDB的分析性能比SQLite快10-100倍。可以理解为'DuckDB是单机版的Doris'。
总结
大文件处理面试讲四种策略按优先级排序:① dtype优化(category/float32减少内存70%,最简单有效);② usecols只读需要的列;③ chunksize分块读取;④ 换引擎(DuckDB/Polars处理GB级数据比Pandas更高效)。文件格式优先Parquet而不是CSV。能讲出3.5GB CSV从MemoryError到DuckDB 3秒出结果的实际方案,面试官会觉得你有实际的大文件处理经验。
下一篇聊Python装饰器:Airflow的@dag装饰器是什么原理?装饰器到底是什么语法糖?
你们怎么处理超过内存的数据文件?用DuckDB或Polars了吗?评论区聊聊~
关注「Wood聊大数据」| 9年大数据老兵,只讲大数据面试干货