openpyxl是所有解析操作excel文件的python库,最受欢迎的扩展之一,因为它的底层lxml是用Cython实现的,读取大型的Excel文件的性能非常出色。
本片示例是读取一个具有一个接近87万记录的Excel表格,并将所有记录加载到Cython扩展类型的list中,需要强调的是Openpyxl加载工作簿使用最小的时间,一定要使用只读模式。
在load_workbook函数中指定read_only = True即可,如下所示:
wb=openpyxl.load_workbook(filename,read_only=True)
反例演示
下面的代码是一个非常低效的实现,我们分析一下代码,我们从load_workbook函数加载完成后我们得到Workbook对象
从Workbook对象中我们可以得到Sheet对象-ws ,但在遍历操作中使用Sheet对象的max_row和max_column属性不是一个明智的选择,因为excel工作表实际上可能比它们看起来更大,并且最终您要遍历许多空单元格。
from openpyxl import load_workbook
def read_excel():
wb=load_workbook("./Vegetable_Fruits.xlsx",read_only=True)
ws=wb['sheet1']
data=list()
maxRow=ws.max_row+1
maxCol=ws.max_column
for row in range(1,maxRow):
neRow=[]
for col in range(0,maxCol):
neRow.append(ws[row][col].value)
if len(neRow):
data.append(neRow)
return data
我们对上面函数做些修改,将maxRow的记录指定为1000,也就是我们仅读取1000条记录,看看时间消耗77s,那么我们推算一下如果读取完868,966条记录需要的时间67,605秒。
我们对上面的代码做一些修改,我们将Sheet对象的max_rows属性和max_column属性替换为rows,
from openpyxl import load_workbook
def read_excel():
wb=load_workbook("./Vegetable_Fruits.xlsx",read_only=True)
ws=wb['sheet1']
data=[]
for row in ws.rows:
neRow=[]
for cell in row:
neRow.append(cell.value)
if len(neRow):
data.append(neRow)
return data
恩,时间开销是非常悬殊的,修改后的代码,获取excel工作捕的接近87万记录,时间开销35秒,而修改之前的代码获取代1000条记录要消耗77.8秒
要使openpyxl获取速度最快的读取速度,不应该在每一行的循环中进行如下的操作
- 查找列标题
- 调用Sheet对象的max_row属性或max_column属性,这操作开销异常巨大
- 使用A坐标风格访问单元格,例如ws["AB" + str(i)]
上面的代码,其实我们还可以做进一步的优化,我们可以 使用openpyxl内置的迭代器,只要通过enumerate方法传入Sheet对象的rows属性,进而简直告知openpyxl调用其他内部的迭代器,因为Openpyxl的内部迭代器是由C代码所写,因此具有绝代的访问性能优势。
而且我们也使用Cython代码对上面的代码进一步优化
%%cython
from openpyxl import load_workbook
cpdef list read_excel_cy2(str filename):
wb=load_workbook(filename,read_only=True)
ws=wb['sheet1']
cdef list data=list(),neRow
cdef int rdx,cdx
for rdx,row in enumerate(ws.rows):
neRow=[]
for cdx,cell in enumerate(row):
neRow.append(cell.value)
if len(neRow):
data.append(neRow)
return data
这里使用Cython对函数中的某些计算变量进行类型静态化总能快1-2s
小结
要openpyxl在读取速度上获取最佳的速度,受很多因素影响,例如设备I/O资源使用情况,我们在算法逻辑上做到如下几点
- 调用load_workbook时候使用只读模式
- 使用Cython对循环计算变量进行C类型静态化
- 在循环中调用尽可能少地起作用,并将中间数据存储在变量中。 它可能会使代码有点笨拙,但它往往会更高效
- 上文提到的注意事项。
做到这些,可以使您的代码更具可读性(但这比起第1点和第2点而言是锦上添花)。 Python关于什么是变量和什么是函数也可能是模棱两可的。 但通常来说,中间变量适合于多个函数的调用。对于I/O密集型的代码加速而言,Cython是力不从心的,此时,我们更多地考虑使用异步编程模式,或多线程模式来进一步提升读取速度。
网友评论