将Excel数据加速到熊猫

时间:2019-06-19 08:05:11

标签: python excel python-3.x pandas performance

我有一段非常简单的代码,其中有一组文件名,我需要打开每个文件名并提取一些数据以供以后使用。

for file in unique_file_names[1:]:
        file_name = rootdir + "/" + str(file)
        test_time = time.clock()    
        try:
            wb_loop = load_workbook(file_name, read_only=True, data_only=True)
            ws_loop = wb_loop["SHEET1"]
            df = pd.DataFrame(ws_loop.values)
            print("Opening Workbook:         ", time.clock()-test_time)

            newarray = np.vstack((newarray, df.loc[4:43,:13].values))
            print("Data Manipulation:         ", time.clock()-test_time)

因此,我尝试了几种不同的模块来读取excel文件,包括直接使用pandas.read_excel(),这是最佳方法,设法将工作簿打开时间为1.5-2s,并且堆叠需要花费0.03秒的时间。

我认为根据索引将数据分配到数组中的第三个维度可能会更快,但是我更专注于加快加载电子表格的时间,有什么建议吗?

编辑:我也确实创建了一个多线程池来尝试加快速度,但是由于某种原因,它开始使用15Gb内存并导致计算机崩溃

编辑2:

因此,最快的方法是按照公认的答案建议使用xlrd。我还意识到,在循环结束时删除工作簿更快。最终代码类似于

for file in unique_file_names[1:]:
        file_name = rootdir + "/" + str(file)
        test_time = time.clock()    
        try:
            wb_loop = xlrd.open_workbook(file_name, on_demand = True)
            ws_loop = wb_loop.sheet_by_name("Sheet1")
            print("Opening Workbook:         ", time.clock()-test_time)

            df = pd.DataFrame([ws_loop.row_values(n) for n in  range(ws_loop.nrows)])            

            newarray = np.vstack((newarray, df.loc[4:43,:13].values))
            del wb_loop

            print("Data Manipulation:         ", time.clock()-test_time)

        except:
            pass
        counter+=1
        print("%s %% Done" %(counter*100/len(unique_file_names)))

    wb_new = xlwt.Workbook()
    ws_new = wb_new.add_sheet("Test")
    ws_new.write(newarray)
    wb_new.save(r"C:Libraries/Documents/NewOutput.xls")

这将输出每个循环平均时间1.6-1.8s。谢谢大家的帮助。

2 个答案:

答案 0 :(得分:1)

这是一个快速基准测试(扩展this one)。显然,对于测试.xlsx文件,直接使用xlrd的速度要比熊猫快一些。如果.csv文件可用,则读取它们肯定会快得多,但使用LibreOffice进行转换则要慢得多:

pd_base 1.96 [in seconds]
pd_float 2.03
pd_object 2.01 [see cs95´s comment to your question]
pd_xlrd 1.95
pyxl_base 2.15
xlrd_base 1.79
csv_ready 0.17
csv_convert 18.72

代码如下:

import pandas as pd
import openpyxl
import xlrd
import subprocess

file = 'test.xlsx'
df = pd.DataFrame([[i+j for i in range(50)] for j in range(100)])
df.to_excel(file, index=False)
df.to_csv(file.replace('.xlsx', '.csv'), index=False)

def pd_base():
    df = pd.read_excel(file)
def pd_float():
    df = pd.read_excel(file, dtype=np.int)
def pd_object():
    df = pd.read_excel(file, sheet_name="Sheet1", dtype=object)
def pd_xlrd():
    df = pd.read_excel(file, engine='xlrd')
def pyxl_base():
    wb = openpyxl.load_workbook(file, read_only=True, keep_links=False, data_only=True)
    sh = wb.active
    df = pd.DataFrame(sh.values)
def xlrd_base():
    wb = xlrd.open_workbook(file)
    sh = wb.sheet_by_index(0)
    df = pd.DataFrame([sh.row_values(n) for n in  range(sh.nrows)])
def csv_ready():    
    df = pd.read_csv(file.replace('.xlsx', '.csv'))
def csv_convert():    
    out = subprocess.check_output(['libreoffice --headless --convert-to csv test.xlsx'], shell=True, stderr=subprocess.STDOUT)
    df = pd.read_csv(file.replace('.xlsx', '.csv'))

def measure(func, nums=50):
    temp = time.time()
    for num in range(nums):
        func()
    diff = time.time() - temp
    print(func.__name__, '%.2f' % diff)

for func in [pd_base, pd_float, pd_object, pd_xlrd, pyxl_base, xlrd_base, csv_ready, csv_convert]:
    measure(func)    

答案 1 :(得分:0)

两个提示:

  • ProcessPoolExecutor具有比纯多处理池更好的接口
  • 如果要加载大文件,必须控制内存的使用方式。

如果不提供类型,则大多数库将使用可能的最大值(64位)。如果即使在控制类型之后,您的数据也无法容纳在内存中,您就需要考虑分区并溢出到磁盘上。

下面是通过Executor接口编写的用于控制数据类型的代码示例

from concurrent.futures import ProcessPoolExecutor
from openpyxl import load_workbook
import pandas as pd
import numpy as np


def load_single(file):
    file_name = rootdir + "/" + str(file)
    wb_loop = load_workbook(file_name, read_only=True, data_only=True)
    ws_loop = wb_loop["SHEET1"]
    df = pd.DataFrame(ws_loop.values)
    partial_array = df.loc[4:43, :13].values.astype(np.float32)
    return partial_array

def run():
    executor = ProcessPoolExecutor(max_workers=4)
    files = unique_file_names[1:]
    results = executor.map(load_single, files)
    new_array = np.empty((0, 39), dtype=np.float32)
    for partial_array in results:
        new_array = np.vstack([new_array, partial_array])