Python
C#
Excel
pandas
ExcelDataReader

ExcelのデータをPython、C#で読み込むまとめ

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 文を書く必要はありませんが、ライブラリーをインストールしておく必要はあります。

example.py
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ファイルを操作するためのライブラリーがいくつかありますが、その中で最もよく使われるライブラリーの一つです。

example.py
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形式まで広く対応しています。

example.cs
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から始まります。

example.cs
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からになります。

example.cs
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で行ごとに処理をしています。

example.cs
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が公開されたばかりなのでバイナリーの方がまだ十分に対応していないためというのもあると思います。