Webアプリ用のデータをExcelを使って作っている関係で、Python、C#でExcelファイルを読み込むためのライブラリーを比較してみました。(.NET Core 2.1、Python3.7 が公開されたので記載内容を修正 2018年7月3日)
Excelファイルを読み込むための方法として、Microsoft.Office.Interop.Excelを使ったCOM参照による方法を紹介している記事も多いですが、それを使うのは処理が遅いし、問題も多いし、そもそも Excel が必要なので Linux サーバーだと動作しません。以下に紹介する OSS のライブラリーはかなり改善されていて、少し注意すれば問題なくかつ高速に処理することができます。
パフォーマンス比較
Excelのファイルを読み込みTSV形式で出力するコンソールアプリケーションを作成し、それの起動から終了までの時間を測定しました。Excelのファイルは、589行☓235列の表でxlsx形式で829KBのものを使いました。
テストに使ったソースコード及びデータは、GitHubの方に公開してあります。まだ、整理はできていませんが興味があればみてください。
テスト結果
Linux(Ubuntu 18.04)
ライブラリ | バージョン | xlsx 読込処理時間 | xls 読込処理時間 |
---|---|---|---|
Pandas (xlrd) | 0.23.1 (1.1.0) | 898 | 572 |
openpyxl | 2.5.4 | 1314 | - |
ExcelDataReader | 3.4.0 | 704 | 341 |
DotNetCore.NPOI | 1.0.2 | 1016 | 384 |
EPPlus | 4.5.2.1 | 1954 | - |
ClosedXML | 0.93.0 | 2336 | - |
処理時間は、ExcelDataReader
が最も速くなりました。一方、Pandasを使うと、ExcelDataReader を使うより読み込み時間は1.3〜1.6倍ぐらいかかりますが、プログラムは1行で書けるし、読み込んだ後のデータの加工のための機能が多くNumpyを使って高速な処理ができるので便利です。自分はPandasを使うことに決めました。
各ライブラリーの特徴
以下では、各ライブラリーの特徴と Excel の xlsx ファイルを読み込んで、tsv 形式で書き出すコードを載せています。
Python
Pandas (xlrd)
xlrd は、単独で Excelファイルを読み込むことができますが、pandas.read_excel を使って Pandas のデータフレームに変換した方が処理が楽です。以下のように簡単な記述で tsv 形式に変換できます。pandas.read_excel を使用する場合 xlrd の import 文を書く必要はありませんが、ライブラリーをインストールしておく必要はあります。
import pandas as pd
df = pd.read_excel(in_path, header=None)
df.to_csv(out_path, header=False, sep='\t', index=False)
データフレームには多くのAPIがあり、csv形式以外に、json, HTMLテーブル,SQLデータベース等に簡単に出力できます。
データフレームは、df.iat[0, 0]
のようにしてCellの番号でCellの値を取得したり設定したりすることができるので、VBAやC#でよくするようにfor文で処理をすることもできますが、行列での演算が整備されているので、そちらの方を使うことが多くなります。
上の式では、任意の表を扱えるように header=None にしていますが、データベース形式のようにヘッダーがきちんとある場合は、例えば3行目にある場合はheader=2
と指定するとヘッダーの名前で列を指定できるので便利です。
また、上の式のようにシート名を指定していない場合は、一番最初のシートが読み込まれます。シートを指定したい場合は、sheet_name='シート名'
又は2番目のシートであればsheet_name=1
のように番号で指定することができます。詳しくは、pandas.read_excelのマニュアルをみてください。
openpyxl
openpyxlは、Excel 2010 の xlsx/xlsm/xltx/xltmfor ファイルを読み書きできる Python のライブラリーです。PythonにはExcelファイルを操作するためのライブラリーがいくつかありますが、その中で最もよく使われるライブラリーの一つです。
from openpyxl import load_workbook
import csv
def iter_row(row):
yield [cell.value for cell in row]
wb = load_workbook(in_path, read_only=True)
sheet = wb.worksheets[0]
with open(out_path, 'w') as f:
writer = csv.writer(f, delimiter='\t', quoting=csv.QUOTE_MINIMAL)
for row in sheet.rows:
writer.writerows(iter_row(row))
Excelのセルには、上の場合であればsheet['A1']
又はsheet.cell(row=1, column=1)
のようにしてアクセスできます。
C#
ExcelDataReader
読み込み専用のソフトで、高速に処理できるのが特徴で、海外では以前から人気のあるライブラリーです。かっては日本語の処理に問題があったのですが、現時点では解消されたので、日本語でも安心して使えます。Excel 2.0 から Excel 2007以降のOpen XML形式まで広く対応しています。
using (var stream = File.Open(inFile, FileMode.Open, FileAccess.Read))
using (var excelReader = ExcelReaderFactory.CreateReader(stream))
{
var result = excelReader.AsDataSet();
var sb = new StringBuilder();
foreach (DataRow datarow in result.Tables[0].Rows)
{
sb.Append(datarow.ItemArray.Aggregate((s, x) => s + "\t" + x.ToString()));
sb.Append("\n");
}
File.WriteAllText(outFile, sb.ToString());
}
EPPlus
Office Open XML 形式(xlsx)の読み込み及び書き込みに対応しています。チャートや VBA を操作できる API もあり、Excel に関して言えば NPOI よりも機能は豊富です。.NET Core で使う場合は、Ver4.5からの対応なので現在は RC のものを使う必要があります。また、.NETStandard 2.0 の対応なので、.NET Core では 2.0 からの対応になります。
EPPlusでは、Cellsの番号は1から始まります。
FileInfo newFile = new FileInfo(infile);
ExcelPackage pck = new ExcelPackage(newFile);
//Ver4.5では、Worksheetsは 0 から始まるように変更
ExcelWorksheet sheet = pck.Workbook.Worksheets[1];
var sb = new StringBuilder();
int rows = sheet.Dimension.Rows;
for (int r = 1; r <= rows; r++)
{
for (int c = 1; c <= sheet.Dimension.Columns; c++)
sb.Append(sheet.Cells[r, c].Text + "\t");
sb.Remove(sb.Length - 1, 1);
sb.Append("\n");
}
File.WriteAllText(outFile, sb.ToString());
NPOI
Java の Apacche POI を .NET に移植したものです。2007 の xlsx 及び 2003 の xls ファイルの双方に対応しており、読み込みも書き出しもできます。Excel だけでなく docx にも対応しています。Tony Qu's NPOIは.NET Core には対応していませんが、それから派生した DotNetCore.NPOIが、.NET Standard 2.0 に対応しているので、.NET Core 2.0 でも動作します。DotNetCore.NPOI は ZKWeb.System.Drawing を使っているので Linux で動作させる場合には、libgdiplus が必要になります。
NPOIは、Cellsの番号は0からになります。
FileStream stream = File.OpenRead(inFile);
var book = new XSSFWorkbook(stream);
stream.Close();
ISheet sheet = book.GetSheetAt(0);
int lastRowNum = sheet.LastRowNum;
var sb = new StringBuilder();
for (int r = 0; r < sheet.LastRowNum; r++)
{
var datarow = sheet.GetRow(r);
{
foreach (ICell cell in datarow.Cells)
{
if(cell.CellType == CellType.Numeric)
sb.Append(cell.NumericCellValue + "\t");
else
sb.Append(cell.StringCellValue + "\t");
}
sb.Remove(sb.Length - 1, 1);
sb.Append("\n");
}
}
File.WriteAllText(outFile, sb.ToString());
ClosedXML
Microsoft 純正の Open XML SDK をそのままで使うのはあまりにも面倒なので、それを使いやすくしたライブラリーです。ベースとしてOpen XML SDK を使うので、対応する Excel のファイル形式は、Excel2007以降のOpen XML形式のものになります。Open XML SDK がベースなので機能は豊富ですが、処理は少し重いです。
sheet.Cells()を使って処理をすると処理がものすごく遅くなるので、sheet.Rows()を使ってforeachで行ごとに処理をしています。
var workbook = new ClosedXML.Excel.XLWorkbook(inFile);
ClosedXML.Excel.IXLWorksheet sheet = workbook.Worksheets.Worksheet(1);
var sb = new StringBuilder();
var rows = sheet.LastRowUsed().RangeAddress.FirstAddress.RowNumber;
var columns = sheet.LastColumnUsed().RangeAddress.FirstAddress.ColumnNumber;
foreach(var row in sheet.Rows())
{
foreach (var cell in row.Cells())
sb.Append(cell.GetString() + "\t");
sb.Remove(sb.Length - 1, 1);
sb.Append("\n");
}
File.WriteAllText(outFile, sb.ToString());
参考 Windows(Windows10)でのテスト結果
Windows(Windows10)でのテスト結果は以下のとおりでした。Windows と Linux は、同一のPCでデュアルブートで立ち上げたものを使っています。処理時間の単位はミリ秒です。
ライブラリ | バージョン | xlsx 読込処理時間 | xls 読込処理時間 |
---|---|---|---|
Pandas (xlrd) | 0.23.1 (1.1.0) | 1148 | 755 |
openpyxl | 2.5.4 | 1848 | - |
ExcelDataReader(.NET Framework) | 3.4.0 | 581 | 344 |
ExcelDataReader(.NET Core) | 3.4.0 | 610 | 353 |
NPOI(.NET Framework) | 2.3.0 | 1004 | 372 |
DotNetCore.NPOI(.NET Core) | 1.0.2 | 1062 | 388 |
EPPlus(.NET Framework) | 4.5.2.1 | 1310 | - |
EPPlus(.NET Core) | 4.5.2.1 | 1246 | - |
ClosedXML(.NET Framework) | 0.93.0 | 2202 | - |
ClosedXML(.NET Core) | 0.93.0 | 2069 | - |
.NET Core 2.1 になって、Linux と Windows の処理速度にやっと差がなくなりました。また、.NET Core と .NET Framework との処理時間もほぼ同じなりました。やっと、Linux で C# が使えるようになったといいでしょう。しかしながら、.NET Framework 4.5が公開されたのが2012年8月なので6年かかったということになります。競争相手のJavaにはKotlinという新しい言語が出てきたし、その間にJavaScriptは大きく進歩してしまいました。遅すぎたというのが現実だと思います。
Pythonの方は、Windowsで使うと3割ぐらいも遅くなるという結果になりました。PythonをWindowsで使うと遅くなるのは以前からでしたが、Python3.7が公開されたばかりなのでバイナリーの方がまだ十分に対応していないためというのもあると思います。