我遇到了一个大 xls 文件的大问题。当我的应用程序添加新的统计记录(文件末尾的新行)时,需要很长的时间(一分钟)。如果我用空的 xls 文件替换它,效果最好(1-2 秒)。因此,如果可能的话,我会尝试对此进行优化。
我使用类似的东西:
def add_stats_record():
# Add record
lock = LockFile(STATS_FILE)
with lock:
# Open for read
rb = open_workbook(STATS_FILE, formatting_info=True)
sheet_records = rb.sheet_by_index(0)
# record_id
START_ROW = sheet_records.nrows
try:
record_id = int(sheet_records.cell(START_ROW - 1, 0).value) + 1
except:
record_id = 1
# Open for write
wb = copy(rb)
sheet_records = wb.get_sheet(0)
# Set normal style
style_normal = xlwt.XFStyle()
normal_font = xlwt.Font()
style_normal.font = normal_font
# Prepare some data here
........................
# then:
for i, col in enumerate(SHEET_RECORDS_COLS):
sheet_records.write(START_ROW, i, possible_values.get(col[0], ''),
style_normal)
wb.save(STATS_FILE)
您认为这里有需要改进的地方吗?或者您能给我一个更好的想法/示例如何做到这一点?
最佳答案
可能不是您想听到的答案,但几乎没有什么可以优化的。
import xlwt, xlrd
from xlutils.copy import copy as copy
from time import time
def add_stats_record():
#Open for read
start_time = time()
rb = xlrd.open_workbook(STATS_FILE, formatting_info=True)
sheet_records_original = rb.sheet_by_index(0)
print('Elapsed time for opening: %.2f' % (time()-start_time))
#Record_id
start_time = time()
START_ROW = sheet_records_original.nrows
SHEET_RECORDS_COLS = sheet_records_original.ncols
try:
record_id = int(sheet_records.cell(START_ROW - 1, 0).value) + 1
except:
record_id = 1
print('Elapsed time for record ID: %.2f' % (time()-start_time))
#Open for write
start_time = time()
wb = copy(rb)
sheet_records = wb.get_sheet(0)
print('Elapsed time for write: %.2f' % (time()-start_time))
#Set normal style
style_normal = xlwt.XFStyle()
normal_font = xlwt.Font()
style_normal.font = normal_font
#Read all the data and get some stats
start_time = time()
max_col = {}
start_time = time()
for col_idx in range(0,16):
max_value = 0
for row_idx in range(START_ROW):
if sheet_records_original.cell(row_idx, col_idx).value:
val = float(sheet_records_original.cell(row_idx, col_idx).value)
if val > max_value:
max_col[col_idx] = str(row_idx) + ';' + str(col_idx)
text_cells = [[0 for x in range(15)] for y in range(START_ROW)]
for col_idx in range(16,31):
max_value = 0
for row_idx in range(START_ROW):
if sheet_records_original.cell(row_idx, col_idx).value:
val = str(sheet_records_original.cell(row_idx, col_idx).value).replace('text', '').count(str(col_idx))
if val > max_value:
max_col[col_idx] = str(row_idx) + ';' + str(col_idx)
print('Elapsed time for reading data/stats: %.2f' % (time()-start_time))
#Write the stats row
start_time = time()
for i in range(SHEET_RECORDS_COLS):
sheet_records.write(START_ROW, i, max_col[i], style_normal)
start_time = time()
wb.save(STATS_FILE)
print('Elapsed time for writing: %.2f' % (time()-start_time))
if __name__ == '__main__':
STATS_FILE = 'output.xls'
start_time2 = time()
add_stats_record()
print ('Total time: %.2f' % (time() - start_time2))
Elapsed time for opening: 2.43
Elapsed time for record ID: 0.00
Elapsed time for write: 7.62
Elapsed time for reading data/stats: 2.35
Elapsed time for writing: 3.33
Total time: 15.75
从这些结果可以清楚地看出,您的代码几乎没有任何改进的空间。打开/复制/写入弥补了大部分时间,但只是对 xlrd/xlwt
的简单调用。
在open_workbook
中使用on_demand=True
也没有帮助。
使用 openpyxl
也不会提高性能。
from openpyxl import load_workbook
from time import time
#Load workbook
start_time = time()
wb = load_workbook('output.xlsx')
print('Elapsed time for loading workbook: %.2f' % (time.time()-start_time))
#Read all data
start_time = time()
ws = wb.active
cell_range1 = ws['A1':'P20001']
cell_range2 = ws['Q1':'AF20001']
print('Elapsed time for reading workbook: %.2f' % (time.time()-start_time))
#Save to a new workbook
start_time = time()
wb.save("output_tmp.xlsx")
print('Elapsed time for saving workbook: %.2f' % (time.time()-start_time))
Elapsed time for loading workbook: 22.35
Elapsed time for reading workbook: 0.00
Elapsed time for saving workbook: 21.11
Ubuntu 14.04(虚拟机)/Python2.7-64bit/普通硬盘(与原生Windows 10结果相似,Python 3加载性能较差,但写入性能更好)。
使用 Pandas 和 Numpy 生成随机数据
import pandas as pd
import numpy as np
#just random numbers
df = pd.DataFrame(np.random.rand(20000,30), columns=range(0,30))
#convert half the columns to text
for i in range(15,30):
df[i].apply(str)
df[i] = 'text' + df[i].astype(str)
writer = pd.ExcelWriter(STATS_FILE)
df.to_excel(writer,'Sheet1')
writer.save()
经过一番摆弄多处理
后,我发现了一个稍微改进的解决方案。由于复制操作是最耗时的操作,并且共享工作簿会使性能变差,因此采取了不同的方法。两个线程都读取原始工作簿,一个线程读取数据,计算统计信息并将其写入文件(tmp.txt
),另一个线程复制工作簿,等待统计文件出现,然后将其写入新复制的工作簿。
差异:总共所需时间减少 12%(两个脚本的 n=3)。不太好,但除了不使用 Excel 文件之外,我想不出其他方法。
xls_copy.py
def xls_copy(STATS_FILE, START_ROW, style_normal):
from xlutils.copy import copy as copy
from time import sleep, time
from os import stat
from xlrd import open_workbook
print('started 2nd thread')
start_time = time()
rb = open_workbook(STATS_FILE, formatting_info=True)
wb = copy(rb)
sheet_records = wb.get_sheet(0)
print('2: Elapsed time for xls_copy: %.2f' % (time()-start_time))
counter = 0
filesize = stat('tmp.txt').st_size
while filesize == 0 and counter < 10**5:
sleep(0.01)
filesize = stat('tmp.txt').st_size
counter +=1
with open('tmp.txt', 'r') as f:
for line in f.readlines():
cells = line.split(';')
sheet_records.write(START_ROW, int(cells[0]), cells[1], style_normal)
start_time = time()
wb.save('tmp_' + STATS_FILE)
print('2: Elapsed time for writing: %.2f' % (time()-start_time))
xlsx_multi.py
from xls_copy import xls_copy
import xlwt, xlrd
from time import time
from multiprocessing import Process
def add_stats_record():
#Open for read
start_time = time()
rb = xlrd.open_workbook(STATS_FILE, formatting_info=True)
sheet_records_original = rb.sheet_by_index(0)
print('Elapsed time for opening: %.2f' % (time()-start_time))
#Record_id
start_time = time()
START_ROW = sheet_records_original.nrows
f = open('tmp.txt', 'w')
f.close()
#Set normal style
style_normal = xlwt.XFStyle()
normal_font = xlwt.Font()
style_normal.font = normal_font
#start 2nd thread
p = Process(target=xls_copy, args=(STATS_FILE, START_ROW, style_normal,))
p.start()
print('continuing with 1st thread')
SHEET_RECORDS_COLS = sheet_records_original.ncols
try:
record_id = int(sheet_records.cell(START_ROW - 1, 0).value) + 1
except:
record_id = 1
print('Elapsed time for record ID: %.2f' % (time()-start_time))
#Read all the data and get some stats
start_time = time()
max_col = {}
start_time = time()
for col_idx in range(0,16):
max_value = 0
for row_idx in range(START_ROW):
if sheet_records_original.cell(row_idx, col_idx).value:
val = float(sheet_records_original.cell(row_idx, col_idx).value)
if val > max_value:
max_col[col_idx] = str(row_idx) + ';' + str(col_idx)
text_cells = [[0 for x in range(15)] for y in range(START_ROW)]
for col_idx in range(16,31):
max_value = 0
for row_idx in range(START_ROW):
if sheet_records_original.cell(row_idx, col_idx).value:
val = str(sheet_records_original.cell(row_idx, col_idx).value).replace('text', '').count(str(col_idx))
if val > max_value:
max_col[col_idx] = str(row_idx) + ';' + str(col_idx)
#write statistics to a temp file
with open('tmp.txt', 'w') as f:
for k in max_col:
f.write(str(k) + ';' + max_col[k] + str('\n'))
print('Elapsed time for reading data/stats: %.2f' % (time()-start_time))
p.join()
if __name__ == '__main__':
done = False
wb = None
STATS_FILE = 'output.xls'
start_time2 = time()
add_stats_record()
print ('Total time: %.2f' % (time() - start_time2))
关于python - 使用 xlwt 优化 xls 文件中的添加行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37553767/