【问题标题】:openpyxl is really slow in read only mode!! how can I improve my script?openpyxl 在只读模式下真的很慢!!如何改进我的脚本?
【发布时间】:2020-09-26 15:59:56
【问题描述】:

我写了一个简单的例子。此脚本读取一个 excel 文件并仅从其中复制第一列中存在列表“ls”中指定的字符串之一的行。复制的行将粘贴到另一个 excel 文件中。在我的代码下方:

from datetime import datetime
import openpyxl

print("Current Time =", datetime.now().strftime("%H:%M:%S"))
src_wb = openpyxl.load_workbook("C:\\Users\\Admin\\Desktop\\database.xlsx")
print("Current Time =", datetime.now().strftime("%H:%M:%S"))
print("\n\n")

src_ws = src_wb[src_wb.sheetnames[0]]

dst_wb = openpyxl.Workbook()
dst_ws = dst_wb.active
dst_ws.title = "TEST"

dst_row_id, src_row_id = 0, 0 
ls = ["ciao", "hola", "hello"]
my_column_id = 1

for row in src_ws.rows:
    src_row_id += 1
    #print("Current Time =", datetime.now().strftime("%H:%M:%S"))

    for element in ls:
            if src_ws.cell(src_row_id, my_column_id).value == element:
                dst_row_id += 1
                dst_column_id = 0
                for cell in row:

                    dst_column_id += 1
                    dst_cell = dst_ws.cell(dst_row_id,column=dst_column_id)

                    dst_cell.value = cell.value
                break

dst_wb.save("test.xlsx")
dst_wb.close()

在输出下方:

λ python stack.py
Current Time = 19:36:38
Current Time = 19:37:20 <- after this point the script is quite fast

这段代码运行良好,但是当我的源 excel 文件很重时,指令 src_wb = openpyxl.load_workbook("C:\\Users\\Admin\\Desktop\\database.xlsx") 需要很多次。在我的示例中,“database.xlsx”文件有 177137 行14 列。打开它,openpyxl 需要 42 秒,但是当它必须读取行时,它似乎很快。

为了改善这种情况我尝试使用“只读”模式(openpyxl.load_workbook("C:\\Users\\Admin\\Desktop\\database.xlsx", read_only = True)但是这样脚本变得太慢了..它可以快速打开“database.xlsx”文件,但是在“复制和粘贴”非常慢(它太慢了,完成任务需要几个小时!)请参阅下面的输出:

λ python stack.py
Current Time = 19:37:47
Current Time = 19:37:52 <-- after this point the script is very slow

如何提高只读模式下的脚本性能?或者以另一种方式,我不知道.. 根据您的经验,改进我的代码的最佳解决方案是什么?

@Daniel Ocando:我的 excel 文件由简单的字符串组成。下面是一个例子:

【问题讨论】:

  • 文件中单元格的上下文是什么?您介意展示一下 Excel 文件的外观示例吗?
  • # Daniel Ocando,我的“database.xlsx”文件由简单的字符串组成,没什么特别的。看我附上的截图(这只是一个例子)。
  • 您的只读模式问题是因为您没有遵循使用它的建议。

标签: python openpyxl


【解决方案1】:

您提供的代码具有三个嵌套的 for 循环(这通常意味着代码的时间复杂度为 O(n3))。我设法删除了其中一个 for 循环,因此将时间复杂度降低到 O(n2)

您可以利用sets are generally faster than lists regarding membership tests 并将ls = ["ciao", "hola", "hello"] 列表更改为set

最后,您可以利用write_only mode 来尝试加快写作速度。

考虑到单元格只包含一个带有简单单词的字符串,您可以尝试以下代码并检查性能是否有所提高:

from datetime import datetime
import openpyxl

def get_list(row):
    list_for_row_values = []
    for cell in row:
        list_for_row_values.append(cell.value)
    return list_for_row_values

print("Current Time =", datetime.now().strftime("%H:%M:%S"))
src_wb = openpyxl.load_workbook("C:\\Users\\Admin\\Desktop\\database.xlsx", read_only=True)
print("Current Time =", datetime.now().strftime("%H:%M:%S"))
print("\n\n")

src_ws = src_wb[src_wb.sheetnames[0]]

dst_wb = openpyxl.Workbook(write_only=True)
dst_ws = dst_wb.create_sheet()

ls = ("ciao", "hola", "hello")

for row in src_ws.rows:
    if(row[0].value) in ls:
        list_for_row_values = get_list(row)
        dst_ws.append(list_for_row_values)

dst_wb.save("test.xlsx")

【讨论】:

  • @Daniel Ocando:哇哦,Daniel,它有效!我刚刚用你的代码调整了我的真实代码,它似乎工作得很好。我还添加了一个缓存以防止添加重复行(它只是一个简单的字典)。我对openpyxl不太了解,但似乎我的代码和你的代码之间的主要区别在于选择通过“append”方法而不是“cell”方法编写。可能在“只读”模式下,“cell”方法存在一些性能问题。
  • Daniel,只是想知道,在您的代码中,是否也可以复制单元格样式?如果它不影响性能,也可以添加此功能。
  • 您需要使用 copy() 模块。查找其他post中的所有信息。
  • 关于集合比包含检查的列表更快的观点仅在理论上有效,几乎可以肯定与此无关 - 这是一种很好的做法,但也是过早的优化。
猜你喜欢
  • 2022-06-30
  • 2011-11-08
  • 2012-05-31
  • 1970-01-01
  • 2019-03-19
  • 2022-12-09
  • 1970-01-01
  • 1970-01-01
  • 2019-01-29
相关资源
最近更新 更多