讲师中心 微信公众号
AI工具推荐 视频效率加速

Pandas批量文件处理性能优化:避免循环内concat与并发实践

梦杰姑娘_7514

梦杰姑娘_7514

发布时间:2025-11-18 13:09:01

|

784人浏览过

|

来源于php中文网

原创

Pandas批量文件处理性能优化:避免循环内concat与并发实践

本文深入探讨了在pandas中高效处理和合并大量csv文件的方法。针对循环内部频繁使用`pd.concat`导致的性能瓶颈,文章提出了将数据收集到字典中并在循环结束后进行一次性合并的优化策略。此外,结合`pathlib`进行路径管理和利用多线程实现并发处理,进一步提升了数据处理效率和内存利用率,为大规模数据整合提供了专业的解决方案。

引言:循环内concat的性能陷阱

在数据处理工作中,我们经常需要从多个文件中读取数据并将其合并到一个大型Pandas DataFrame中。一个常见的直觉做法是在循环中逐个读取文件,然后使用pd.concat将每个文件的数据追加到主DataFrame。然而,当文件数量庞大(例如上千个)且每个文件的数据量不小(例如15MB,超过10,000行)时,这种做法会导致严重的性能问题。初始的几次循环可能很快完成,但随着主DataFrame的不断增大,每次concat操作所需的时间会呈指数级增长,最终使得整个过程变得异常缓慢,甚至可能耗尽系统内存。

原始问题与低效实践分析

考虑以下场景:我们有一个包含文件路径信息的DataFrame df,需要遍历其中的每一行,读取对应的CSV文件,对其进行转置和格式化,最终合并到一个名为 merged_data 的大型DataFrame中。

原始代码示例

import pandas as pd
import os

# 假设 root_path 和 df 已经定义
# root_path = '/path/to/your/root'
# df = pd.DataFrame({'File ID': ['folderA', 'folderB'], 'File Name': ['file001.txt', 'file002.txt']})

merged_data = pd.DataFrame()
count = 0
for index, row in df.iterrows():
    folder_name = row['File ID'].strip()
    file_name = row['File Name'].strip()
    file_path = os.path.join(root_path, folder_name, file_name)

    # 读取、转置并格式化文件数据
    file_data = pd.read_csv(file_path, names=['Case', f'{folder_name}_{file_name}'], sep='\t')
    file_data_transposed = file_data.set_index('Case').T.reset_index(drop=True)
    file_data_transposed.insert(loc=0, column='folder_file_id', value=str(folder_name+'_'+file_name))

    # 每次循环都进行 concat
    merged_data = pd.concat([merged_data, file_data_transposed], axis=0, ignore_index=True)
    count = count + 1
    print(count)

性能瓶颈分析

上述代码的性能问题主要源于以下几点:

  1. 循环内频繁 pd.concat: pd.concat操作在每次执行时,会创建一个新的DataFrame,并将现有数据和新数据复制到其中。随着merged_data的增大,每次复制的数据量也随之增加,导致内存分配和数据复制的开销急剧上升。这是导致性能呈指数级下降的主要原因。
  2. df.iterrows(): 虽然在小规模数据上不是主要瓶颈,但对于大型DataFrame,iterrows()会返回一个迭代器,每次迭代生成一个Series,这比向量化操作效率低。
  3. os.path.join: 虽然功能正确,但os.path模块在处理路径时不如pathlib模块面向对象且简洁。

优化方案一:延迟合并与Pathlib

解决循环内concat性能问题的核心思想是:避免在循环中重复执行昂贵的操作,而是将所有中间结果收集起来,在循环结束后一次性执行合并。

核心思想

我们将不再每次迭代都将数据追加到merged_data,而是将每个文件处理后的结果(通常是一个Pandas Series或DataFrame片段)存储在一个Python字典中。字典的键可以是文件的唯一标识符,值则是处理后的数据。循环结束后,我们再将这个字典传递给pd.concat,进行一次性高效合并。

优化代码实现

import pathlib
import pandas as pd

# 假设 root_path 和 df 已经定义
root_path = pathlib.Path('root') # 使用 pathlib 代替 os.path
df = pd.DataFrame({
    'File ID': ['folderA', 'folderB'], 
    'File Name': ['file001.txt', 'file002.txt']
})

