使用Pandas为54个大CSV文件新增列的实现方案问询
问题:海量CSV数据新增列的实现是否适用及优化方案?
我拥有54个CSV文件,每个文件包含300万行数据。需为每个CSV文件新增列,当前实现代码如下:
local_list = pd.DataFrame(columns=["ORI_LOCAL_NAME","DES_LOCAL_NAME"]) for i in range(0,df.__len__()): row = df.iloc[i,:] ori_name = row['ORI_TOLL_NAME'] des_name = row['DES_TOLL_NAME'] ori_local_name = toll_info[ori_name] des_local_name = toll_info[des_name] local_list = local_list.append({"ORI_LOCAL_NAME": ori_local_name, "DES_LOCAL_NAME": des_local_name}, ignore_index=True)请问该实现是否适用于海量数据场景,有无优化方案?
回答:
兄弟,你这个实现完全不适合海量数据场景,甚至可以说是Pandas处理大数据的典型反面案例——我给你拆解问题和优化方案:
为什么当前代码不行?
- 逐行循环处理300万行:Pandas的设计核心是向量化操作,逐行遍历
iloc的速度慢到离谱,300万行可能要跑几十分钟甚至更久。 - 循环里用
append():每次append都会创建一个新的DataFrame对象,旧对象还要等待GC回收,内存占用会随着循环次数爆炸式增长,处理到几十万行时大概率会触发内存溢出。
优化方案(按优先级排序)
1. 用向量化映射替代逐行循环
toll_info看起来是个字典(或者可以转成字典),直接用map()方法批量处理整个列,速度能提升几个数量级:
# 直接在原DataFrame上生成新列,完全不需要创建额外的local_list df['ORI_LOCAL_NAME'] = df['ORI_TOLL_NAME'].map(toll_info) df['DES_LOCAL_NAME'] = df['DES_TOLL_NAME'].map(toll_info)
这两行代码就能搞定300万行数据,本质是让Pandas在底层用C语言批量处理,比Python循环快几十倍。
2. 分块读取处理单个CSV文件
每个CSV有300万行,54个文件总数据量超过16亿行,一次性加载到内存基本不可能,必须分块处理:
import pandas as pd def process_single_csv(file_path, toll_info): processed_chunks = [] # chunksize根据你的内存情况调整,比如10万行一块,内存压力小就调大 for chunk in pd.read_csv(file_path, chunksize=100000): # 对每个块做向量化映射 chunk['ORI_LOCAL_NAME'] = chunk['ORI_TOLL_NAME'].map(toll_info) chunk['DES_LOCAL_NAME'] = chunk['DES_TOLL_NAME'].map(toll_info) processed_chunks.append(chunk) # 合并所有块并返回 return pd.concat(processed_chunks, ignore_index=True) # 遍历所有CSV文件处理 for csv_path in your_csv_file_list: # 替换成你的54个文件路径列表 result_df = process_single_csv(csv_path, toll_info) # 保存处理后的文件,建议存新文件避免覆盖原数据 result_df.to_csv(f"processed_{csv_path}", index=False)
分块处理能把内存占用控制在单个块的大小,避免内存溢出,而且每个块的处理依然是向量化的,速度不会慢。
3. 额外的内存/速度优化点
- 如果
toll_info是从其他表格来的,转成pd.Series再map,速度会比字典更快:toll_series = pd.Series(toll_info) df['ORI_LOCAL_NAME'] = df['ORI_TOLL_NAME'].map(toll_series) - 读取CSV时指定
dtype参数,减少内存占用:比如已知ORI_TOLL_NAME是字符串,可以指定dtype={'ORI_TOLL_NAME': 'string'},避免Pandas自动推断成object类型浪费内存。 - 用
usecols参数只读取需要的列:如果原CSV里有很多不需要的列,读取时只加载ORI_TOLL_NAME、DES_TOLL_NAME以及其他需要保留的列,进一步降低内存压力。
内容的提问来源于stack exchange,提问作者Jungseok Cho
相关产品推荐
相关产品推荐

