web-dev-qa-db-ja.com

Excelファイルの読み取りは、xlrdと比較してopenpyxlを使用すると非常に遅くなります

毎日SQL Serverにインポートする必要があるExcelスプレッドシートがあります。スプレッドシートには、約50列にわたって約250,000行が含まれます。 openpyxlxlrdを使用して、ほぼ同じコードを使用して両方をテストしました。

私が使用しているコードは次のとおりです(デバッグステートメントを除く)。

import xlrd
import openpyxl

def UseXlrd(file_name):
    workbook = xlrd.open_workbook(file_name, on_demand=True)
    worksheet = workbook.sheet_by_index(0)
    first_row = []
    for col in range(worksheet.ncols):
        first_row.append(worksheet.cell_value(0,col))
    data = []
    for row in range(1, worksheet.nrows):
        record = {}
        for col in range(worksheet.ncols):
            if isinstance(worksheet.cell_value(row,col), str):
                record[first_row[col]] = worksheet.cell_value(row,col).strip()
            else:
                record[first_row[col]] = worksheet.cell_value(row,col)
        data.append(record)
    return data


def UseOpenpyxl(file_name):
    wb = openpyxl.load_workbook(file_name, read_only=True)
    sheet = wb.active
    first_row = []
    for col in range(1,sheet.max_column+1):
        first_row.append(sheet.cell(row=1,column=col).value)
    data = []
    for r in range(2,sheet.max_row+1):
        record = {}
        for col in range(sheet.max_column):
            if isinstance(sheet.cell(row=r,column=col+1).value, str):
                record[first_row[col]] = sheet.cell(row=r,column=col+1).value.strip()
            else:
                record[first_row[col]] = sheet.cell(row=r,column=col+1).value
        data.append(record)
    return data

xlrd_results = UseXlrd('foo.xls')
openpyxl_resuts = UseOpenpyxl('foo.xls')

3500行を含む同じExcelファイルを渡すと、実行時間が大幅に異なります。 xlrdを使用すると、2秒以内にファイル全体を辞書のリストに読み込むことができます。 openpyxlを使用すると、次の結果が得られます。

Reading Excel File...
Read 100 lines in 114.14509415626526 seconds
Read 200 lines in 471.43183994293213 seconds
Read 300 lines in 982.5288782119751 seconds
Read 400 lines in 1729.3348784446716 seconds
Read 500 lines in 2774.886833190918 seconds
Read 600 lines in 4384.074863195419 seconds
Read 700 lines in 6396.7723388671875 seconds
Read 800 lines in 7998.775000572205 seconds
Read 900 lines in 11018.460735321045 seconds

最終スクリプトでxlrdを使用できますが、さまざまな問題(つまり、intがfloatとして読み込まれる、dateがintとして読み込まれる、datetimeがfloatとして読み込まれる)のために、多くのフォーマットをハードコーディングする必要があります。さらにいくつかのインポートでこのコードを再利用する必要があるため、特定の列を適切にフォーマットするためにハードコーディングを試みても、4つの異なるスクリプト間で同様のコードを維持する必要はありません。

続行方法に関するアドバイスはありますか?

15
Ron Johnson

あなたはちょうど iterate シート上で:

_def UseOpenpyxl(file_name):
    wb = openpyxl.load_workbook(file_name, read_only=True)
    sheet = wb.active
    rows = sheet.rows
    first_row = [cell.value for cell in next(rows)]
    data = []
    for row in rows:
        record = {}
        for key, cell in Zip(first_row, row):
            if cell.data_type == 's':
                record[key] = cell.value.strip()
            else:
                record[key] = cell.value
        data.append(record)
    return data
_

これは大きなファイルに対応する必要があります。リストdataが大きすぎる場合は、結果をチャンクしたい場合があります。

現在、openpyxlバージョンは、xlrdバージョンの約2倍の時間がかかります。

_%timeit xlrd_results = UseXlrd('foo.xlsx')
1 loops, best of 3: 3.38 s per loop

%timeit openpyxl_results = UseOpenpyxl('foo.xlsx')
1 loops, best of 3: 6.87 s per loop
_

Xlrdとopenpyxlは、整数と浮動小数点の解釈が若干異なる場合があることに注意してください。テストデータについては、出力を比較可能にするためにfloat()を追加する必要がありました。

_def UseOpenpyxl(file_name):
    wb = openpyxl.load_workbook(file_name, read_only=True)
    sheet = wb.active
    rows = sheet.rows
    first_row = [float(cell.value) for cell in next(rows)]
    data = []
    for row in rows:
        record = {}
        for key, cell in Zip(first_row, row):
            if cell.data_type == 's':
                record[key] = cell.value.strip()
            else:
                record[key] = float(cell.value)
        data.append(record)
    return data
_

これで、両方のバージョンでテストデータの結果が同じになりました。

_>>> xlrd_results == openpyxl_results
True
_
14
Mike Müller

Pandas モジュールの完璧な候補のように思えます:

import pandas as pd
import sqlalchemy
import pyodbc

# pyodbc
#
# assuming the following:
# username: scott
# password: tiger
# DSN: mydsn
engine = create_engine('mssql+pyodbc://scott:tiger@mydsn')

# pymssql
#
#engine = create_engine('mssql+pymssql://scott:tiger@hostname:port/dbname')


df = pd.read_Excel('foo.xls')

# write the DataFrame to a table in the sql database
df.to_sql("table_name", engine)

DataFrame.to_sql() 関数の説明

PSそれはかなり速く、非常に使いやすいはずです

1
MaxU