data = {} # 用于收集所有处理后的数据
# 使用 enumerate 而非外部计数器,并直接迭代 df.iterrows()
for count, (_, row) in enumerate(df.iterrows(), 1):
    folder_name = row['File ID'].strip()
    file_name = row['File Name'].strip()

    # 使用 pathlib 构建文件路径,更简洁安全
    file_path = root_path / folder_name / file_name
    folder_file_id = f'{folder_name}_{file_name}'

    # 读取CSV文件,指定 header=None 因为文件没有表头
    # memory_map=True 可以提高大文件读取效率
    # low_memory=False 确保正确推断所有列的数据类型
    file_data = pd.read_csv(file_path, header=None, sep='\t',
                            names=['Case', folder_file_id],
                            memory_map=True, low_memory=False)

    # 设置 'Case' 列为索引,并使用 squeeze() 将单列DataFrame转换为Series
    data[folder_file_id] = file_data.set_index('Case').squeeze()
    print(count)

# 循环结束后,一次性进行 concat
merged_data = (pd.concat(data, names=['folder_file_id'])
                 .unstack('Case').reset_index())

示例输入数据:

python全能编程助手
python全能编程助手

SkillSub Pro - Python 题解与代码注释双功能技能功能概述SkillSub Pro - Python 题解与代码注释双功能技能是一项面向实际任务的技能,主要用于SkillSub Pro 是一个 Python 题解生成与代码注释的 双功能合体技能 ,专为学生、算法学习者和开发者设计;✅ 一个技能,两种用途 :;核心要点📝 题解模式 :输入题目/题号,自动生成完整 Python 题解(含详细注释、解题思路、复杂度分析);💬 注释模式 :输入 Python 代码,自动添加详细中。它将相关步骤、

下载
# df
   File ID    File Name
0  folderA  file001.txt
1  folderB  file002.txt

# root/folderA/file001.txt
0   1234
1   5678
2   9012
3   3456
4   7890

# root/folderB/file002.txt
0   4567
1   8901
2   2345
3   6789

示例输出:

>>> merged_data
Case       folder_file_id       0       1       2       3       4
0     folderA_file001.txt  1234.0  5678.0  9012.0  3456.0  7890.0
1     folderB_file002.txt  4567.0  8901.0  2345.0  6789.0     NaN

代码解析与优势

  1. pathlib 模块: pathlib.Path对象提供了更直观、面向对象的方式来处理文件系统路径。例如,使用/运算符即可拼接路径,替代了os.path.join,代码更具可读性。
  2. 数据收集到字典 data: 这是核心优化点。每次循环将处理后的数据(一个Series)存储到字典中,避免了频繁的内存重分配和数据复制。
  3. pd.read_csv 参数优化:
    • header=None: 明确指定文件没有表头,避免Pandas误将第一行数据作为表头。
    • names=['Case', folder_file_id]: 直接为列指定名称。
    • memory_map=True: 尝试将文件映射到内存,对于大文件可能提高读取效率。
    • low_memory=False: 告诉Pandas在读取整个文件后才推断数据类型,这会消耗更多内存,但能避免混合类型列的问题,尤其在有大量列时非常有用。
  4. set_index('Case').squeeze(): set_index('Case')将'Case'列设为索引。.squeeze()方法用于移除单维度的条目,例如将一个只有一列的DataFrame转换为一个Series,这对于后续的concat和unstack操作非常方便。
  5. 一次性 pd.concat: 循环结束后,将包含所有Series的字典data传递给pd.concat。Pandas会高效地将这些Series合并成一个DataFrame。
    • names=['folder_file_id']: 为新生成的层级索引指定名称。
  6. unstack('Case').reset_index():
    • pd.concat(data, names=['folder_file_id']) 会生成一个多层索引的Series,其中第一层索引是folder_file_id,第二层是Case。
    • .unstack('Case') 会将Case索引级别转换为列,实现数据的“宽”格式转换,使其符合原始需求中的转置效果。
    • .reset_index() 将多层索引转换为普通列,并重置数字索引,得到最终的扁平化DataFrame。

