承接《Pandas 抽样与随机数据》,本篇讲解 Pandas 处理超大 CSV/Excel 等文件的方案:分块读取、分批迭代、只读取指定列、数据类型压缩、Dtype 优化、内存查看、chunksize、skiprows、低内存模式、写入分块、大文件避坑,适合几十MB~数GB级数据,保持系列教程统一风格。
前置导入
import pandas as pd
import numpy as np
一、问题背景
直接 pd.read_csv("big.csv") 一次性载入,文件超过内存就会:
- 内存爆满、程序被系统杀死
- 交换分区大量占用,速度极慢
核心思路:不要一次性全部读到内存,分批加载 + 减少加载内容 + 优化数据类型
二、chunksize 分块迭代读取(最核心)
chunksize 指定每一块读取多少行,返回迭代器,循环逐块处理,内存只保留当前块。
# 每次读取 10000 行
chunk_iter = pd.read_csv("big.csv", chunksize=10000)
for chunk in chunk_iter:
# 对每一块做清洗、过滤、统计、写入
res = chunk[chunk["sales"] > 1000]
# 追加写入结果
res.to_csv("filter_result.csv", mode="a", header=False, index=False)
注意:第一次写入需要
header=True,后续追加header=False,避免重复写表头。
封装表头判断版本:
first = True
for chunk in pd.read_csv("big.csv", chunksize=10000):
chunk_filter = chunk[chunk["amount"] > 0]
chunk_filter.to_csv("out.csv", mode="a", header=first, index=False)
first = False
三、只读取需要的列 usecols
不需要的列直接不加载,大幅减少内存占用
# 只加载 id、sales、date 三列
df = pd.read_csv("big.csv", usecols=["id", "sales", "date"])
也可以传索引列表:
df = pd.read_csv("big.csv", usecols=[0,2,5])
四、跳过行、限制读取行数
nrows:只读取前N行,用来快速预览、测试脚本skiprows:跳过前面若干行
# 只读取前5000行,调试用
df = pd.read_csv("big.csv", nrows=5000)
# 跳过前2行
df = pd.read_csv("big.csv", skiprows=2)
skiprows 也可以传函数,跳过指定行号。
五、dtype 类型压缩,降低内存(非常有效)
默认自动识别类型经常浪费内存:int64、float64 占空间很大。
在读取时直接指定 dtype,缩小存储空间:
dtype_map = {
"id": "int32",
"sales": "float32",
"region": "category"
}
df = pd.read_csv("big.csv", dtype=dtype_map)
category:适合基数少、重复多的字符串(地区、分类、标签),内存压缩效果极强- 数字优先用 int8 / int16 / int32、float32,在取值范围允许下使用
⚠️ 注意:数值超出范围会报错,要先确认值域。
六、parse_dates 按需解析时间
时间解析开销很大,不需要就不要开启;需要则指定列:
df = pd.read_csv("big.csv", parse_dates=["order_time"])
如果格式固定,搭配 format 加速解析:
df = pd.read_csv("big.csv", parse_dates=["order_time"], format="%Y-%m-%d %H:%M:%S")
七、low_memory 参数警告
经常遇到警告:DtypeWarning: Columns have mixed types.
原因:同一列前面行是数字,后面行是字符串,Pandas自动推断类型混乱。
两种方案:
- 提前指定 dtype(推荐)
low_memory=False关闭分块推断(治标不治本)
df = pd.read_csv("big.csv", low_memory=False)
八、查看内存占用 memory_usage
# 每一列内存,单位字节
df.memory_usage(deep=True)
# 汇总
print(df.memory_usage(deep=True).sum() / 1024 / 1024, "MB")
deep=True 对 object、category 字符串列统计更准确。
九、大文件写入:分块写入
不要把所有结果攒到内存再写,边处理边输出。
前面 chunksize 示例就是标准写法。
如果是输出 Excel:xlsx 不适合超大文件;大输出优先 csv / parquet。
重点:Excel 本身不适合千万行级别数据,尽量避免。
十、Parquet 格式替代 CSV(高性能二进制格式)
CSV 是文本,读写慢、占内存;Parquet 列式存储,自带压缩、类型保存,是大数据首选。
# 写入
df.to_parquet("data.parquet", compression="snappy")
# 读取,支持指定列
df = pd.read_parquet("data.parquet", columns=["id","sales"])
Parquet 天然支持:
- 按列读取、分区读取
- 压缩、类型持久化
- 比csv快很多,体积小很多
Feather 也是类似列式格式,适合 Pandas 内部交换。
十一、大Excel文件处理
read_excel 底层 openpyxl / xlrd 性能弱,不适合超大Excel。
- 小几万行:正常 read_excel
- 几十万行以上:优先导出为csv
- 必须读大xlsx:使用
openpyxl+ 迭代,或者chunk(pandas excel chunk支持有限)
# 不推荐超大数据用excel
df = pd.read_excel("big.xlsx", sheet_name="sales")
十二、进阶:Dask(替代Pandas,超大数据)
当文件大到单块都难以处理,直接换 Dask DataFrame,API 和 Pandas 几乎一致,自动分块并行:
import dask.dataframe as dd
dask_df = dd.read_csv("big.csv", dtype=dtype_map)
res = dask_df[dask_df["sales"]>1000].compute()
compute() 才会触发计算,把结果拉回Pandas。
十三、常用场景模板
模板1:分块统计汇总(求和、计数)
total_sales = 0
cnt = 0
for chunk in pd.read_csv("big.csv", chunksize=10000):
total_sales += chunk["sales"].sum()
cnt += len(chunk)
print("总销售额:", total_sales)
print("总行数:", cnt)
模板2:分块过滤输出新文件
first_flag = True
for chunk in pd.read_csv("big.csv", chunksize=20000, usecols=["id","sales","region"]):
out = chunk[chunk["sales"] >= 500]
out.to_csv("filter_out.csv", mode="a", header=first_flag, index=False)
first_flag = False
十四、高频坑清单
- chunksize 不是越小越好,太小IO频繁,速度变慢;一般 1w~10w 行,根据内存调试
- 分块循环里用
pd.concat不断追加到列表外面,越攒越大,失去分块意义 - category 用于基数很大的字段(比如id、订单号),反而更耗内存
- 读取时不指定dtype,默认int64/float64,内存翻倍
- 表头重复:追加写入忘记控制header,结果文件多行表头
- 混合类型列不处理,low_memory警告,数据读错
- 超大Excel幻想用pandas直接读取,极易卡死
- 分块join/merge:跨块关联很难,分块适合单文件过滤、统计,关联尽量用Parquet+Dask
十五、速查表
| 需求 | 方案 |
|---|---|
| 文件太大内存不够 | read_csv(chunksize=N) 迭代 |
| 只需要部分字段 | usecols 指定列 |
| 减少内存占用 | dtype + category |
| 快速预览测试 | nrows |
| 高性能存储格式 | parquet(snappy压缩) |
| 千万行级Excel | 优先转csv,不推荐pandas直接读 |
| 超大数据、并行 | Dask DataFrame |
| 查看内存消耗 | memory_usage(deep=True) |
0 条笔记