小能豆

如何快速将多张 Excel 文件合并为一个 Excel 文件

py

问题概述

我需要将几个 .xlsx 文件合并到工作表中,其中每个工作表名称必须是文件名。


当前问题

下面的代码在处理几个文件之后会变慢并消耗大量内存。


尝试的解决方案

关闭 excel 文件并删除数据框并gc手动运行不起作用。

代码

import pandas as pd
import openpyxl
import os
import gc as gc

print("Copying sheets from multiple files to one file")

dir_input = 'D:/MeusProjetosJava/Importacao/'
dir_output = "Integrados/combined.xlsx"

cwd = os.path.abspath(dir_input)
files = os.listdir(cwd)

df_total = pd.DataFrame()
df_total.to_excel(dir_output) #create a new file
workbook=openpyxl.load_workbook(dir_output)
ss_sheet = workbook['Sheet1']
ss_sheet.title = 'TempExcelSheetForDeleting'
workbook.save(dir_output)


for file in files:                         # loop through Excel files
    if file.endswith('.xls') or file.endswith('.xlsx'):
        excel_file = pd.ExcelFile(cwd+"/"+file)
        sheets = excel_file.sheet_names
        for sheet in sheets:
            sheet_name = str(file.title())
            sheet_name = sheet_name.replace(".xlsx","").lower()
            sheet_name = sheet_name.removesuffix(".xlsx")

            print(file, sheet_name)

            df = excel_file.parse(sheet_name = sheet)
            with pd.ExcelWriter(dir_output,mode='a') as writer:
                df.to_excel(writer, sheet_name=f"{sheet_name}", index=False)
                del df

        excel_file.close()
        del excel_file
        sheets = None
        gc.collect()


workbook=openpyxl.load_workbook(dir_output)
std=workbook["TempExcelSheetForDeleting"]
workbook.remove(std)
workbook.save(dir_output)
print("all done")

阅读 15

收藏
2024-12-09

共1个答案

小能豆

你的问题是,在处理多个 .xlsx 文件并将它们合并到一个工作簿时,内存使用量不断增加并且速度变慢。这通常是由于在循环中频繁打开和关闭文件以及多次调用 pandasExcelWriteropenpyxl 导致的性能瓶颈。

你提到尝试手动清理内存和关闭文件没有奏效。以下是一些改进的建议和重构代码来减少内存使用和提高性能:

改进思路:

  1. 减少频繁的文件打开/关闭:每次打开 ExcelWriter 和写入文件时会增加内存负担,特别是在处理多个文件时。相反,可以先将所有需要写入的内容暂存到一个数据结构中,最后一次性写入。
  2. 避免频繁的 gc.collect() 调用:Python 的垃圾回收机制应该足够智能,手动调用 gc.collect() 通常不必要,并且会影响性能。
  3. 优化 openpyxlpandas 的结合使用openpyxlpandas 会进行重复的 I/O 操作,可以通过减少这种重复工作来提高效率。

改进后的代码:

import pandas as pd
import openpyxl
import os

print("Copying sheets from multiple files to one file")

dir_input = 'D:/MeusProjetosJava/Importacao/'
dir_output = "Integrados/combined.xlsx"

cwd = os.path.abspath(dir_input)
files = os.listdir(cwd)

# Prepare ExcelWriter once, write all sheets at once
with pd.ExcelWriter(dir_output, engine='openpyxl') as writer:

    # Loop through Excel files
    for file in files:
        if file.endswith('.xls') or file.endswith('.xlsx'):
            excel_file = pd.ExcelFile(cwd + "/" + file)
            sheets = excel_file.sheet_names
            for sheet in sheets:
                # Create a unique sheet name based on the file name
                sheet_name = file.replace(".xlsx", "").replace(".xls", "").lower()
                print(f"Processing {file}, Sheet: {sheet_name}")

                # Parse the sheet into a DataFrame
                df = excel_file.parse(sheet_name=sheet)

                # Write the DataFrame to the Excel file with the sheet name
                df.to_excel(writer, sheet_name=sheet_name, index=False)

            # No need to manually close, `excel_file` is handled by context manager

# After all sheets are written, now remove the default "Sheet1" if it exists
workbook = openpyxl.load_workbook(dir_output)
if 'Sheet1' in workbook.sheetnames:
    del workbook['Sheet1']
workbook.save(dir_output)

print("All done")

主要改进:

  1. 一次性使用 ExcelWriter:所有的文件和工作表都在同一个 with 语句块中写入,避免了多次打开和关闭 ExcelWriter 的开销。
  2. 删除默认的 “Sheet1” 工作表:在写入完所有文件后,我们通过 openpyxl 删除默认的工作表 Sheet1,这是 pandas.ExcelWriter 创建的新工作簿中的默认工作表。
  3. 简化的文件处理:每个文件和工作表只需要被读取一次,并写入目标文件中。此方法避免了大量的中间变量和额外的内存使用。

为什么这样更高效:

  • 通过将所有工作表一次性写入输出文件,你避免了每次写入时重复打开和关闭文件的开销。
  • 在每个工作表写入后不再频繁执行 gc.collect(),Python 会自动管理内存。
  • 使用 openpyxl 删除默认的 Sheet1,保证了最终的文件格式是我们需要的。

额外的优化:

  1. 如果文件较大,可以考虑分批加载和写入,以避免一次性加载所有数据带来的内存压力。
  2. 如果文件中的工作表非常多,可以考虑使用 openpyxlxlrd 等库直接读取工作表,而不是先加载到 pandas.DataFrame,避免 pandas 的中间存储消耗过多内存。

希望这个版本的代码能够有效地提升性能并减少内存使用!

2024-12-09