优化方案二:利用多线程并发处理

对于I/O密集型任务(如读取大量文件),即使优化了concat,文件读取本身仍可能成为瓶颈。在这种情况下,可以考虑使用多线程(或多进程)来并行化文件读取过程。Python的concurrent.futures模块提供了方便的接口来实现这一点。

并发处理的原理

多线程在Python中受GIL(全局解释器锁)的限制,对于CPU密集型任务效果不佳。但对于I/O密集型任务(如文件读写、网络请求),当一个线程等待I/O操作完成时,GIL会被释放,允许其他线程执行Python代码。因此,多线程可以显著提高I/O密集型任务的整体吞吐量。

多线程代码实现

from concurrent.futures import ThreadPoolExecutor
import pathlib
import pandas as pd

root_path = pathlib.Path('root')
df = pd.DataFrame({
    'File ID': ['folderA', 'folderB'], 
    'File Name': ['file001.txt', 'file002.txt']
})

def read_and_process_csv(args):
    """
    一个辅助函数,用于在单独的线程中读取和处理单个CSV文件。
    """
    count, row_dict = args # 解包参数,row_dict 是 df.to_dict('records') 的一行
    folder_name = row_dict['File ID'].strip()
    file_name = row_dict['File Name'].strip()
    file_path = root_path / folder_name / file_name
    folder_file_id = f'{folder_name}_{file_name}'

    file_data = pd.read_csv(file_path, header=None, sep='\t',
                            names=['Case', folder_file_id],
                            memory_map=True, low_memory=False)
    print(f"Processing {count}: {folder_file_id}")
    return folder_file_id, file_data.set_index('Case').squeeze()

# 创建一个线程池,max_workers 根据CPU核心数和I/O负载调整
with ThreadPoolExecutor(max_workers=4) as executor: # 示例使用4个工作线程
    # 将 DataFrame 转换为字典列表,以便传递给线程池
    # enumerate 用于添加计数器
    batch_args = enumerate(df[['File ID', 'File Name']].to_dict('records'), 1)

    # 使用 executor.map 并行执行 read_and_process_csv 函数
    # data 将是一个迭代器,按提交顺序返回结果
    data_iterator = executor.map(read_and_process_csv, batch_args)

    # 将迭代器转换为字典,以便 pd.concat 处理
    data_dict = dict(data_iterator)

# 循环结束后,一次性进行 concat
merged_data = (pd.concat(data_dict, names=['folder_file_id'])
                 .unstack('Case').reset_index())

适用场景与注意事项

  1. ThreadPoolExecutor: 创建一个线程池,max_workers参数控制同时运行的最大线程数。对于I/O密集型任务,通常可以设置得比CPU核心数高一些。
  2. read_and_process_csv 函数: 这个函数封装了单个文件的读取和处理逻辑,它将作为线程池的任务被执行。
  3. df.to_dict('records'): 将DataFrame转换为一个字典列表,每个字典代表一行数据。这样可以方便地将行数据作为参数传递给线程函数。
  4. executor.map(): 这是ThreadPoolExecutor提供的一个高阶函数,它将一个函数应用到可迭代对象的每个元素上,并返回一个迭代器,其中包含函数调用的结果。它会按照提交的顺序返回结果,这对于后续的pd.concat非常重要。
  5. I/O密集型任务: 多线程特别适合文件读取这种I/O密集型任务。如果任务是CPU密集型的(例如大量的数值计算),则应考虑使用ProcessPoolExecutor(多进程)来规避GIL的限制。
  6. 错误处理: 在生产环境中,需要为并发任务添加适当的错误处理机制,例如使用try-except块捕获文件读取或处理中的异常。
  7. 内存管理: 尽管并发读取可以加快速度,但如果每个文件都很大,所有文件的数据最终仍会加载到内存中。因此,需要确保系统有足够的内存来容纳所有合并后的数据。

总结与最佳实践

