Python Pandas处理千万级数据帧的内存优化技巧
处理千万级数据时,Pandas 经常会爆出 MemoryError,或者导致电脑卡死。这是因为 Pandas 默认加载方式非常“铺张”。通过优化数据类型和加载策略,通常能将内存占用降低 50% 甚至 90% 以下。
第一阶段:诊断内存占用情况
运行 以下代码查看数据帧的详细内存使用情况。
import pandas as pd
# 假设 df 是你的数据帧
df.info(memory_usage='deep')
重点关注输出信息中的 memory usage 列以及每一列的 Dtype(数据类型)。如果很多列显示为 object 或 int64/float64,这就是内存被浪费的根源。
查看 各列具体的内存占用统计,找出占用最大的几列。
df.memory_usage(deep=True).sort_values(ascending=False)
第二阶段:优化数值类型
Pandas 加载整数时默认使用 int64(64位),浮点数默认使用 float64。对于大多数业务数据,这完全没必要。
自动降级 数值列的最小类型。
# 处理整数列
df_int = df.select_dtypes(include=['int64'])
converted_int = df_int.apply(pd.to_numeric, downcast='integer')
# 处理浮点数列
df_float = df.select_dtypes(include=['float64'])
converted_float = df_float.apply(pd.to_numeric, downcast='float')
更新 原始数据帧。
df[converted_int.columns] = converted_int
df[converted_float.columns] = converted_float
执行 完上述步骤后,数值列通常会从 8 字节(int64/float64)降至 1 到 4 字节(int8 到 float32),直接节省约 75% 的内存空间。
第三阶段:优化字符串与分类数据
字符串类型(object)是内存杀手。如果某列表示的是“性别”、“省份”、“产品ID”等重复度高的数据,将其转换为 category 类型是最高效的手段。
判断 某一列是否适合转换。经验法则是:当唯一值的数量除以总行数小于 0.5(即重复率高)时,转换效果显著。
检查 唯一值占比。
# 检查 'city' 列的唯一值比例
ratio = len(df['city'].unique()) / len(df)
print(ratio)
转换 低基数字符串列为 category 类型。
df['city'] = df['city'].astype('category')
对于纯文本内容(如用户评论、描述)无法转换为 category,保持 为 object 类型,但确保在后续处理中尽早丢弃不需要的文本列。
第四阶段:优化日期时间类型
日期通常以字符串(object)形式读入,既占内存又无法进行时间运算。
转换 日期字符串为 datetime 类型。
df['date_column'] = pd.to_datetime(df['date_column'])
datetime64[ns] 通常比存储日期的字符串更节省空间,且支持快速的时间序列操作。
第五阶段:读取时即优化
最好的优化是在数据进入内存之前就完成。与其读入后再转换,不如在 read_csv 时直接指定类型。
创建 一个字典,映射列名与目标类型。
dtype_dict = {
'id': 'int32',
'age': 'int8',
'price': 'float32',
'gender': 'category',
'city': 'category'
}
使用 dtype 参数读取 CSV。
df = pd.read_csv('large_file.csv', dtype=dtype_dict)
指定 parse_dates 参数直接解析日期。
df = pd.read_csv('large_file.csv', dtype=dtype_dict, parse_dates=['date_column'])
第六阶段:分块处理超大文件
如果优化后仍然内存不足,使用 分块读取策略。不要一次性加载整个文件。
设置 chunksize 参数进行迭代读取。
chunk_size = 100000 # 每次读取 10 万行
chunk_iterator = pd.read_csv('huge_file.csv', chunksize=chunk_size)
for chunk in chunk_iterator:
# 在这里对每一个 chunk 进行处理
# 例如:过滤、聚合、清洗或保存到数据库
process(chunk)
这种方式将内存占用维持在 chunk_size 对应的大小,无论文件总行数是多少,内存始终是稳定的。
第七阶段:及时释放无用内存
在数据清洗过程中,经常会产生中间变量。
使用 del 语句删除不再需要的大变量。
temp_df = pd.DataFrame(...)
# 处理完毕
del temp_df
手动调用 垃圾回收。
import gc
gc.collect()
常见数据类型内存占用对比表
了解不同类型的底层成本,有助于做出正确选择。
| 数据类型 | 说明 | 每个元素占用字节数 | 适用场景 |
|---|---|---|---|
int64 |
默认整数 | 8 | 极大数值(超出 20 亿) |
int32 |
中等整数 | 4 | 一般 ID、计数(约 ±21 亿内) |
int16 |
短整数 | 2 | 年龄、年份、评分(-32768 到 32767) |
int8 |
极短整数 | 1 | 标志位(0-255)、状态码 |
float64 |
默认浮点数 | 8 | 高精度科学计算 |
float32 |
单精度浮点数 | 4 | 一般金额、经纬度、百分比 |
object |
字符串/混合类型 | 变大 (通常 >50) | 纯文本、JSON |
category |
分类数据 | 变小 (取决于基数) | 重复的字符串(如性别、国家) |
datetime64[ns] |
时间戳 | 8 | 时间序列分析 |
记住:将 int64 改为 int8 能节省 87.5% 的该列内存;将字符串改为 category 在低基数情况下通常能节省 90% 以上。

暂无评论,快来抢沙发吧!