拼接百万级CSV生成Pandas DataFrame遇内存错误,求优化方案
解决方案:处理大CSV拼接的内存问题
嘿,我来帮你搞定这个内存溢出的问题!首先先指出你现有代码里的两个关键小问题,再给几个针对大数据拼接的优化方案——毕竟10个百万行级别的CSV,确实容易吃满内存~
先修正代码里的基础错误
你当前的代码有两个明显问题,先搞定它们:
- 循环里
pd.read_csv('file')用了字符串'file',应该是变量file,不然每次都在读一个名叫"file"的文件(甚至会直接报错); - 你现在的循环每次都把
df覆盖成当前文件的DataFrame,最后pd.concat(df,...)其实只拼接了最后一个文件,根本没把所有文件合起来!正确的做法是把每个df存到列表里再拼接(不过这只是语法修正,内存问题还要靠后面的方案)。
修正后的基础版代码(但还是可能内存不够):
import pandas as pd df_list = [] for file in filenames: df = pd.read_csv(file, sep=',', encoding='utf-8', dtype='object') df_list.append(df) print(f"已加载 {file},行数:{len(df)}") concatenated_df = pd.concat(df_list, ignore_index=True)
针对内存问题的优化方案
上面的基础版会把所有10个DataFrame全加载到内存,所以会触发MemoryError。结合你32GB内存的配置,这些方案更实用:
1. 分块读取+逐块拼接
不用一次性加载整个文件,用chunksize参数分批次读取,每读一块就拼接到结果里,大幅降低内存峰值:
import pandas as pd # 初始化空的结果DataFrame concatenated_df = pd.DataFrame() for file in filenames: # 分块读取,比如每次读10万行(可根据内存情况调整) chunk_iter = pd.read_csv(file, sep=',', encoding='utf-8', dtype='object', chunksize=100000) for chunk in chunk_iter: concatenated_df = pd.concat([concatenated_df, chunk], ignore_index=True) print(f"已添加 {file} 的数据块,当前总行数:{len(concatenated_df)}")
这种方式每次内存里只保留一小块数据,能有效避免内存溢出。
2. 只读取必要的列
如果业务不需要CSV里的所有列,一定要用usecols参数指定只加载需要的列,直接砍掉不必要的内存占用:
# 假设你只需要col1、col3、col5这三列 needed_cols = ['col1', 'col3', 'col5'] concatenated_df = pd.DataFrame() for file in filenames: chunk_iter = pd.read_csv( file, sep=',', encoding='utf-8', usecols=needed_cols, dtype='object', chunksize=100000 ) for chunk in chunk_iter: concatenated_df = pd.concat([concatenated_df, chunk], ignore_index=True)
3. 优化数据类型(关键!)
你现在用dtype='object'会让所有列都存成Python对象,这是非常占内存的。针对不同列指定更高效的数据类型,能把内存占用降到原来的1/5甚至更低:
- 字符串列:重复值多就用
category类型;重复少但格式固定用string类型(pandas 1.0+支持); - 数值列:用最小能容纳范围的类型,比如
int32代替int64,float32代替float64; - 日期列:用
parse_dates参数直接解析成datetime64类型,比存字符串省内存。
示例代码:
# 自定义数据类型映射 dtype_map = { 'id': 'int32', 'category_col': 'category', 'value': 'float32', 'name': 'string' } concatenated_df = pd.DataFrame() for file in filenames: chunk_iter = pd.read_csv( file, sep=',', encoding='utf-8', dtype=dtype_map, parse_dates=['date_col'], # 自动解析日期列 chunksize=100000 ) for chunk in chunk_iter: concatenated_df = pd.concat([concatenated_df, chunk], ignore_index=True)
4. 使用Dask DataFrame(终极方案)
如果上面的方法还是不够,试试Dask——它专门处理超出内存的大数据,用法和pandas几乎一致,但会延迟计算,不用把所有数据加载到内存:
import dask.dataframe as dd # 用Dask读取所有CSV ddf = dd.read_csv(filenames, sep=',', encoding='utf-8', dtype='object') # 执行拼接(Dask会延迟计算,直到调用compute()) concatenated_df = ddf.compute() # 如果内存还是紧张,可以不用转成pandas,直接用Dask处理数据
Dask会自动分块处理,32GB内存应付10个百万行CSV完全没问题。
内容的提问来源于stack exchange,提问作者Sun
相关产品推荐
相关产品推荐

