IT業務効率化

Python to excelを高速化するPyExcelerateを使ってみた

PyExcelerate 使ってみた

はじめに

普段Pythonで大量データをエクセルに出力するシステムを扱っています。

しかしデータが大きいと中々時間が掛かってしまうことに悩んでいます。

そんな時、PyExcelerateというライブラリを見つけました!

https://github.com/kz26/PyExcelerate

まだバージョン0.8.0です。どきどきします。openpyxl・xlwtとそれぞれ比較してみようと思います。やってみましょう!

結論

このコードで出力できる

sample_csv_df = pd.read_csv("50000_Sales_Records.csv")

wb = Workbook()
wb.new_sheet("a test sheet", data=sample_csv_df.values.tolist())
wb.save("pyexcelerate_output.xlsx")

評価

他のpythonでexcelを操作するライブラリに比べかなり速いです。

また可読性・使いやすさも問題なしです。

  • openpyxlの2倍の速度で書き込みが可能
  • 1行のコードでpandasと組み合わせて使える
  • 列幅を自動調整する機能を持っている(詳細
  • cellを範囲指定して色・書式設定することができる(詳細
  • 列指定、行指定で色・書式設定することができる(詳細

また前回の記事のopenpyxlとpandasでエクセルを見やすく出力するコードと比較すると、4倍の速度で処理が完了しました。

git

https://github.com/hirayuki32/excel_python/

それではここまでにどういうことを試したか詳細に、書いてきます。

※本サイトのテーマJINの不具合により、一部コードの中括弧の後にスペースがあります。

環境

スペック

  • macOS Mojava10.14.5
  • mac book air(13-inch, 2017)
  • CPU 1.8 GHz Intel Core i5
  • メモリ 8 GB 1600 MHz DDR3
  • python3.6
  • jupyter notebook 5.5.0

利用するデータ

今回利用するデータはこちらのページで提供してくれているSampleデータを利用します。50,000行のデータセットを選択しました。

他のpythonライブラリのベンチマーク結果

時間の計測はjupyter notebookにて%%timeで計測します。比較対象は前回の記事から引っ張ってきました。それぞれ同じデータに対してcsvから読み込み、excelに出力するまでにこのくらいの時間を必要とします。

DataFrame.to_excel 15.2s
openpyxl ws.cell 16.4s
openpyxl ws.append 15.4s
xlwt
※xlsファイルしか作れない
7.62s(ファイル容量が大きくなってしまう)

PyExcelerateでexcelを出力してみる

まずは実行して時間を見てみる

まずはimportします。

from pyexcelerate import Workbook

次に下記のコードを実行してみます。

%%timeit
with open("50000_Sales_Records.csv", "r") as file:
    data = csv.reader(file)

    wb = Workbook()
    wb.new_sheet("a test sheet", data=data)
    wb.save("pyexcelerate_output.xlsx")

さあ!気になる実行時間は!?

8.11 s ± 111 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

8秒!速っ!!

DataFrame.to_excel 15.2s
openpyxl ws.cell 16.4s
openpyxl ws.append 15.4s
xlwt
※xlsファイルしか作れない
7.62s(ファイル容量大きくなってしまう)
pyexcelerate 8.11 s

ダントツですね!openpyxlの2倍弱の速さがあります。すごいものを見つけてしまった・・・

ファイルは以下の画像のように出力されていました。

PyExcelerate使ってみた

openpyxlやxlwtと異なり、右端で折り返してくれる仕様です。

DataFrame入れられるのか?

下記のコードを実行してみます。

%%time
sample_csv_df = pd.read_csv("50000_Sales_Records.csv")

wb = Workbook()
wb.new_sheet("a test sheet", data=sample_csv_df)
wb.save("pyexcelerate_output.xlsx")

実行に掛かった時間は176 ms。速すぎっ!?っと思っていたら失敗していました。エラーは起きませんでしたが、エクセルファイルが正しく出力されていなかったです。

2次元配列に戻す必要がありそうです(そりゃそうか)。df.values.tolist()でDataFrameを2次元配列のリストに戻します。

%%time
sample_csv_df = pd.read_csv("50000_Sales_Records.csv")

wb = Workbook()
wb.new_sheet("a test sheet", data=sample_csv_df.values.tolist())
wb.save("pyexcelerate_output.xlsx")

さあ気になる実行時間は!?

8.44 s ± 236 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

速いっ!!pandas標準のdf.to_excel()の2倍弱の速さで出力できます。やっぱり表データの加工にはpandasは最強なので、連携できるのは嬉しいですね。

pandasのto_excelにengineという引数があり、そこでopenpyxlかxlwtを選べるのですが、そこに追加されて欲しいです。

幅や色を整えて綺麗に出力する

この内容はこちらの記事に分けてまとめました。

PyExcelerate
Pythonでエクセルファイルを綺麗に整形する【PyExcelerate】行に色をつけたり、セルのサイズを調整したり エクセルファイルにデータをこぴぺして、幅を調整して色をつけて提出する。 多部...

速度比較してみる

比較対象は前回の記事のpandasとopenpyxlで書いたコードです。

%%timeit
output_file_name = "pandas_output.xlsx"
sample_csv_df = pd.read_csv("50000_Sales_Records.csv")
sample_csv_df.to_excel(output_file_name, index=False, startrow=1, startcol=1)

# 色付け、列の幅を整えるためにopenpyxlで読み直す
book = openpyxl.load_workbook(output_file_name)
ws = book.worksheets[0]

fill_pattern = openpyxl.styles.PatternFill(patternType='solid', fgColor='2ecc71')
header = ws[2]  # 2行目を左から順に見ていき、データが最後に存在するカラムまで取得する

min_length = 3
for header_cell in header:
    header_cell.fill = fill_pattern
    
    column = header_cell.column
    column_cells = ws[column]
    length = max(len(str(cell.value)) for cell in column_cells)
    if min_length > length:
        length = min_length
    ws.column_dimensions[column].width = length

book.save(output_file_name)

実行時間は

39.6 s ± 1.6 s per loop (mean ± std. dev. of 7 runs, 1 loop each)

でした

そしてPyExcelerate

%%timeit
sample_csv_df = pd.read_csv("50000_Sales_Records.csv")
rownum = len(sample_csv_df.index)
colnum = len(sample_csv_df.columns)

first_cell = "B2"
end_cell = get_column_letter(colnum+1) + str(rownum+1)
header_range = (first_cell, get_column_letter(colnum+1) + str(2))


wb = Workbook()
ws = wb.new_sheet("a test sheet")
ws.range(first_cell, end_cell).value = sample_csv_df.values.tolist()
ws.range(header_range[0], header_range[1]).style.fill.background = Color(46, 204, 113, 0)
for i in range(1, colnum+1):
    ws.set_col_style(i, Style(size=-1))
wb.save("pyexcelerate_output.xlsx")

実行時間は

11.3 s ± 722 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

圧倒的ですね!!1/4の速度です!

そしてコードの行数は少し減っているとともに、ws.set_col_style(i, Style(size=-1))で列幅を自動調整してくれているので、その分すっきりと読みやすいです。(私の実力のせいでむしろ読みにくいかもしれません。直しますのでぜひご指摘ください。)

おわりに

openpyxlにしかない機能がいくつかありましたが、PyExcelerateは速度・使いやすさ共に勝っていると感じました。

これがpandasの標準エンジンになることを祈ります。

お読みいただきありがとうございました。
pandasのDataFrameを複数シートに
pandasのデータをエクセルのシート毎に分けて入れるDataFrame.to_excelだけでは対応できない? pandasのDataFrameをシート毎に分けてエクセルに保存していきた...
python 自動化の課題
Pythonで業務自動化においてコードよりも大切なこと退屈なことをPythonにやらせてみて気づいたコードより大切なこと こちらの有名なPythonの本があるように、たくさんことが...
ABOUT ME
hirayuki
今年で社会人3年目になります。 日々体当たりで仕事を覚えています。 テーマはIT・教育です。 少しでも技術に親しんでもらえるよう、noteで4コマ漫画も書いています。 https://note.mu/hirayuki