合并10万文本文件并补全信息时Pandas迭代过慢,求优化方案
优化10万份文本文件合并与补全的Pandas方案
核心性能瓶颈分析
你当前代码的最大问题是每次循环调用pd.concat拼接DataFrame——concat每次都会创建新的内存对象,10万次迭代会产生巨量的内存拷贝开销,这是速度慢的主要原因。以下是针对性的优化方案:
优化方法与实现
1. 用列表暂存小DataFrame,最后一次性合并
放弃每次循环拼接,先将所有处理好的小DataFrame存入列表,循环结束后再执行一次concat,彻底消除频繁内存拷贝的开销。
import os import pandas as pd df_list = [] # 初始化列表暂存所有小DataFrame resultsDir = "你的目标目录" for root, dirs, files in os.walk(resultsDir): for file in files: if not file.endswith('.txt'): continue resultsFile = os.path.relpath(os.path.join(root, file)) # 先判断文件是否存在且非空,避免无效IO if not os.path.isfile(resultsFile): print(f"文件 {resultsFile} 不存在") continue if os.path.getsize(resultsFile) == 0: # 直接创建带错误标记的空DataFrame empty_df = pd.DataFrame({'path': [resultsFile], 'resultsFile': [file], 'error': ["empty results file"]}) df_list.append(empty_df) continue # 读取文件并解析路径字段 df = pd.read_csv(resultsFile, delimiter='\t') df['path'] = resultsFile df['resultsFile'] = file pathLvls = resultsFile.split(os.sep) df['sdCard'] = pathLvls[-3][:5] df['site'] = pathLvls[-3][6:] df['capture'] = pathLvls[-4] df_list.append(df) # 最后一次性合并所有DataFrame df2 = pd.concat(df_list, ignore_index=True)
2. 多进程批量读取,加速IO操作
文件读取属于IO密集型任务,用多进程可以充分利用CPU资源,并行处理多个文件的读取与解析,大幅缩短总耗时。
import os import pandas as pd from concurrent.futures import ProcessPoolExecutor def process_file(file_info): resultsFile, file = file_info # 重复文件判断与处理逻辑 if not os.path.isfile(resultsFile): return pd.DataFrame({'path': [resultsFile], 'resultsFile': [file], 'error': ["file not found"]}) if os.path.getsize(resultsFile) == 0: return pd.DataFrame({'path': [resultsFile], 'resultsFile': [file], 'error': ["empty results file"]}) df = pd.read_csv(resultsFile, delimiter='\t') df['path'] = resultsFile df['resultsFile'] = file pathLvls = resultsFile.split(os.sep) df['sdCard'] = pathLvls[-3][:5] df['site'] = pathLvls[-3][6:] df['capture'] = pathLvls[-4] return df # 先收集所有需要处理的文件信息 file_list = [] resultsDir = "你的目标目录" for root, dirs, files in os.walk(resultsDir): for file in files: if file.endswith('.txt'): resultsFile = os.path.relpath(os.path.join(root, file)) file_list.append((resultsFile, file)) # 多进程处理,进程数建议设为CPU核心数 with ProcessPoolExecutor(max_workers=os.cpu_count()) as executor: df_list = list(executor.map(process_file, file_list)) # 合并结果 df2 = pd.concat(df_list, ignore_index=True)
3. 优化pd.read_csv参数,降低内存占用
通过指定数据类型、跳过无用列等方式减少DataFrame的内存占用,间接提升处理速度:
- 用
dtype参数指定列的类型(比如dtype={'col1': 'int32', 'col2': 'float32'}),避免Pandas自动推断时占用多余内存 - 用
usecols指定需要读取的列,跳过不需要的字段 - 若文件无表头,添加
header=None参数
示例:
df = pd.read_csv(resultsFile, delimiter='\t', dtype={'sdCard': 'str', 'site': 'str'}, usecols=['需要的列名1', '需要的列名2'])
4. 提前解析路径字段,减少重复计算
可以在收集文件路径时就提前计算好sdCard、site、capture字段,避免每个文件处理时重复执行split操作:
file_list = [] resultsDir = "你的目标目录" for root, dirs, files in os.walk(resultsDir): for file in files: if file.endswith('.txt'): resultsFile = os.path.relpath(os.path.join(root, file)) pathLvls = resultsFile.split(os.sep) sdCard = pathLvls[-3][:5] site = pathLvls[-3][6:] capture = pathLvls[-4] # 将路径解析结果一起存入文件列表 file_list.append((resultsFile, file, sdCard, site, capture))
之后在process_file函数中直接使用这些预计算的值,无需再次解析路径。
内容的提问来源于stack exchange,提问作者josh
相关产品推荐
相关产品推荐

