提高 excel 文件操作的速度(使用 openpyxl):检查值并在条件下删除行操作
Increase the speed of an excel file operations (using openpyxl): check value and delete rows operations if condition
我有一个中等大小的 excel 文件,大约有 25000 行。
在 excel 文件中,我检查特定列值是否在列表中,如果在列表中,我删除该行。
我正在使用 openpyxl。
代码:
count = 1
while count <= ws.max_row:
if ws.cell(row=count, column=2).value in remove_list:
ws.delete_rows(count, 1)
else:
count += 1
wb.save(src)
代码有效,但完成速度非常慢(需要数小时)。
我知道这是只读和只写模式,但在我的情况下,我同时使用了,第一次检查和第二次删除。
就个人而言,我会做两件事:
首先将列表转换为集合,这样查找项目的时间会更少
remove_set = set(remove_list)
...
if ws.cell(row=count, column=2).value in remove_set:
然后我会避免删除行,因为重新组织表示 sheet 的数据结构需要很多时间。
我会创建一个新的空白作品sheet并只向其中添加必须保留的行。
然后保存新作品sheet,如果你愿意,可以覆盖原来的作品。
如果仍然需要太长时间,请考虑使用 CSV 格式,这样您就可以将输入数据视为文本并以相同的方式输出,稍后从 spreadsheet 程序重新导入数据(例如女士-Excel)
查看 official docs and at this tutorial 了解如何使用 CSV 库
进一步说明:正如@Charlie Clark 所发现的那样,
的计算
ws.max_row
可能也需要一些时间,无需重复。
要做到这一点,最简单的解决方案是从最后一行倒推到第一行,这样删除的行就不会影响前面行的位置。
我看到您正在使用需要删除的行列表。相反,您可以创建 "sequences" 行来删除,从而将 [2,3,4,5,6,7,8,45,46,47,48] 等删除列表更改为 [[2 , 7],[45, 4]]
即从第2行开始删除7行,然后从第45行开始删除4行
批量删除比 1 对 1 快。我在大约 10 秒内删除了 6k 行
以下代码将列表转换为 lists/sequences 的列表:
def get_sequences(list_of_ints):
sequence_count = 1
sequences = []
for row in list_of_ints:
next_item = None
if list_of_ints.index(row) < (len(list_of_ints) - 1):
next_item = list_of_ints[list_of_ints.index(row) + 1]
if (row + 1) == next_item:
sequence_count += 1
else:
first_in_sequence = list_of_ints[list_of_ints.index(row) - sequence_count + 1]
sequences.append([first_in_sequence, sequence_count])
sequence_count = 1
return sequences
然后运行另一个要删除的循环
for sequence in sequences:
sheet.delete_rows(sequence[0], sequence[1])
当必须从 sheet 中删除多行时,我创建了一个包含这些行号的列表,例如remove_list
然后我将 sheet 重写为临时 sheet,排除这些行。我删除原来的sheet,把临时的sheet重命名为原来的sheet。在下面查看我执行此操作的功能:
def delete_excel_rows_with_openpyxl(workbook, sheet, remove_list):
""" Delete rows with row numbers in remove_list from sheet contained in workbook """
temp_sheet = workbook.create_sheet('TempSheet')
destination_row_counter = 1
for source_row_counter, source_row in enumerate(sheet.iter_rows(min_row=1, max_row=sheet.max_row)):
try:
i = remove_list.index(source_row_counter+1) # enumerate counts from 0 and sheet from 1
# do not copy row
del remove_list[i]
except ValueError:
# copy row
column_count = 1
for cell in source_row:
temp_sheet.cell(row=destination_row_counter, column=column_count).value = cell.value
column_count = column_count + 1
destination_row_counter = destination_row_counter + 1
sheet_title = sheet.title
workbook.remove_sheet(sheet)
temp_sheet.title = sheet_title
return workbook, temp_sheet
补充 ketdaddy 的回复。我对其进行了测试并注意到,当您按照建议在 for 循环中使用此序列时,您需要更新每个循环中的行号以说明已删除的行。
例如,当你进入循环的第二步时,开始行不是原来的开始行,而是原来的开始行减去之前删除的行。
此代码将更新 ketdaddy 的序列以生成考虑到这一点的序列。
original sequence = get_sequences(deleterows)
updated_sequence=[]
cumdelete = 0
for start, delete in original sequence:
new_start = start-cumdelete
cumdelete = cumdelete + delete
updated_sequence.append([new_start, delete])
updated_sequence
我有一个中等大小的 excel 文件,大约有 25000 行。
在 excel 文件中,我检查特定列值是否在列表中,如果在列表中,我删除该行。
我正在使用 openpyxl。
代码:
count = 1
while count <= ws.max_row:
if ws.cell(row=count, column=2).value in remove_list:
ws.delete_rows(count, 1)
else:
count += 1
wb.save(src)
代码有效,但完成速度非常慢(需要数小时)。
我知道这是只读和只写模式,但在我的情况下,我同时使用了,第一次检查和第二次删除。
就个人而言,我会做两件事:
首先将列表转换为集合,这样查找项目的时间会更少
remove_set = set(remove_list)
...
if ws.cell(row=count, column=2).value in remove_set:
然后我会避免删除行,因为重新组织表示 sheet 的数据结构需要很多时间。
我会创建一个新的空白作品sheet并只向其中添加必须保留的行。
然后保存新作品sheet,如果你愿意,可以覆盖原来的作品。
如果仍然需要太长时间,请考虑使用 CSV 格式,这样您就可以将输入数据视为文本并以相同的方式输出,稍后从 spreadsheet 程序重新导入数据(例如女士-Excel)
查看 official docs and at this tutorial 了解如何使用 CSV 库
进一步说明:正如@Charlie Clark 所发现的那样,
的计算ws.max_row
可能也需要一些时间,无需重复。
要做到这一点,最简单的解决方案是从最后一行倒推到第一行,这样删除的行就不会影响前面行的位置。
我看到您正在使用需要删除的行列表。相反,您可以创建 "sequences" 行来删除,从而将 [2,3,4,5,6,7,8,45,46,47,48] 等删除列表更改为 [[2 , 7],[45, 4]]
即从第2行开始删除7行,然后从第45行开始删除4行
批量删除比 1 对 1 快。我在大约 10 秒内删除了 6k 行
以下代码将列表转换为 lists/sequences 的列表:
def get_sequences(list_of_ints):
sequence_count = 1
sequences = []
for row in list_of_ints:
next_item = None
if list_of_ints.index(row) < (len(list_of_ints) - 1):
next_item = list_of_ints[list_of_ints.index(row) + 1]
if (row + 1) == next_item:
sequence_count += 1
else:
first_in_sequence = list_of_ints[list_of_ints.index(row) - sequence_count + 1]
sequences.append([first_in_sequence, sequence_count])
sequence_count = 1
return sequences
然后运行另一个要删除的循环
for sequence in sequences:
sheet.delete_rows(sequence[0], sequence[1])
当必须从 sheet 中删除多行时,我创建了一个包含这些行号的列表,例如remove_list
然后我将 sheet 重写为临时 sheet,排除这些行。我删除原来的sheet,把临时的sheet重命名为原来的sheet。在下面查看我执行此操作的功能:
def delete_excel_rows_with_openpyxl(workbook, sheet, remove_list):
""" Delete rows with row numbers in remove_list from sheet contained in workbook """
temp_sheet = workbook.create_sheet('TempSheet')
destination_row_counter = 1
for source_row_counter, source_row in enumerate(sheet.iter_rows(min_row=1, max_row=sheet.max_row)):
try:
i = remove_list.index(source_row_counter+1) # enumerate counts from 0 and sheet from 1
# do not copy row
del remove_list[i]
except ValueError:
# copy row
column_count = 1
for cell in source_row:
temp_sheet.cell(row=destination_row_counter, column=column_count).value = cell.value
column_count = column_count + 1
destination_row_counter = destination_row_counter + 1
sheet_title = sheet.title
workbook.remove_sheet(sheet)
temp_sheet.title = sheet_title
return workbook, temp_sheet
补充 ketdaddy 的回复。我对其进行了测试并注意到,当您按照建议在 for 循环中使用此序列时,您需要更新每个循环中的行号以说明已删除的行。
例如,当你进入循环的第二步时,开始行不是原来的开始行,而是原来的开始行减去之前删除的行。
此代码将更新 ketdaddy 的序列以生成考虑到这一点的序列。
original sequence = get_sequences(deleterows)
updated_sequence=[]
cumdelete = 0
for start, delete in original sequence:
new_start = start-cumdelete
cumdelete = cumdelete + delete
updated_sequence.append([new_start, delete])
updated_sequence