16GB内存下读取8GB大CSV至Pandas DataFrame遇内存错误求解
解决大CSV文件读取为Pandas DataFrame的内存问题
核心原因分析
直接用pd.concat合并分块数据仍触发内存错误,本质是所有分块最终会被同时加载到内存中,而Pandas DataFrame在内存中的占用量通常是原始CSV的2-5倍(尤其是字符串列),8GB CSV对应的内存需求可能超过16GB可用空间。NameError则是因为代码中的process是占位函数,需要自行实现处理逻辑。
解决方案步骤
1. 优先优化数据类型,大幅降低内存占用
这是最有效的前置操作,通过压缩数据类型减少单块数据的内存消耗:
- 对字符串列:如果列的唯一值占比低(比如小于20%),转为
category类型; - 对数值列:用更小的整数/浮点数类型替代默认的
int64/float64; - 仅读取需要的列:通过
usecols参数过滤无关列。
示例代码:
import pandas as pd import gc # 先读取少量样本探查列属性 sample_df = pd.read_csv(r'C:\folder\test.csv', sep=";", encoding="utf-8", decimal=",", nrows=1000) # 优化数值列类型 def optimize_numeric_dtypes(df): for col in df.select_dtypes(include=['int64', 'float64']).columns: # 根据数值范围选择最小可用类型 if df[col].dtype == 'int64': if df[col].min() >= 0 and df[col].max() <= 255: df[col] = df[col].astype('uint8') elif df[col].min() >= -128 and df[col].max() <= 127: df[col] = df[col].astype('int8') elif df[col].min() >= -32768 and df[col].max() <= 32767: df[col] = df[col].astype('int16') elif df[col].dtype == 'float64': # 若无高精度需求,转float32 df[col] = df[col].astype('float32') return df # 优化字符串列(基数低转category) for col in sample_df.select_dtypes(include=['object']).columns: unique_ratio = sample_df[col].nunique() / len(sample_df) if unique_ratio < 0.2: sample_df[col] = sample_df[col].astype('category') # 获取优化后的 dtype 映射 optimized_dtypes = sample_df.dtypes.to_dict()
2. 分块读取+高效格式暂存,避免一次性加载
将分块数据保存为内存效率更高的feather或parquet格式(比pkl更节省空间且读取更快),再逐步合并:
chunk_size = 500000 # 根据内存情况调整,建议比之前更小 chunk_files = [] # 分块读取并保存 for idx, chunk in enumerate(pd.read_csv( r'C:\folder\test.csv', sep=";", encoding="utf-8", decimal=",", dtype=optimized_dtypes, chunksize=chunk_size, usecols=list(optimized_dtypes.keys()) # 仅读取需要的列 )): # 再次优化当前分块的类型 chunk = optimize_numeric_dtypes(chunk) # 保存为feather文件 chunk_path = f'C:\\folder\\chunk_{idx}.feather' chunk.to_feather(chunk_path) chunk_files.append(chunk_path) # 及时释放内存 del chunk gc.collect() # 逐步合并feather文件,每次只加载一块到内存 df_list = [] for file in chunk_files: df_chunk = pd.read_feather(file) df_list.append(df_chunk) del df_chunk gc.collect() # 最终合并为完整DataFrame dfcompl = pd.concat(df_list, ignore_index=True)
3. 解决process未定义的问题
原代码中的process(chunk)是示例占位函数,若你需要在分块时做数据清洗/转换,可自行实现该函数,例如:
def process(chunk): # 示例:删除空值列、转换日期格式 chunk = chunk.dropna(subset=['Column1']) chunk['DateColumn'] = pd.to_datetime(chunk['DateColumn'], format='%Y-%m-%d') return chunk
额外提示
- 若合并后仍内存不足,可考虑按需处理:即不合并成完整DataFrame,而是基于分块文件进行逐块计算,最后汇总结果;
- 关闭Pandas的
low_memory参数(low_memory=False)可避免部分类型推断导致的内存波动,但需确保dtype已手动指定。
内容的提问来源于stack exchange,提问作者PSt
相关产品推荐
相关产品推荐