在Pandas中高效处理和合并大量文件是数据工程中的常见挑战。本文提供了两种关键的优化策略:

  1. 延迟合并: 永远不要在循环内部频繁地使用pd.concat。相反,将每个迭代产生的数据片段收集到一个列表或字典中,然后在循环结束后执行一次性的大规模合并。这能显著减少内存分配和数据复制的开销。
  2. 利用pathlib进行路径管理: pathlib模块提供了更现代、更直观、更安全的路径操作方式,推荐替代os.path。
  3. 并发处理: 对于I/O密集型任务,可以利用concurrent.futures.ThreadPoolExecutor实现多线程并发读取和初步处理,进一步缩短整体执行时间。

通过采纳这些最佳实践,您可以有效地处理数千甚至数万个文件,将原本耗时数小时甚至数天的任务缩短到可接受的时间范围内,极大地提升数据处理效率。在实际应用中,根据具体的数据量、文件大小和系统资源,可以灵活选择并组合这些优化方法。

相关文章

数码产品性能查询
数码产品性能查询

该软件包括了市面上所有手机CPU,手机跑分情况,电脑CPU,电脑产品信息等等,方便需要大家查阅数码产品最新情况,了解产品特性,能够进行对比选择最具性价比的商品。

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

Seko
Seko Hot

一款AI视频创作工具,主要用于商汤科技推出的创编一体的AI短视频创作Agent,适合需要提升相关任务效率的用户。

立刻MV
立刻MV Hot

立刻MV是一款AI文本写作工具,AI 音乐视频(MV)创作工具。

讯飞绘文

讯飞绘文是一款由科大讯飞推出的一站式 AIGC 内容运营平台。

二狗PPT
二狗PPT Hot

一款AI演示文稿工具,主要用于专为中式职场打造的AI PPT生成工具,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

Atoms
Atoms Hot

Atoms是一款AI智能体工具,第一支自动构建真实业务的 AI 团队。

相关专题

更多
Python 时间序列分析与预测
Python 时间序列分析与预测

本专题专注讲解 Python 在时间序列数据处理与预测建模中的实战技巧,涵盖时间索引处理、周期性与趋势分解、平稳性检测、ARIMA/SARIMA 模型构建、预测误差评估,以及基于实际业务场景的时间序列项目实操,帮助学习者掌握从数据预处理到模型预测的完整时序分析能力。

280

2025.12.04

Python 数据清洗与预处理实战
Python 数据清洗与预处理实战

本专题系统讲解 Python 在数据清洗与预处理中的核心技术,包括使用 Pandas 进行缺失值处理、异常值检测、数据格式化、特征工程与数据转换,结合 NumPy 高效处理大规模数据。通过实战案例,帮助学习者掌握 如何处理混乱、不完整数据,为后续数据分析与机器学习模型训练打下坚实基础。

212

2026.01.31

Python数据分析与Pandas高级实战
Python数据分析与Pandas高级实战

本专题围绕 Python 数据分析展开,系统讲解 Pandas 的高级用法,包括数据清洗、透视表、时间序列分析以及多表合并与分组操作。通过实战案例,帮助开发者掌握高效处理与分析数据的方法,提高数据处理效率与分析能力。

271

2026.04.13

数据类型有哪几种
数据类型有哪几种

数据类型有整型、浮点型、字符型、字符串型、布尔型、数组、结构体和枚举等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2331

2023.10.31

php数据类型
php数据类型

本专题整合了php数据类型相关内容,阅读专题下面的文章了解更多详细内容。

494

2025.10.31

c语言 数据类型
c语言 数据类型

本专题整合了c语言数据类型相关内容,阅读专题下面的文章了解更多详细内容。

402

2026.02.12

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

5704

2023.10.24

Go语言中的运算符有哪些
Go语言中的运算符有哪些

Go语言中的运算符有:1、加法运算符;2、减法运算符;3、乘法运算符;4、除法运算符;5、取余运算符;6、比较运算符;7、位运算符;8、按位与运算符;9、按位或运算符;10、按位异或运算符等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2304

2024.02.23

Conan私有仓库搭建教程
Conan私有仓库搭建教程

本专题系统的讲解Conan私有仓库的搭建流程,涵盖仓库服务部署、存储目录配置、用户认证、权限划分和远程地址添加,并介绍内部C++依赖包的上传、下载及版本维护方法。

0

2026.09.22

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn